index.js 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368
  1. module.exports =
  2. /******/ (function(modules) { // webpackBootstrap
  3. /******/ // The module cache
  4. /******/ var installedModules = {};
  5. /******/
  6. /******/ // The require function
  7. /******/ function __webpack_require__(moduleId) {
  8. /******/
  9. /******/ // Check if module is in cache
  10. /******/ if(installedModules[moduleId]) {
  11. /******/ return installedModules[moduleId].exports;
  12. /******/ }
  13. /******/ // Create a new module (and put it into the cache)
  14. /******/ var module = installedModules[moduleId] = {
  15. /******/ i: moduleId,
  16. /******/ l: false,
  17. /******/ exports: {}
  18. /******/ };
  19. /******/
  20. /******/ // Execute the module function
  21. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  22. /******/
  23. /******/ // Flag the module as loaded
  24. /******/ module.l = true;
  25. /******/
  26. /******/ // Return the exports of the module
  27. /******/ return module.exports;
  28. /******/ }
  29. /******/
  30. /******/
  31. /******/ // expose the modules object (__webpack_modules__)
  32. /******/ __webpack_require__.m = modules;
  33. /******/
  34. /******/ // expose the module cache
  35. /******/ __webpack_require__.c = installedModules;
  36. /******/
  37. /******/ // define getter function for harmony exports
  38. /******/ __webpack_require__.d = function(exports, name, getter) {
  39. /******/ if(!__webpack_require__.o(exports, name)) {
  40. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  41. /******/ }
  42. /******/ };
  43. /******/
  44. /******/ // define __esModule on exports
  45. /******/ __webpack_require__.r = function(exports) {
  46. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  47. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  48. /******/ }
  49. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  50. /******/ };
  51. /******/
  52. /******/ // create a fake namespace object
  53. /******/ // mode & 1: value is a module id, require it
  54. /******/ // mode & 2: merge all properties of value into the ns
  55. /******/ // mode & 4: return value when already ns object
  56. /******/ // mode & 8|1: behave like require
  57. /******/ __webpack_require__.t = function(value, mode) {
  58. /******/ if(mode & 1) value = __webpack_require__(value);
  59. /******/ if(mode & 8) return value;
  60. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  61. /******/ var ns = Object.create(null);
  62. /******/ __webpack_require__.r(ns);
  63. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  64. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  65. /******/ return ns;
  66. /******/ };
  67. /******/
  68. /******/ // getDefaultExport function for compatibility with non-harmony modules
  69. /******/ __webpack_require__.n = function(module) {
  70. /******/ var getter = module && module.__esModule ?
  71. /******/ function getDefault() { return module['default']; } :
  72. /******/ function getModuleExports() { return module; };
  73. /******/ __webpack_require__.d(getter, 'a', getter);
  74. /******/ return getter;
  75. /******/ };
  76. /******/
  77. /******/ // Object.prototype.hasOwnProperty.call
  78. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  79. /******/
  80. /******/ // __webpack_public_path__
  81. /******/ __webpack_require__.p = "";
  82. /******/
  83. /******/
  84. /******/ // Load entry module and return exports
  85. /******/ return __webpack_require__(__webpack_require__.s = 1);
  86. /******/ })
  87. /************************************************************************/
  88. /******/ ([
  89. /* 0 */
  90. /***/ (function(module, exports, __webpack_require__) {
  91. "use strict";
  92. // 获取字节长度,中文算2个字节
  93. function getStrLen(str) {
  94. // eslint-disable-next-line no-control-regex
  95. return str.replace(/[^\x00-\xff]/g, 'aa').length;
  96. }
  97. // 截取指定字节长度的子串
  98. function substring(str, n) {
  99. if (!str) return '';
  100. var len = getStrLen(str);
  101. if (n >= len) return str;
  102. var l = 0;
  103. var result = '';
  104. for (var i = 0; i < str.length; i++) {
  105. var ch = str.charAt(i);
  106. // eslint-disable-next-line no-control-regex
  107. l = /[^\x00-\xff]/i.test(ch) ? l + 2 : l + 1;
  108. result += ch;
  109. if (l >= n) break;
  110. }
  111. return result;
  112. }
  113. function getRandom() {
  114. var max = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 10;
  115. var min = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
  116. return Math.floor(Math.random() * (max - min) + min);
  117. }
  118. function getFontSize(font) {
  119. var reg = /(\d+)(px)/i;
  120. var match = font.match(reg);
  121. return match && match[1] || 10;
  122. }
  123. function compareVersion(v1, v2) {
  124. v1 = v1.split('.');
  125. v2 = v2.split('.');
  126. var len = Math.max(v1.length, v2.length);
  127. while (v1.length < len) {
  128. v1.push('0');
  129. }
  130. while (v2.length < len) {
  131. v2.push('0');
  132. }
  133. for (var i = 0; i < len; i++) {
  134. var num1 = parseInt(v1[i], 10);
  135. var num2 = parseInt(v2[i], 10);
  136. if (num1 > num2) {
  137. return 1;
  138. } else if (num1 < num2) {
  139. return -1;
  140. }
  141. }
  142. return 0;
  143. }
  144. module.exports = {
  145. getStrLen: getStrLen,
  146. substring: substring,
  147. getRandom: getRandom,
  148. getFontSize: getFontSize,
  149. compareVersion: compareVersion
  150. };
  151. /***/ }),
  152. /* 1 */
  153. /***/ (function(module, exports, __webpack_require__) {
  154. "use strict";
  155. var _barrageDom = __webpack_require__(2);
  156. var _barrageDom2 = _interopRequireDefault(_barrageDom);
  157. var _barrageCanvas = __webpack_require__(3);
  158. var _barrageCanvas2 = _interopRequireDefault(_barrageCanvas);
  159. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  160. Component({
  161. options: {
  162. addGlobalClass: true
  163. },
  164. properties: {
  165. zIndex: {
  166. type: Number,
  167. value: 10
  168. },
  169. renderingMode: {
  170. type: String,
  171. value: 'canvas'
  172. }
  173. },
  174. methods: {
  175. getBarrageInstance: function getBarrageInstance() {
  176. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  177. opt.comp = this;
  178. this.barrageInstance = this.data.renderingMode === 'dom' ? new _barrageDom2.default(opt) : new _barrageCanvas2.default(opt);
  179. return this.barrageInstance;
  180. },
  181. onAnimationend: function onAnimationend(e) {
  182. var _e$currentTarget$data = e.currentTarget.dataset,
  183. tunnelid = _e$currentTarget$data.tunnelid,
  184. bulletid = _e$currentTarget$data.bulletid;
  185. this.barrageInstance.animationend({
  186. tunnelId: tunnelid,
  187. bulletId: bulletid
  188. });
  189. },
  190. onTapBullet: function onTapBullet(e) {
  191. var _e$currentTarget$data2 = e.currentTarget.dataset,
  192. tunnelid = _e$currentTarget$data2.tunnelid,
  193. bulletid = _e$currentTarget$data2.bulletid;
  194. this.barrageInstance.tapBullet({
  195. tunnelId: tunnelid,
  196. bulletId: bulletid
  197. });
  198. }
  199. }
  200. });
  201. /***/ }),
  202. /* 2 */
  203. /***/ (function(module, exports, __webpack_require__) {
  204. "use strict";
  205. exports.__esModule = true;
  206. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  207. var _require = __webpack_require__(0),
  208. substring = _require.substring,
  209. getRandom = _require.getRandom,
  210. getFontSize = _require.getFontSize;
  211. var Bullet = function () {
  212. function Bullet() {
  213. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  214. _classCallCheck(this, Bullet);
  215. this.bulletId = opt.bulletId;
  216. this.addContent(opt);
  217. }
  218. /**
  219. * image 结构
  220. * {
  221. * head: {src, width, height},
  222. * tail: {src, width, height},
  223. * gap: 4 // 图片与文本间隔
  224. * }
  225. */
  226. Bullet.prototype.addContent = function addContent() {
  227. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  228. var defaultBulletOpt = {
  229. duration: 0, // 动画时长
  230. passtime: 0, // 弹幕穿越右边界耗时
  231. content: '', // 文本
  232. color: '#000000', // 默认黑色
  233. width: 0, // 弹幕宽度
  234. height: 0, // 弹幕高度
  235. image: {}, // 图片
  236. paused: false // 是否暂停
  237. };
  238. Object.assign(this, defaultBulletOpt, opt);
  239. };
  240. Bullet.prototype.removeContent = function removeContent() {
  241. this.addContent({});
  242. };
  243. return Bullet;
  244. }();
  245. // tunnel(轨道)
  246. var Tunnel = function () {
  247. function Tunnel() {
  248. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  249. _classCallCheck(this, Tunnel);
  250. var defaultTunnelOpt = {
  251. tunnelId: 0,
  252. height: 0, // 轨道高度
  253. width: 0, // 轨道宽度
  254. safeGap: 4, // 相邻弹幕安全间隔
  255. maxNum: 10, // 缓冲队列长度
  256. bullets: [], // 弹幕
  257. last: -1, // 上一条发送的弹幕序号
  258. bulletStatus: [], // 0 空闲,1 占用中
  259. disabled: false, // 禁用中
  260. sending: false, // 弹幕正在发送
  261. timer: null // 定时器
  262. };
  263. Object.assign(this, defaultTunnelOpt, opt);
  264. this.bulletStatus = new Array(this.maxNum).fill(0);
  265. for (var i = 0; i < this.maxNum; i++) {
  266. this.bullets.push(new Bullet({
  267. bulletId: i
  268. }));
  269. }
  270. }
  271. Tunnel.prototype.disable = function disable() {
  272. this.disabled = true;
  273. this.last = -1;
  274. this.sending = false;
  275. this.bulletStatus = new Array(this.maxNum).fill(1);
  276. this.bullets.forEach(function (bullet) {
  277. return bullet.removeContent();
  278. });
  279. };
  280. Tunnel.prototype.enable = function enable() {
  281. if (this.disabled) {
  282. this.bulletStatus = new Array(this.maxNum).fill(0);
  283. }
  284. this.disabled = false;
  285. };
  286. Tunnel.prototype.clear = function clear() {
  287. this.last = -1;
  288. this.sending = false;
  289. this.bulletStatus = new Array(this.maxNum).fill(0);
  290. this.bullets.forEach(function (bullet) {
  291. return bullet.removeContent();
  292. });
  293. if (this.timer) {
  294. clearTimeout(this.timer);
  295. }
  296. };
  297. Tunnel.prototype.getIdleBulletIdx = function getIdleBulletIdx() {
  298. var idle = this.bulletStatus.indexOf(0, this.last + 1);
  299. if (idle === -1) {
  300. idle = this.bulletStatus.indexOf(0);
  301. }
  302. return idle;
  303. };
  304. Tunnel.prototype.getIdleBulletNum = function getIdleBulletNum() {
  305. var count = 0;
  306. this.bulletStatus.forEach(function (status) {
  307. if (status === 0) count++;
  308. });
  309. return count;
  310. };
  311. Tunnel.prototype.addBullet = function addBullet(opt) {
  312. if (this.disabled) return;
  313. var idx = this.getIdleBulletIdx();
  314. if (idx >= 0) {
  315. this.bulletStatus[idx] = 1;
  316. this.bullets[idx].addContent(opt);
  317. }
  318. };
  319. Tunnel.prototype.removeBullet = function removeBullet(bulletId) {
  320. if (this.disabled) return;
  321. this.bulletStatus[bulletId] = 0;
  322. var bullet = this.bullets[bulletId];
  323. bullet.removeContent();
  324. };
  325. return Tunnel;
  326. }();
  327. // Barrage(控制中心)
  328. var Barrage = function () {
  329. function Barrage() {
  330. var _this = this;
  331. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  332. _classCallCheck(this, Barrage);
  333. var defaultBarrageOpt = {
  334. duration: 10, // 弹幕动画时长
  335. lineHeight: 1.2, // 弹幕行高
  336. padding: [0, 0, 0, 0], // 弹幕区四周留白
  337. alpha: 1, // 全局透明度
  338. font: '10px sans-serif', // 全局字体
  339. mode: 'separate', // 弹幕重叠 overlap 不重叠 separate
  340. range: [0, 1], // 弹幕显示的垂直范围,支持两个值。[0,1]表示弹幕整个随机分布,
  341. tunnelShow: false, // 显示轨道线
  342. tunnelMaxNum: 30, // 隧道最大缓冲长度
  343. maxLength: 30, // 弹幕最大字节长度,汉字算双字节
  344. safeGap: 4, // 发送时的安全间隔
  345. enableTap: false, // 点击弹幕停止动画高亮显示
  346. tunnelHeight: 0,
  347. tunnelNum: 0,
  348. tunnels: [],
  349. idleTunnels: null,
  350. enableTunnels: null,
  351. distance: 2000,
  352. comp: null // 组件实例
  353. };
  354. Object.assign(this, defaultBarrageOpt, opt);
  355. this._ready = false;
  356. this._deferred = [];
  357. var query = this.comp.createSelectorQuery();
  358. query.select('.barrage-area').boundingClientRect(function (res) {
  359. _this.init(res);
  360. _this.ready();
  361. }).exec();
  362. }
  363. Barrage.prototype.ready = function ready() {
  364. var _this2 = this;
  365. this._ready = true;
  366. this._deferred.forEach(function (item) {
  367. // eslint-disable-next-line prefer-spread
  368. _this2[item.callback].apply(_this2, item.args);
  369. });
  370. this._deferred = [];
  371. };
  372. Barrage.prototype._delay = function _delay(method, args) {
  373. this._deferred.push({
  374. callback: method,
  375. args: args
  376. });
  377. };
  378. Barrage.prototype.init = function init(opt) {
  379. this.width = opt.width;
  380. this.height = opt.height;
  381. this.fontSize = getFontSize(this.font);
  382. this.idleTunnels = new Set();
  383. this.enableTunnels = new Set();
  384. this.tunnels = [];
  385. this.availableHeight = this.height - this.padding[0] - this.padding[2];
  386. this.tunnelHeight = this.fontSize * this.lineHeight;
  387. this.tunnelNum = Math.floor(this.availableHeight / this.tunnelHeight);
  388. for (var i = 0; i < this.tunnelNum; i++) {
  389. this.idleTunnels.add(i); // 空闲的隧道id集合
  390. this.enableTunnels.add(i); // 可用的隧道id集合
  391. this.tunnels.push(new Tunnel({ // 隧道集合
  392. width: this.width,
  393. height: this.tunnelHeight,
  394. safeGap: this.safeGap,
  395. maxNum: this.tunnelMaxNum,
  396. tunnelId: i
  397. }));
  398. }
  399. this.comp.setData({
  400. fontSize: this.fontSize,
  401. tunnelShow: this.tunnelShow,
  402. tunnels: this.tunnels,
  403. font: this.font,
  404. alpha: this.alpha,
  405. padding: this.padding.map(function (item) {
  406. return item + 'px';
  407. }).join(' ')
  408. });
  409. // 筛选符合范围的隧道
  410. this.setRange();
  411. };
  412. // 设置显示范围 range: [0,1]
  413. Barrage.prototype.setRange = function setRange(range) {
  414. var _this3 = this;
  415. if (!this._ready) {
  416. this._delay('setRange', range);
  417. return;
  418. }
  419. range = range || this.range;
  420. var top = range[0] * this.tunnelNum;
  421. var bottom = range[1] * this.tunnelNum;
  422. // 释放符合要求的隧道
  423. // 找到目前空闲的隧道
  424. var idleTunnels = new Set();
  425. var enableTunnels = new Set();
  426. this.tunnels.forEach(function (tunnel, tunnelId) {
  427. if (tunnelId >= top && tunnelId < bottom) {
  428. var disabled = tunnel.disabled;
  429. tunnel.enable();
  430. enableTunnels.add(tunnelId);
  431. if (disabled || _this3.idleTunnels.has(tunnelId)) {
  432. idleTunnels.add(tunnelId);
  433. }
  434. } else {
  435. tunnel.disable();
  436. }
  437. });
  438. this.idleTunnels = idleTunnels;
  439. this.enableTunnels = enableTunnels;
  440. this.range = range;
  441. this.comp.setData({ tunnels: this.tunnels });
  442. };
  443. Barrage.prototype.setFont = function setFont(font) {
  444. if (!this._ready) {
  445. this._delay('setFont', font);
  446. return;
  447. }
  448. if (typeof font !== 'string') return;
  449. this.font = font;
  450. this.comp.setData({ font: font });
  451. };
  452. Barrage.prototype.setAlpha = function setAlpha(alpha) {
  453. if (!this._ready) {
  454. this._delay('setAlpha', alpha);
  455. return;
  456. }
  457. if (typeof alpha !== 'number') return;
  458. this.alpha = alpha;
  459. this.comp.setData({ alpha: alpha });
  460. };
  461. Barrage.prototype.setDuration = function setDuration(duration) {
  462. if (!this._ready) {
  463. this._delay('setDuration', duration);
  464. return;
  465. }
  466. if (typeof duration !== 'number') return;
  467. this.duration = duration;
  468. this.clear();
  469. };
  470. // 开启弹幕
  471. Barrage.prototype.open = function open() {
  472. if (!this._ready) {
  473. this._delay('open');
  474. return;
  475. }
  476. this._isActive = true;
  477. };
  478. // 关闭弹幕,清除所有数据
  479. Barrage.prototype.close = function close() {
  480. if (!this._ready) {
  481. this._delay('close');
  482. return;
  483. }
  484. this._isActive = false;
  485. this.clear();
  486. };
  487. Barrage.prototype.clear = function clear() {
  488. this.tunnels.forEach(function (tunnel) {
  489. return tunnel.clear();
  490. });
  491. this.idleTunnels = new Set(this.enableTunnels);
  492. this.comp.setData({ tunnels: this.tunnels });
  493. };
  494. // 添加一批弹幕,轨道满时会被丢弃
  495. Barrage.prototype.addData = function addData() {
  496. var _this4 = this;
  497. var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  498. if (!this._ready) {
  499. this._delay('addData', data);
  500. return;
  501. }
  502. if (!this._isActive) return;
  503. data.forEach(function (item) {
  504. item.content = substring(item.content, _this4.maxLength);
  505. _this4.addBullet2Tunnel(item);
  506. });
  507. this.comp.setData({
  508. tunnels: this.tunnels
  509. }, function () {
  510. _this4.updateBullets();
  511. });
  512. };
  513. // 发送一条弹幕
  514. Barrage.prototype.send = function send() {
  515. var _this5 = this;
  516. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  517. if (!this._ready) {
  518. this._delay('send', opt);
  519. return;
  520. }
  521. var tunnel = this.getEnableTunnel();
  522. if (tunnel === null) return;
  523. var timer = setInterval(function () {
  524. var tunnel = _this5.getIdleTunnel();
  525. if (tunnel) {
  526. _this5.addData([opt]);
  527. clearInterval(timer);
  528. }
  529. }, 16);
  530. };
  531. // 添加至轨道
  532. Barrage.prototype.addBullet2Tunnel = function addBullet2Tunnel() {
  533. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  534. var tunnel = this.getIdleTunnel();
  535. if (tunnel === null) return;
  536. var tunnelId = tunnel.tunnelId;
  537. tunnel.addBullet(opt);
  538. if (tunnel.getIdleBulletNum() === 0) this.removeIdleTunnel(tunnelId);
  539. };
  540. Barrage.prototype.updateBullets = function updateBullets() {
  541. var _this6 = this;
  542. var self = this;
  543. var query = this.comp.createSelectorQuery();
  544. query.selectAll('.bullet-item').boundingClientRect(function (res) {
  545. if (!_this6._isActive) return;
  546. for (var i = 0; i < res.length; i++) {
  547. var _res$i$dataset = res[i].dataset,
  548. tunnelid = _res$i$dataset.tunnelid,
  549. bulletid = _res$i$dataset.bulletid;
  550. var tunnel = self.tunnels[tunnelid];
  551. var bullet = tunnel.bullets[bulletid];
  552. bullet.width = res[i].width;
  553. bullet.height = res[i].height;
  554. }
  555. self.animate();
  556. }).exec();
  557. };
  558. Barrage.prototype.animate = function animate() {
  559. var _this7 = this;
  560. this.tunnels.forEach(function (tunnel) {
  561. _this7.tunnelAnimate(tunnel);
  562. });
  563. };
  564. Barrage.prototype.tunnelAnimate = function tunnelAnimate(tunnel) {
  565. var _this8 = this;
  566. if (tunnel.disabled || tunnel.sending || !this._isActive) return;
  567. var next = (tunnel.last + 1) % tunnel.maxNum;
  568. var bullet = tunnel.bullets[next];
  569. if (!bullet) return;
  570. if (bullet.content || bullet.image.head || bullet.image.tail) {
  571. var _comp$setData;
  572. tunnel.sending = true;
  573. tunnel.last = next;
  574. var duration = this.duration;
  575. if (this.mode === 'overlap') {
  576. duration = this.distance * this.duration / (this.distance + bullet.width);
  577. }
  578. var passDistance = bullet.width + tunnel.safeGap;
  579. bullet.duration = duration;
  580. // 等上一条通过右边界
  581. bullet.passtime = Math.ceil(passDistance * bullet.duration * 1000 / this.distance);
  582. this.comp.setData((_comp$setData = {}, _comp$setData['tunnels[' + tunnel.tunnelId + '].bullets[' + bullet.bulletId + ']'] = bullet, _comp$setData), function () {
  583. tunnel.timer = setTimeout(function () {
  584. tunnel.sending = false;
  585. _this8.tunnelAnimate(tunnel);
  586. }, bullet.passtime);
  587. });
  588. }
  589. };
  590. Barrage.prototype.showTunnel = function showTunnel() {
  591. this.comp.setData({
  592. tunnelShow: true
  593. });
  594. };
  595. Barrage.prototype.hideTunnel = function hideTunnel() {
  596. this.comp.setData({
  597. tunnelShow: false
  598. });
  599. };
  600. Barrage.prototype.removeIdleTunnel = function removeIdleTunnel(tunnelId) {
  601. this.idleTunnels.delete(tunnelId);
  602. };
  603. Barrage.prototype.addIdleTunnel = function addIdleTunnel(tunnelId) {
  604. this.idleTunnels.add(tunnelId);
  605. };
  606. // 从可用的隧道中随机挑选一个
  607. Barrage.prototype.getEnableTunnel = function getEnableTunnel() {
  608. if (this.enableTunnels.size === 0) return null;
  609. var enableTunnels = Array.from(this.enableTunnels);
  610. var index = getRandom(enableTunnels.length);
  611. return this.tunnels[enableTunnels[index]];
  612. };
  613. // 从还有余量的隧道中随机挑选一个
  614. Barrage.prototype.getIdleTunnel = function getIdleTunnel() {
  615. if (this.idleTunnels.size === 0) return null;
  616. var idleTunnels = Array.from(this.idleTunnels);
  617. var index = getRandom(idleTunnels.length);
  618. return this.tunnels[idleTunnels[index]];
  619. };
  620. Barrage.prototype.animationend = function animationend(opt) {
  621. var _comp$setData2;
  622. var tunnelId = opt.tunnelId,
  623. bulletId = opt.bulletId;
  624. var tunnel = this.tunnels[tunnelId];
  625. var bullet = tunnel.bullets[bulletId];
  626. if (!tunnel || !bullet) return;
  627. tunnel.removeBullet(bulletId);
  628. this.addIdleTunnel(tunnelId);
  629. this.comp.setData((_comp$setData2 = {}, _comp$setData2['tunnels[' + tunnelId + '].bullets[' + bulletId + ']'] = bullet, _comp$setData2));
  630. };
  631. Barrage.prototype.tapBullet = function tapBullet(opt) {
  632. var _comp$setData3;
  633. if (!this.enableTap) return;
  634. var tunnelId = opt.tunnelId,
  635. bulletId = opt.bulletId;
  636. var tunnel = this.tunnels[tunnelId];
  637. var bullet = tunnel.bullets[bulletId];
  638. bullet.paused = !bullet.paused;
  639. this.comp.setData((_comp$setData3 = {}, _comp$setData3['tunnels[' + tunnelId + '].bullets[' + bulletId + ']'] = bullet, _comp$setData3));
  640. };
  641. return Barrage;
  642. }();
  643. exports.default = Barrage;
  644. /***/ }),
  645. /* 3 */
  646. /***/ (function(module, exports, __webpack_require__) {
  647. "use strict";
  648. exports.__esModule = true;
  649. var _utils = __webpack_require__(0);
  650. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  651. var Bullet = function () {
  652. function Bullet(barrage) {
  653. var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  654. _classCallCheck(this, Bullet);
  655. var defaultBulletOpt = {
  656. color: '#000000', // 默认黑色
  657. font: '10px sans-serif',
  658. fontSize: 10, // 全局字体大小
  659. content: '',
  660. textWidth: 0,
  661. speed: 0, // 根据屏幕停留时长计算
  662. x: 0,
  663. y: 0,
  664. tunnelId: 0,
  665. // image: {
  666. // head: {src, width, height}, // 弹幕头部添加图片
  667. // tail: {src, width, height}, // 弹幕尾部添加图片
  668. // gap: 4 // 图片与文本间隔
  669. // }
  670. image: {},
  671. imageHead: null, // Image 对象
  672. imageTail: null
  673. // status: 0 //0:待播放 1: 未完全进入屏幕 2: 完全进入屏幕 3: 完全退出屏幕
  674. };
  675. Object.assign(this, defaultBulletOpt, opt);
  676. this.barrage = barrage;
  677. this.ctx = barrage.ctx;
  678. this.canvas = barrage.canvas;
  679. }
  680. Bullet.prototype.move = function move() {
  681. var _this = this;
  682. if (this.image.head && !this.imageHead) {
  683. var Image = this.canvas.createImage();
  684. Image.src = this.image.head.src;
  685. Image.onload = function () {
  686. _this.imageHead = Image;
  687. };
  688. Image.onerror = function () {
  689. // eslint-disable-next-line no-console
  690. console.log('Fail to load image: ' + _this.image.head.src);
  691. };
  692. }
  693. if (this.image.tail && !this.imageTail) {
  694. var _Image = this.canvas.createImage();
  695. _Image.src = this.image.tail.src;
  696. _Image.onload = function () {
  697. _this.imageTail = _Image;
  698. };
  699. _Image.onerror = function () {
  700. // eslint-disable-next-line no-console
  701. console.log('Fail to load image: ' + _this.image.tail.src);
  702. };
  703. }
  704. if (this.imageHead) {
  705. var _image$head = this.image.head,
  706. _image$head$width = _image$head.width,
  707. width = _image$head$width === undefined ? this.fontSize : _image$head$width,
  708. _image$head$height = _image$head.height,
  709. height = _image$head$height === undefined ? this.fontSize : _image$head$height,
  710. _image$head$gap = _image$head.gap,
  711. gap = _image$head$gap === undefined ? 4 : _image$head$gap;
  712. var x = this.x - gap - width;
  713. var y = this.y - 0.5 * height;
  714. this.ctx.drawImage(this.imageHead, x, y, width, height);
  715. }
  716. if (this.imageTail) {
  717. var _image$tail = this.image.tail,
  718. _image$tail$width = _image$tail.width,
  719. _width = _image$tail$width === undefined ? this.fontSize : _image$tail$width,
  720. _image$tail$height = _image$tail.height,
  721. _height = _image$tail$height === undefined ? this.fontSize : _image$tail$height,
  722. _image$tail$gap = _image$tail.gap,
  723. _gap = _image$tail$gap === undefined ? 4 : _image$tail$gap;
  724. var _x2 = this.x + this.textWidth + _gap;
  725. var _y = this.y - 0.5 * _height;
  726. this.ctx.drawImage(this.imageTail, _x2, _y, _width, _height);
  727. }
  728. this.x = this.x - this.speed;
  729. this.ctx.fillStyle = this.color;
  730. this.ctx.fillText(this.content, this.x, this.y);
  731. };
  732. return Bullet;
  733. }();
  734. // tunnel(轨道)
  735. var Tunnel = function () {
  736. function Tunnel(barrage) {
  737. var opt = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
  738. _classCallCheck(this, Tunnel);
  739. var defaultTunnelOpt = {
  740. activeQueue: [], // 正在屏幕中列表
  741. nextQueue: [], // 待播放列表
  742. maxNum: 30,
  743. freeNum: 30, // 剩余可添加量
  744. height: 0,
  745. width: 0,
  746. disabled: false,
  747. tunnelId: 0,
  748. safeArea: 4,
  749. sending: false // 弹幕正在发送
  750. };
  751. Object.assign(this, defaultTunnelOpt, opt);
  752. this.freeNum = this.maxNum;
  753. this.barrage = barrage; // 控制中心
  754. this.ctx = barrage.ctx;
  755. }
  756. Tunnel.prototype.disable = function disable() {
  757. this.disabled = true;
  758. };
  759. Tunnel.prototype.enable = function enable() {
  760. this.disabled = false;
  761. };
  762. Tunnel.prototype.clear = function clear() {
  763. this.activeQueue = [];
  764. this.nextQueue = [];
  765. this.sending = false;
  766. this.freeNum = this.maxNum;
  767. this.barrage.addIdleTunnel(this.tunnelId);
  768. };
  769. Tunnel.prototype.addBullet = function addBullet(bullet) {
  770. if (this.disabled) return;
  771. if (this.freeNum === 0) return;
  772. this.nextQueue.push(bullet);
  773. this.freeNum--;
  774. if (this.freeNum === 0) {
  775. this.barrage.removeIdleTunnel(this.tunnelId);
  776. }
  777. };
  778. Tunnel.prototype.animate = function animate() {
  779. if (this.disabled) return;
  780. // 无正在发送弹幕,添加一条
  781. var nextQueue = this.nextQueue;
  782. var activeQueue = this.activeQueue;
  783. if (!this.sending && nextQueue.length > 0) {
  784. var bullet = nextQueue.shift();
  785. activeQueue.push(bullet);
  786. this.freeNum++;
  787. this.sending = true;
  788. this.barrage.addIdleTunnel(this.tunnelId);
  789. }
  790. if (activeQueue.length > 0) {
  791. activeQueue.forEach(function (bullet) {
  792. return bullet.move();
  793. });
  794. var head = activeQueue[0];
  795. var tail = activeQueue[activeQueue.length - 1];
  796. // 队首移出屏幕
  797. if (head.x + head.textWidth < 0) {
  798. activeQueue.shift();
  799. }
  800. // 队尾离开超过安全区
  801. if (tail.x + tail.textWidth + this.safeArea < this.width) {
  802. this.sending = false;
  803. }
  804. }
  805. };
  806. return Tunnel;
  807. }();
  808. var Barrage = function () {
  809. function Barrage() {
  810. var _this2 = this;
  811. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  812. _classCallCheck(this, Barrage);
  813. var defaultBarrageOpt = {
  814. font: '10px sans-serif',
  815. duration: 15, // 弹幕屏幕停留时长
  816. lineHeight: 1.2,
  817. padding: [0, 0, 0, 0],
  818. tunnelHeight: 0,
  819. tunnelNum: 0,
  820. tunnelMaxNum: 30, // 隧道最大缓冲长度
  821. maxLength: 30, // 最大字节长度,汉字算双字节
  822. safeArea: 4, // 发送时的安全间隔
  823. tunnels: [],
  824. idleTunnels: [],
  825. enableTunnels: [],
  826. alpha: 1, // 全局透明度
  827. mode: 'separate', // 弹幕重叠 overlap 不重叠 separate
  828. range: [0, 1], // 弹幕显示的垂直范围,支持两个值。[0,1]表示弹幕整个随机分布,
  829. fps: 60, // 刷新率
  830. tunnelShow: false, // 显示轨道线
  831. comp: null // 组件实例
  832. };
  833. Object.assign(this, defaultBarrageOpt, opt);
  834. var systemInfo = wx.getSystemInfoSync();
  835. this.ratio = systemInfo.pixelRatio;
  836. this.selector = '#weui-canvas';
  837. this._ready = false;
  838. this._deferred = [];
  839. var query = this.comp.createSelectorQuery();
  840. query.select(this.selector).boundingClientRect();
  841. query.select(this.selector).node();
  842. query.exec(function (res) {
  843. _this2.canvas = res[1].node;
  844. _this2.init(res[0]);
  845. _this2.ready();
  846. });
  847. }
  848. Barrage.prototype.ready = function ready() {
  849. var _this3 = this;
  850. this._ready = true;
  851. this._deferred.forEach(function (item) {
  852. // eslint-disable-next-line prefer-spread
  853. _this3[item.callback].apply(_this3, item.args);
  854. });
  855. this._deferred = [];
  856. };
  857. Barrage.prototype._delay = function _delay(method, args) {
  858. this._deferred.push({
  859. callback: method,
  860. args: args
  861. });
  862. };
  863. Barrage.prototype.init = function init() {
  864. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  865. this.width = opt.width;
  866. this.height = opt.height;
  867. this.fontSize = (0, _utils.getFontSize)(this.font);
  868. this.innerDuration = this.transfromDuration2Canvas(this.duration);
  869. var ratio = this.ratio; // 设备像素比
  870. this.canvas.width = this.width * ratio;
  871. this.canvas.height = this.height * ratio;
  872. this.ctx = this.canvas.getContext('2d');
  873. this.ctx.scale(ratio, ratio);
  874. this.ctx.textBaseline = 'middle';
  875. this.ctx.globalAlpha = this.alpha;
  876. this.ctx.font = this.font;
  877. this.idleTunnels = [];
  878. this.enableTunnels = [];
  879. this.tunnels = [];
  880. this.availableHeight = this.height - this.padding[0] - this.padding[2];
  881. this.tunnelHeight = this.fontSize * this.lineHeight;
  882. this.tunnelNum = Math.floor(this.availableHeight / this.tunnelHeight);
  883. for (var i = 0; i < this.tunnelNum; i++) {
  884. this.idleTunnels.push(i); // 空闲的隧道id集合
  885. this.enableTunnels.push(i); // 可用的隧道id集合
  886. this.tunnels.push(new Tunnel(this, { // 隧道集合
  887. width: this.width,
  888. height: this.tunnelHeight,
  889. safeArea: this.safeArea,
  890. maxNum: this.tunnelMaxNum,
  891. tunnelId: i
  892. }));
  893. }
  894. // 筛选符合范围的隧道
  895. this.setRange();
  896. this._isActive = false;
  897. };
  898. Barrage.prototype.transfromDuration2Canvas = function transfromDuration2Canvas(duration) {
  899. // 2000 是 dom 中移动的距离
  900. return duration * this.width / 2000;
  901. };
  902. // 设置显示范围 range: [0,1]
  903. Barrage.prototype.setRange = function setRange(range) {
  904. var _this4 = this;
  905. if (!this._ready) {
  906. this._delay('setRange', range);
  907. return;
  908. }
  909. range = range || this.range;
  910. var top = range[0] * this.tunnelNum;
  911. var bottom = range[1] * this.tunnelNum;
  912. // 释放符合要求的隧道
  913. // 找到目前空闲的隧道
  914. var idleTunnels = [];
  915. var enableTunnels = [];
  916. this.tunnels.forEach(function (tunnel, tunnelId) {
  917. if (tunnelId >= top && tunnelId < bottom) {
  918. tunnel.enable();
  919. enableTunnels.push(tunnelId);
  920. if (_this4.idleTunnels.indexOf(tunnelId) >= 0) {
  921. idleTunnels.push(tunnelId);
  922. }
  923. } else {
  924. tunnel.disable();
  925. }
  926. });
  927. this.idleTunnels = idleTunnels;
  928. this.enableTunnels = enableTunnels;
  929. this.range = range;
  930. };
  931. Barrage.prototype.setFont = function setFont(font) {
  932. if (!this._ready) {
  933. this._delay('setFont', font);
  934. return;
  935. }
  936. this.font = font;
  937. this.fontSize = (0, _utils.getFontSize)(this.font);
  938. this.ctx.font = font;
  939. };
  940. Barrage.prototype.setAlpha = function setAlpha(alpha) {
  941. if (!this._ready) {
  942. this._delay('setAlpha', alpha);
  943. return;
  944. }
  945. this.alpha = alpha;
  946. this.ctx.globalAlpha = alpha;
  947. };
  948. Barrage.prototype.setDuration = function setDuration(duration) {
  949. if (!this._ready) {
  950. this._delay('setDuration', duration);
  951. return;
  952. }
  953. this.clear();
  954. this.duration = duration;
  955. this.innerDuration = this.transfromDuration2Canvas(duration);
  956. };
  957. // 开启弹幕
  958. Barrage.prototype.open = function open() {
  959. if (!this._ready) {
  960. this._delay('open');
  961. return;
  962. }
  963. if (this._isActive) return;
  964. this._isActive = true;
  965. this.play();
  966. };
  967. // 关闭弹幕,清除所有数据
  968. Barrage.prototype.close = function close() {
  969. if (!this._ready) {
  970. this._delay('close');
  971. return;
  972. }
  973. if (!this._isActive) return;
  974. this._isActive = false;
  975. this.pause();
  976. this.clear();
  977. };
  978. // 开启弹幕滚动
  979. Barrage.prototype.play = function play() {
  980. var _this5 = this;
  981. this._rAFId = this.canvas.requestAnimationFrame(function () {
  982. _this5.animate();
  983. _this5.play();
  984. });
  985. };
  986. // 停止弹幕滚动
  987. Barrage.prototype.pause = function pause() {
  988. if (typeof this._rAFId === 'number') {
  989. this.canvas.cancelAnimationFrame(this._rAFId);
  990. }
  991. };
  992. // 清空屏幕和缓冲的数据
  993. Barrage.prototype.clear = function clear() {
  994. this.ctx.clearRect(0, 0, this.width, this.height);
  995. this.tunnels.forEach(function (tunnel) {
  996. return tunnel.clear();
  997. });
  998. };
  999. // 添加一批弹幕,轨道满时会被丢弃
  1000. Barrage.prototype.addData = function addData() {
  1001. var _this6 = this;
  1002. var data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
  1003. if (!this._ready) {
  1004. this._delay('addData', data);
  1005. return;
  1006. }
  1007. if (!this._isActive) return;
  1008. data.forEach(function (item) {
  1009. return _this6.addBullet2Tunnel(item);
  1010. });
  1011. };
  1012. // 发送一条弹幕
  1013. // 为保证发送成功,选取一条可用隧道,替换待发送队列队头元素
  1014. Barrage.prototype.send = function send() {
  1015. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  1016. if (!this._ready) {
  1017. this._delay('send', opt);
  1018. return;
  1019. }
  1020. var tunnel = this.getEnableTunnel();
  1021. if (tunnel === null) return;
  1022. opt.tunnelId = tunnel.tunnelId;
  1023. var bullet = this.registerBullet(opt);
  1024. tunnel.nextQueue[0] = bullet;
  1025. };
  1026. // 添加至轨道 {content, color}
  1027. Barrage.prototype.addBullet2Tunnel = function addBullet2Tunnel() {
  1028. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  1029. var tunnel = this.getIdleTunnel();
  1030. if (tunnel === null) return;
  1031. opt.tunnelId = tunnel.tunnelId;
  1032. var bullet = this.registerBullet(opt);
  1033. tunnel.addBullet(bullet);
  1034. };
  1035. Barrage.prototype.registerBullet = function registerBullet() {
  1036. var opt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  1037. opt.tunnelId = opt.tunnelId || 0;
  1038. opt.content = (0, _utils.substring)(opt.content, this.maxLength);
  1039. var textWidth = this.getTextWidth(opt.content);
  1040. var distance = this.mode === 'overlap' ? this.width + textWidth : this.width;
  1041. opt.textWidth = textWidth;
  1042. opt.speed = distance / (this.innerDuration * this.fps);
  1043. opt.fontSize = this.fontSize;
  1044. opt.x = this.width;
  1045. opt.y = this.tunnelHeight * (opt.tunnelId + 0.5) + this.padding[0];
  1046. return new Bullet(this, opt);
  1047. };
  1048. // 每帧执行的操作
  1049. Barrage.prototype.animate = function animate() {
  1050. // 清空画面后重绘
  1051. this.ctx.clearRect(0, 0, this.width, this.height);
  1052. if (this.tunnelShow) {
  1053. this.drawTunnel();
  1054. }
  1055. this.tunnels.forEach(function (tunnel) {
  1056. return tunnel.animate();
  1057. });
  1058. };
  1059. Barrage.prototype.showTunnel = function showTunnel() {
  1060. this.tunnelShow = true;
  1061. };
  1062. Barrage.prototype.hideTunnel = function hideTunnel() {
  1063. this.tunnelShow = false;
  1064. };
  1065. Barrage.prototype.removeIdleTunnel = function removeIdleTunnel(tunnelId) {
  1066. var idx = this.idleTunnels.indexOf(tunnelId);
  1067. if (idx >= 0) this.idleTunnels.splice(idx, 1);
  1068. };
  1069. Barrage.prototype.addIdleTunnel = function addIdleTunnel(tunnelId) {
  1070. var idx = this.idleTunnels.indexOf(tunnelId);
  1071. if (idx < 0) this.idleTunnels.push(tunnelId);
  1072. };
  1073. // 从可用的隧道中随机挑选一个
  1074. Barrage.prototype.getEnableTunnel = function getEnableTunnel() {
  1075. if (this.enableTunnels.length === 0) return null;
  1076. var index = (0, _utils.getRandom)(this.enableTunnels.length);
  1077. return this.tunnels[this.enableTunnels[index]];
  1078. };
  1079. // 从还有余量的隧道中随机挑选一个
  1080. Barrage.prototype.getIdleTunnel = function getIdleTunnel() {
  1081. if (this.idleTunnels.length === 0) return null;
  1082. var index = (0, _utils.getRandom)(this.idleTunnels.length);
  1083. return this.tunnels[this.idleTunnels[index]];
  1084. };
  1085. Barrage.prototype.getTextWidth = function getTextWidth(content) {
  1086. this.ctx.font = this.font;
  1087. return Math.ceil(this.ctx.measureText(content).width);
  1088. };
  1089. Barrage.prototype.drawTunnel = function drawTunnel() {
  1090. var ctx = this.ctx;
  1091. var tunnelColor = '#CCB24D';
  1092. for (var i = 0; i <= this.tunnelNum; i++) {
  1093. var y = this.padding[0] + i * this.tunnelHeight;
  1094. ctx.beginPath();
  1095. ctx.strokeStyle = tunnelColor;
  1096. ctx.setLineDash([5, 10]);
  1097. ctx.moveTo(0, y);
  1098. ctx.lineTo(this.width, y);
  1099. ctx.stroke();
  1100. if (i < this.tunnelNum) {
  1101. ctx.fillStyle = tunnelColor;
  1102. ctx.fillText('\u5F39\u9053' + (i + 1), 10, this.tunnelHeight / 2 + y);
  1103. }
  1104. }
  1105. };
  1106. return Barrage;
  1107. }();
  1108. exports.default = Barrage;
  1109. /***/ })
  1110. /******/ ]);