{"id":27923324,"url":"https://github.com/bowenz/rc-danmaku","last_synced_at":"2025-05-06T22:32:28.999Z","repository":{"id":65482606,"uuid":"315862793","full_name":"BowenZ/rc-danmaku","owner":"BowenZ","description":"React弹幕组件 / React danmaku(or bullet screen) component","archived":false,"fork":false,"pushed_at":"2024-08-22T08:33:49.000Z","size":1219,"stargazers_count":44,"open_issues_count":2,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-24T12:49:25.556Z","etag":null,"topics":["barrage","bullet-screen","danmaku","react","react-hooks","typescript"],"latest_commit_sha":null,"homepage":"https://bowenz.github.io/pages/rc-danmaku/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/BowenZ.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-11-25T07:35:33.000Z","updated_at":"2025-03-04T06:00:29.000Z","dependencies_parsed_at":"2024-06-19T22:48:48.777Z","dependency_job_id":"b60407af-8aaa-411e-9163-656811962578","html_url":"https://github.com/BowenZ/rc-danmaku","commit_stats":{"total_commits":13,"total_committers":1,"mean_commits":13.0,"dds":0.0,"last_synced_commit":"51ddacd18381893b09a3552a031ad8e97e8904ad"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenZ%2Frc-danmaku","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenZ%2Frc-danmaku/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenZ%2Frc-danmaku/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/BowenZ%2Frc-danmaku/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/BowenZ","download_url":"https://codeload.github.com/BowenZ/rc-danmaku/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252779563,"owners_count":21802972,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["barrage","bullet-screen","danmaku","react","react-hooks","typescript"],"created_at":"2025-05-06T22:31:50.275Z","updated_at":"2025-05-06T22:32:28.975Z","avatar_url":"https://github.com/BowenZ.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# rc-danmaku\n\n[![npm-version](https://badgen.net/npm/v/rc-danmaku)](https://www.npmjs.com/package/rc-danmaku)\n[![github-stars](https://badgen.net/github/stars/BowenZ/rc-danmaku)](https://github.com/BowenZ/rc-danmaku)\n[![bundlephobia](https://badgen.net/bundlephobia/min/rc-danmaku)](https://bundlephobia.com/result?p=rc-danmaku@latest)\n![license](https://badgen.net/npm/license/rc-danmaku)\n\n[English README](./README.en.md)\n\n[更新日志](./CHANGELOG.md)\n\n🚀 React 弹幕组件 - 支持 React 17/18 和 TypeScript\n\n\u003e 🌟 已更新支持React18，版本号为rc-danmaku@1.2.0-alpha.2，若此版本没有bug，会更新到正式版本号；\n\u003e 🐞 若发现问题欢迎提 issue，有功能需求欢迎提 pr 或者在 issue 中反馈。\n\n## 🌰 示例页面\n\n🔗 [https://bowenz.github.io/pages/rc-danmaku/](https://bowenz.github.io/pages/rc-danmaku/)\n\n## 📦 安装\n\n`npm install rc-danmaku -S`\n\n## 🚗 快速开始\n\n复制下方代码可以直接使用（代码 TypeScript 版本）\n\n```tsx\nimport React, { useEffect, useRef } from 'react';\nimport Danmaku from 'rc-danmaku';\n\nconst TestDanmaku: React.FC = () =\u003e {\n  const danmakuInsRef = useRef\u003cDanmaku | null\u003e(null);\n\n  useEffect(() =\u003e {\n    const danmakuIns = new Danmaku('.danmaku-wrapper');\n    danmakuInsRef.current = danmakuIns;\n  }, []);\n\n  return (\n    \u003cdiv className=\"test-danmaku\"\u003e\n      \u003cdiv\n        className=\"danmaku-wrapper\"\n        style={{\n          width: '300px',\n          height: '200px',\n          backgroundColor: '#000',\n        }}\n      /\u003e\n      \u003cbutton\n        type=\"button\"\n        onClick={(): void =\u003e {\n          if (danmakuInsRef.current) {\n            danmakuInsRef.current.push('Hello World!');\n          }\n        }}\n      \u003e\n        发送弹幕\n      \u003c/button\u003e\n    \u003c/div\u003e\n  );\n};\n\nexport default TestDanmaku;\n```\n\n## 📖 文档\n\n### 创建实例\n\n```tsx\n// 第一个参数是弹幕容器，可以传string类型的选择器，或者直接传dom元素\n// 第二个object类型的参数是可选参数，包含弹幕配置，里边的所有项均不是必填项\nconst danmakuIns = new Danmaku('.danmaku-wrapper', {\n  rowHeight: 60, // 弹幕轨道高度，默认40（单位px）\n  speed: 120, // 弹幕速度，默认100（单位px/s）\n  opacity: 1, // 弹幕透明度，默认为1，范围 0-1\n  maxRow: 0, // 弹幕最大轨道数，会根据容器高度自动计算，也可以手动赋值（此处设为0表示使用自动计算高度）\n  minGapWidth: 30, //弹幕之前的最小间隔宽度，默认值20（单位px）\n  // 每个弹幕进入时触发\n  onBulletIn() {\n    console.log('====bullet in====');\n  },\n  // 每个弹幕消失时触发\n  onBulletOut() {\n    console.log('====bullet out====');\n  },\n  // 队列中的弹幕发送完时触发（每次发送弹幕都会检查，不管用何种方式发送，手动清空队列不会触发该事件）\n  onQueueRunOut() {\n    console.log('====queue run out====');\n  },\n});\n```\n\n### 实例方法\n\n初始化后，可调用实例方法发送弹幕\n\n```tsx\nconst danmakuIns = new Danmaku('.danmaku-wrapper');\n\n// 发送文本\ndanmakuIns.push('test string');\n\n// 发送指定颜色的文本\ndanmakuIns.push('test string', {\n  color: 'red',\n});\n\n// 发送React组件\ndanmakuIns.push(\u003cTestReactComponent /\u003e);\n\n// 直接发送文本\n// emit与push的区别是，push会在屏幕中没有空闲位置时暂不发送，等有空位时再发送，\n// emit会不管有没有空位直接发送，传参规则和push完全一样\ndanmakuIns.emit('test string');\n\n// 批量发送\ndanmakuIns.pushAll(['test1', 'test2', 'test3']);\n\n// 暂停\ndanmakuIns.pause();\n\n// 继续\ndanmakuIns.resume();\n\n// 销毁\ndanmakuIns.destroy();\n\n// 查看弹幕队列中剩余的数量\ndanmakuIns.getRestAmount();\n\n// 清空排队中的弹幕队列（已发送的不会被清，不会触发onQueueRunOut事件）\ndanmakuIns.clearQueue();\n```\n\n### 完整使用代码示例\n\n\u003cdetails\u003e\n\u003csummary\u003e展开查看\u003c/summary\u003e\n\n```tsx\nimport React, { useEffect, useRef, useState } from 'react';\nimport styled from 'styled-components';\nimport Danmaku from 'rc-danmaku';\n\nconst textArr = Array.from(\n  '通过对平面中竖直和水平方向的分析我们将宽泛的弹幕重叠问题收敛为轨道中相邻弹幕两两之间的追及问题最终获得了将候选弹幕挂载到合适轨道中的调度策略'\n);\n\nfunction getRandomIndex(length: number): number {\n  return Math.floor(Math.random() * length);\n}\n\nfunction getRandomText(): string {\n  const length = Math.floor(Math.random() * 19) + 1;\n  return Array(length)\n    .fill(null)\n    .map(() =\u003e {\n      return textArr[getRandomIndex(textArr.length)];\n    })\n    .join('');\n}\n\nconst TestDanmaku: React.FC = () =\u003e {\n  const danmakuInsRef = useRef\u003cDanmaku | null\u003e(null);\n  const [showColor, setShowColor] = useState(false);\n  const colorRef = useRef('');\n  const [isPaused, setIsPaused] = useState(false);\n\n  useEffect(() =\u003e {\n    // 第一个参数是弹幕容器，可以传string类型的选择器，或者直接传dom元素\n    // 第二个object类型的参数是可选参数，包含弹幕配置，里边的所有项均不是必填项\n    const danmakuIns = new Danmaku('.danmaku-wrapper', {\n      rowHeight: 60, // 弹幕轨道高度，默认40（单位px）\n      speed: 120, // 弹幕速度，默认100（单位px/s）\n      opacity: 1, // 弹幕透明度，默认为1，范围 0-1\n      maxRow: 5, // 弹幕最大轨道数，会根据容器高度自动计算，也可以手动赋值\n      minGapWidth: 30, //弹幕之前的最小间隔宽度，默认值20（单位px）\n    });\n    danmakuInsRef.current = danmakuIns;\n  }, []);\n\n  useEffect(() =\u003e {\n    if (showColor) {\n      colorRef.current = (document.querySelector(\n        '.color-piker'\n      ) as HTMLInputElement).value;\n    }\n  }, [showColor]);\n\n  useEffect(() =\u003e {\n    if (danmakuInsRef.current) {\n      if (isPaused) {\n        danmakuInsRef.current.pause();\n      } else {\n        danmakuInsRef.current.resume();\n      }\n    }\n  }, [isPaused]);\n  return (\n    \u003cWrapper className=\"test-danmaku\"\u003e\n      \u003cdiv className=\"danmaku-wrapper\" /\u003e\n      \u003cdiv\u003e\n        \u003cspan\u003e透明度\u003c/span\u003e\n        \u003cinput\n          type=\"range\"\n          min=\"0\"\n          max=\"1\"\n          step=\"0.1\"\n          onChange={(e): void =\u003e {\n            if (danmakuInsRef.current) {\n              const { value } = e.target;\n              danmakuInsRef.current.opacity = Number(value);\n            }\n          }}\n        /\u003e\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        \u003cspan\u003e彩色弹幕：\u003c/span\u003e\n        \u003cinput\n          type=\"checkbox\"\n          onChange={(e): void =\u003e {\n            const { checked } = e.target;\n            setShowColor(checked);\n          }}\n        /\u003e\n        \u0026nbsp;\u0026nbsp;\u0026nbsp;\n        \u003cinput\n          className=\"color-piker\"\n          type=\"color\"\n          defaultValue=\"#ff0000\"\n          style={{\n            visibility: showColor ? 'visible' : 'hidden',\n          }}\n          onChange={(e): void =\u003e {\n            const { value } = e.target;\n            colorRef.current = value;\n          }}\n        /\u003e\n      \u003c/div\u003e\n      \u003cbutton\n        type=\"button\"\n        onClick={(): void =\u003e {\n          if (danmakuInsRef.current) {\n            danmakuInsRef.current.emit(getRandomText(), {\n              color: showColor ? colorRef.current : undefined,\n            });\n          }\n        }}\n      \u003e\n        发送随机文本（过多会重叠）\n      \u003c/button\u003e\n      \u003cdiv\u003e\n        \u003cspan\u003e输入文本：\u003c/span\u003e\n        \u003cinput type=\"text\" className=\"danmaku-text-input\" /\u003e\n        \u003cbutton\n          type=\"button\"\n          onClick={(): void =\u003e {\n            if (danmakuInsRef.current) {\n              const $input = document.querySelector(\n                '.danmaku-text-input'\n              ) as HTMLInputElement;\n              if ($input.value \u0026\u0026 $input.value.trim()) {\n                danmakuInsRef.current.emit($input.value, {\n                  color: showColor ? colorRef.current : undefined,\n                });\n              }\n\n              $input.value = '';\n              $input.focus();\n            }\n          }}\n        \u003e\n          发送\n        \u003c/button\u003e\n      \u003c/div\u003e\n      \u003cbutton\n        type=\"button\"\n        onClick={(): void =\u003e {\n          if (danmakuInsRef.current) {\n            danmakuInsRef.current.emit(\u003cTestNode\u003ereact node\u003c/TestNode\u003e);\n          }\n        }}\n      \u003e\n        发送react节点\n      \u003c/button\u003e\n      \u003cbutton\n        type=\"button\"\n        onClick={(): void =\u003e {\n          if (danmakuInsRef.current) {\n            danmakuInsRef.current.push(getRandomText(), {\n              color: showColor ? colorRef.current : undefined,\n            });\n          }\n        }}\n      \u003e\n        推送随机文字到发送队列（过多不会重叠，会延迟发送）\n      \u003c/button\u003e\n      \u003cbutton\n        type=\"button\"\n        onClick={(): void =\u003e {\n          if (danmakuInsRef.current) {\n            danmakuInsRef.current.push(\u003cTestNode\u003ereact node\u003c/TestNode\u003e);\n          }\n        }}\n      \u003e\n        推送React节点到发送队列（过多不会重叠，会延迟发送）\n      \u003c/button\u003e\n      \u003cbutton\n        type=\"button\"\n        onClick={(): void =\u003e {\n          if (danmakuInsRef.current) {\n            danmakuInsRef.current.pushAll(\n              Array(20)\n                .fill(null)\n                .map(() =\u003e getRandomText()),\n              {\n                color: showColor ? colorRef.current : undefined,\n              }\n            );\n          }\n        }}\n      \u003e\n        随机推送20条文字弹幕\n      \u003c/button\u003e\n      \u003cbutton\n        type=\"button\"\n        onClick={(): void =\u003e {\n          if (danmakuInsRef.current) {\n            danmakuInsRef.current.pushAll(\n              Array(20)\n                .fill(null)\n                .map(() =\u003e \u003cTestNode\u003ereact node\u003c/TestNode\u003e)\n            );\n          }\n        }}\n      \u003e\n        随机推送20条React节点\n      \u003c/button\u003e\n      \u003cdiv\u003e\n        {isPaused ? (\n          \u003cspan style={{ color: 'red' }}\u003e暂停中\u003c/span\u003e\n        ) : (\n          \u003cspan style={{ color: 'green' }}\u003e运行中\u003c/span\u003e\n        )}\n      \u003c/div\u003e\n      \u003cdiv\u003e\n        \u003cbutton\n          type=\"button\"\n          onClick={(): void =\u003e {\n            setIsPaused((p) =\u003e !p);\n          }}\n        \u003e\n          {isPaused ? '继续' : '暂停'}\n        \u003c/button\u003e\n      \u003c/div\u003e\n      \u003cbutton\n        type=\"button\"\n        onClick={(): void =\u003e {\n          if (danmakuInsRef.current) {\n            danmakuInsRef.current.destroy();\n            alert('组件已经被销毁，任何操作将会无相应，重新刷新页面再测吧');\n          }\n        }}\n      \u003e\n        销毁（销毁后无法再发送弹幕）\n      \u003c/button\u003e\n    \u003c/Wrapper\u003e\n  );\n};\n\nexport default TestDanmaku;\n\nconst Wrapper = styled.div`\n  display: flex;\n  flex-direction: column;\n  justify-content: center;\n  align-items: center;\n  height: 100vh;\n  height: calc(100vh - 45px);\n  input[type='text'] {\n    padding: 0.2em;\n    width: 150px;\n    max-width: 150px;\n  }\n  button {\n    outline: none;\n    appearance: none;\n    padding: 0.2em 1.45em;\n    margin: 0.1em;\n    border: 0.15em solid #cccccc;\n    color: #000000;\n    background-color: #cccccc;\n    \u0026:hover {\n      border-color: #7a7a7a;\n    }\n    \u0026:active {\n      background-color: #999999;\n    }\n  }\n  .danmaku-wrapper {\n    width: 90%;\n    height: 60vw;\n    max-height: 500px;\n    background-color: #000;\n  }\n`;\n\nconst TestNode = styled.div`\n  width: 100px;\n  height: 30px;\n  background: linear-gradient(90deg, pink, red);\n  border-radius: 20px;\n  color: #fff;\n  line-height: 30px;\n  text-align: center;\n`;\n```\n\n\u003c/details\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbowenz%2Frc-danmaku","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbowenz%2Frc-danmaku","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbowenz%2Frc-danmaku/lists"}