1 | (function (global, factory) {
|
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
3 | typeof define === 'function' && define.amd ? define(factory) :
|
4 | (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Danmaku = factory());
|
5 | }(this, (function () { 'use strict';
|
6 |
|
7 | var transform = (function() {
|
8 |
|
9 | if (typeof document === 'undefined') return 'transform';
|
10 | var properties = [
|
11 | 'oTransform',
|
12 | 'msTransform',
|
13 | 'mozTransform',
|
14 | 'webkitTransform',
|
15 | 'transform'
|
16 | ];
|
17 | var style = document.createElement('div').style;
|
18 | for (var i = 0; i < properties.length; i++) {
|
19 |
|
20 | if (properties[i] in style) {
|
21 | return properties[i];
|
22 | }
|
23 | }
|
24 |
|
25 | return 'transform';
|
26 | }());
|
27 |
|
28 | function createCommentNode(cmt) {
|
29 | var node = document.createElement('div');
|
30 | node.style.cssText = 'position:absolute;';
|
31 | if (typeof cmt.render === 'function') {
|
32 | var $el = cmt.render();
|
33 | if ($el instanceof HTMLElement) {
|
34 | node.appendChild($el);
|
35 | return node;
|
36 | }
|
37 | }
|
38 | node.textContent = cmt.text;
|
39 | if (cmt.style) {
|
40 | for (var key in cmt.style) {
|
41 | node.style[key] = cmt.style[key];
|
42 | }
|
43 | }
|
44 | return node;
|
45 | }
|
46 |
|
47 | function init() {
|
48 | var stage = document.createElement('div');
|
49 | stage.style.cssText = 'overflow:hidden;white-space:nowrap;transform:translateZ(0);';
|
50 | return stage;
|
51 | }
|
52 |
|
53 | function clear(stage) {
|
54 | var lc = stage.lastChild;
|
55 | while (lc) {
|
56 | stage.removeChild(lc);
|
57 | lc = stage.lastChild;
|
58 | }
|
59 | }
|
60 |
|
61 | function resize(stage, width, height) {
|
62 | stage.style.width = width + 'px';
|
63 | stage.style.height = height + 'px';
|
64 | }
|
65 |
|
66 | function framing() {
|
67 |
|
68 | }
|
69 |
|
70 | function setup(stage, comments) {
|
71 | var df = document.createDocumentFragment();
|
72 | var i = 0;
|
73 | var cmt = null;
|
74 | for (i = 0; i < comments.length; i++) {
|
75 | cmt = comments[i];
|
76 | cmt.node = cmt.node || createCommentNode(cmt);
|
77 | df.appendChild(cmt.node);
|
78 | }
|
79 | if (comments.length) {
|
80 | stage.appendChild(df);
|
81 | }
|
82 | for (i = 0; i < comments.length; i++) {
|
83 | cmt = comments[i];
|
84 | cmt.width = cmt.width || cmt.node.offsetWidth;
|
85 | cmt.height = cmt.height || cmt.node.offsetHeight;
|
86 | }
|
87 | }
|
88 |
|
89 | function render(stage, cmt) {
|
90 | cmt.node.style[transform] = 'translate(' + cmt.x + 'px,' + cmt.y + 'px)';
|
91 | }
|
92 |
|
93 |
|
94 | function remove(stage, cmt) {
|
95 | stage.removeChild(cmt.node);
|
96 |
|
97 | if (!this.media) {
|
98 | cmt.node = null;
|
99 | }
|
100 | }
|
101 |
|
102 | var domEngine = {
|
103 | name: 'dom',
|
104 | init: init,
|
105 | clear: clear,
|
106 | resize: resize,
|
107 | framing: framing,
|
108 | setup: setup,
|
109 | render: render,
|
110 | remove: remove,
|
111 | };
|
112 |
|
113 |
|
114 | function allocate(cmt) {
|
115 | var that = this;
|
116 | var ct = this.media ? this.media.currentTime : Date.now() / 1000;
|
117 | var pbr = this.media ? this.media.playbackRate : 1;
|
118 | function willCollide(cr, cmt) {
|
119 | if (cmt.mode === 'top' || cmt.mode === 'bottom') {
|
120 | return ct - cr.time < that._.duration;
|
121 | }
|
122 | var crTotalWidth = that._.width + cr.width;
|
123 | var crElapsed = crTotalWidth * (ct - cr.time) * pbr / that._.duration;
|
124 | if (cr.width > crElapsed) {
|
125 | return true;
|
126 | }
|
127 |
|
128 | var crLeftTime = that._.duration + cr.time - ct;
|
129 | var cmtTotalWidth = that._.width + cmt.width;
|
130 | var cmtTime = that.media ? cmt.time : cmt._utc;
|
131 | var cmtElapsed = cmtTotalWidth * (ct - cmtTime) * pbr / that._.duration;
|
132 | var cmtArrival = that._.width - cmtElapsed;
|
133 |
|
134 | var cmtArrivalTime = that._.duration * cmtArrival / (that._.width + cmt.width);
|
135 | return crLeftTime > cmtArrivalTime;
|
136 | }
|
137 | var crs = this._.space[cmt.mode];
|
138 | var last = 0;
|
139 | var curr = 0;
|
140 | for (var i = 1; i < crs.length; i++) {
|
141 | var cr = crs[i];
|
142 | var requiredRange = cmt.height;
|
143 | if (cmt.mode === 'top' || cmt.mode === 'bottom') {
|
144 | requiredRange += cr.height;
|
145 | }
|
146 | if (cr.range - cr.height - crs[last].range >= requiredRange) {
|
147 | curr = i;
|
148 | break;
|
149 | }
|
150 | if (willCollide(cr, cmt)) {
|
151 | last = i;
|
152 | }
|
153 | }
|
154 | var channel = crs[last].range;
|
155 | var crObj = {
|
156 | range: channel + cmt.height,
|
157 | time: this.media ? cmt.time : cmt._utc,
|
158 | width: cmt.width,
|
159 | height: cmt.height
|
160 | };
|
161 | crs.splice(last + 1, curr - last - 1, crObj);
|
162 |
|
163 | if (cmt.mode === 'bottom') {
|
164 | return this._.height - cmt.height - channel % this._.height;
|
165 | }
|
166 | return channel % (this._.height - cmt.height);
|
167 | }
|
168 |
|
169 |
|
170 | function createEngine(framing, setup, render, remove) {
|
171 | return function() {
|
172 | framing(this._.stage);
|
173 | var dn = Date.now() / 1000;
|
174 | var ct = this.media ? this.media.currentTime : dn;
|
175 | var pbr = this.media ? this.media.playbackRate : 1;
|
176 | var cmt = null;
|
177 | var cmtt = 0;
|
178 | var i = 0;
|
179 | for (i = this._.runningList.length - 1; i >= 0; i--) {
|
180 | cmt = this._.runningList[i];
|
181 | cmtt = this.media ? cmt.time : cmt._utc;
|
182 | if (ct - cmtt > this._.duration) {
|
183 | remove(this._.stage, cmt);
|
184 | this._.runningList.splice(i, 1);
|
185 | }
|
186 | }
|
187 | var pendingList = [];
|
188 | while (this._.position < this.comments.length) {
|
189 | cmt = this.comments[this._.position];
|
190 | cmtt = this.media ? cmt.time : cmt._utc;
|
191 | if (cmtt >= ct) {
|
192 | break;
|
193 | }
|
194 |
|
195 |
|
196 |
|
197 | if (ct - cmtt > this._.duration) {
|
198 | ++this._.position;
|
199 | continue;
|
200 | }
|
201 | if (this.media) {
|
202 | cmt._utc = dn - (this.media.currentTime - cmt.time);
|
203 | }
|
204 | pendingList.push(cmt);
|
205 | ++this._.position;
|
206 | }
|
207 | setup(this._.stage, pendingList);
|
208 | for (i = 0; i < pendingList.length; i++) {
|
209 | cmt = pendingList[i];
|
210 | cmt.y = allocate.call(this, cmt);
|
211 | this._.runningList.push(cmt);
|
212 | }
|
213 | for (i = 0; i < this._.runningList.length; i++) {
|
214 | cmt = this._.runningList[i];
|
215 | var totalWidth = this._.width + cmt.width;
|
216 | var elapsed = totalWidth * (dn - cmt._utc) * pbr / this._.duration;
|
217 | if (cmt.mode === 'ltr') cmt.x = (elapsed - cmt.width + .5) | 0;
|
218 | if (cmt.mode === 'rtl') cmt.x = (this._.width - elapsed + .5) | 0;
|
219 | if (cmt.mode === 'top' || cmt.mode === 'bottom') {
|
220 | cmt.x = (this._.width - cmt.width) >> 1;
|
221 | }
|
222 | render(this._.stage, cmt);
|
223 | }
|
224 | };
|
225 | }
|
226 |
|
227 | var raf =
|
228 | (
|
229 | typeof window !== 'undefined' &&
|
230 | (
|
231 | window.requestAnimationFrame ||
|
232 | window.mozRequestAnimationFrame ||
|
233 | window.webkitRequestAnimationFrame
|
234 | )
|
235 | ) ||
|
236 | function(cb) {
|
237 | return setTimeout(cb, 50 / 3);
|
238 | };
|
239 |
|
240 | var caf =
|
241 | (
|
242 | typeof window !== 'undefined' &&
|
243 | (
|
244 | window.cancelAnimationFrame ||
|
245 | window.mozCancelAnimationFrame ||
|
246 | window.webkitCancelAnimationFrame
|
247 | )
|
248 | ) ||
|
249 | clearTimeout;
|
250 |
|
251 | function binsearch(arr, prop, key) {
|
252 | var mid = 0;
|
253 | var left = 0;
|
254 | var right = arr.length;
|
255 | while (left < right - 1) {
|
256 | mid = (left + right) >> 1;
|
257 | if (key >= arr[mid][prop]) {
|
258 | left = mid;
|
259 | } else {
|
260 | right = mid;
|
261 | }
|
262 | }
|
263 | if (arr[left] && key < arr[left][prop]) {
|
264 | return left;
|
265 | }
|
266 | return right;
|
267 | }
|
268 |
|
269 |
|
270 | function formatMode(mode) {
|
271 | if (!/^(ltr|top|bottom)$/i.test(mode)) {
|
272 | return 'rtl';
|
273 | }
|
274 | return mode.toLowerCase();
|
275 | }
|
276 |
|
277 | function collidableRange() {
|
278 | var max = 9007199254740991;
|
279 | return [
|
280 | { range: 0, time: -max, width: max, height: 0 },
|
281 | { range: max, time: max, width: 0, height: 0 }
|
282 | ];
|
283 | }
|
284 |
|
285 | function resetSpace(space) {
|
286 | space.ltr = collidableRange();
|
287 | space.rtl = collidableRange();
|
288 | space.top = collidableRange();
|
289 | space.bottom = collidableRange();
|
290 | }
|
291 |
|
292 |
|
293 | function play() {
|
294 | if (!this._.visible || !this._.paused) {
|
295 | return this;
|
296 | }
|
297 | this._.paused = false;
|
298 | if (this.media) {
|
299 | for (var i = 0; i < this._.runningList.length; i++) {
|
300 | var cmt = this._.runningList[i];
|
301 | cmt._utc = Date.now() / 1000 - (this.media.currentTime - cmt.time);
|
302 | }
|
303 | }
|
304 | var that = this;
|
305 | var engine = createEngine(
|
306 | this._.engine.framing.bind(this),
|
307 | this._.engine.setup.bind(this),
|
308 | this._.engine.render.bind(this),
|
309 | this._.engine.remove.bind(this)
|
310 | );
|
311 | function frame() {
|
312 | engine.call(that);
|
313 | that._.requestID = raf(frame);
|
314 | }
|
315 | this._.requestID = raf(frame);
|
316 | return this;
|
317 | }
|
318 |
|
319 |
|
320 | function pause() {
|
321 | if (!this._.visible || this._.paused) {
|
322 | return this;
|
323 | }
|
324 | this._.paused = true;
|
325 | caf(this._.requestID);
|
326 | this._.requestID = 0;
|
327 | return this;
|
328 | }
|
329 |
|
330 |
|
331 | function seek() {
|
332 | if (!this.media) {
|
333 | return this;
|
334 | }
|
335 | this.clear();
|
336 | resetSpace(this._.space);
|
337 | var position = binsearch(this.comments, 'time', this.media.currentTime);
|
338 | this._.position = Math.max(0, position - 1);
|
339 | return this;
|
340 | }
|
341 |
|
342 |
|
343 | function bindEvents(_) {
|
344 | _.play = play.bind(this);
|
345 | _.pause = pause.bind(this);
|
346 | _.seeking = seek.bind(this);
|
347 | this.media.addEventListener('play', _.play);
|
348 | this.media.addEventListener('pause', _.pause);
|
349 | this.media.addEventListener('playing', _.play);
|
350 | this.media.addEventListener('waiting', _.pause);
|
351 | this.media.addEventListener('seeking', _.seeking);
|
352 | }
|
353 |
|
354 |
|
355 | function unbindEvents(_) {
|
356 | this.media.removeEventListener('play', _.play);
|
357 | this.media.removeEventListener('pause', _.pause);
|
358 | this.media.removeEventListener('playing', _.play);
|
359 | this.media.removeEventListener('waiting', _.pause);
|
360 | this.media.removeEventListener('seeking', _.seeking);
|
361 | _.play = null;
|
362 | _.pause = null;
|
363 | _.seeking = null;
|
364 | }
|
365 |
|
366 |
|
367 | function init$1(opt) {
|
368 | this._ = {};
|
369 | this.container = opt.container || document.createElement('div');
|
370 | this.media = opt.media;
|
371 | this._.visible = true;
|
372 |
|
373 |
|
374 |
|
375 | {
|
376 | this.engine = 'dom';
|
377 | this._.engine = domEngine;
|
378 | }
|
379 |
|
380 | this._.requestID = 0;
|
381 |
|
382 | this._.speed = Math.max(0, opt.speed) || 144;
|
383 | this._.duration = 4;
|
384 |
|
385 | this.comments = opt.comments || [];
|
386 | this.comments.sort(function(a, b) {
|
387 | return a.time - b.time;
|
388 | });
|
389 | for (var i = 0; i < this.comments.length; i++) {
|
390 | this.comments[i].mode = formatMode(this.comments[i].mode);
|
391 | }
|
392 | this._.runningList = [];
|
393 | this._.position = 0;
|
394 |
|
395 | this._.paused = true;
|
396 | if (this.media) {
|
397 | this._.listener = {};
|
398 | bindEvents.call(this, this._.listener);
|
399 | }
|
400 |
|
401 | this._.stage = this._.engine.init(this.container);
|
402 | this._.stage.style.cssText += 'position:relative;pointer-events:none;';
|
403 |
|
404 | this.resize();
|
405 | this.container.appendChild(this._.stage);
|
406 |
|
407 | this._.space = {};
|
408 | resetSpace(this._.space);
|
409 |
|
410 | if (!this.media || !this.media.paused) {
|
411 | seek.call(this);
|
412 | play.call(this);
|
413 | }
|
414 | return this;
|
415 | }
|
416 |
|
417 |
|
418 | function destroy() {
|
419 | if (!this.container) {
|
420 | return this;
|
421 | }
|
422 |
|
423 | pause.call(this);
|
424 | this.clear();
|
425 | this.container.removeChild(this._.stage);
|
426 | if (this.media) {
|
427 | unbindEvents.call(this, this._.listener);
|
428 | }
|
429 | for (var key in this) {
|
430 |
|
431 | if (Object.prototype.hasOwnProperty.call(this, key)) {
|
432 | this[key] = null;
|
433 | }
|
434 | }
|
435 | return this;
|
436 | }
|
437 |
|
438 | var properties = ['mode', 'time', 'text', 'render', 'style'];
|
439 |
|
440 |
|
441 | function emit(obj) {
|
442 | if (!obj || Object.prototype.toString.call(obj) !== '[object Object]') {
|
443 | return this;
|
444 | }
|
445 | var cmt = {};
|
446 | for (var i = 0; i < properties.length; i++) {
|
447 | if (obj[properties[i]] !== undefined) {
|
448 | cmt[properties[i]] = obj[properties[i]];
|
449 | }
|
450 | }
|
451 | cmt.text = (cmt.text || '').toString();
|
452 | cmt.mode = formatMode(cmt.mode);
|
453 | cmt._utc = Date.now() / 1000;
|
454 | if (this.media) {
|
455 | var position = 0;
|
456 | if (cmt.time === undefined) {
|
457 | cmt.time = this.media.currentTime;
|
458 | position = this._.position;
|
459 | } else {
|
460 | position = binsearch(this.comments, 'time', cmt.time);
|
461 | if (position < this._.position) {
|
462 | this._.position += 1;
|
463 | }
|
464 | }
|
465 | this.comments.splice(position, 0, cmt);
|
466 | } else {
|
467 | this.comments.push(cmt);
|
468 | }
|
469 | return this;
|
470 | }
|
471 |
|
472 |
|
473 | function show() {
|
474 | if (this._.visible) {
|
475 | return this;
|
476 | }
|
477 | this._.visible = true;
|
478 | if (this.media && this.media.paused) {
|
479 | return this;
|
480 | }
|
481 | seek.call(this);
|
482 | play.call(this);
|
483 | return this;
|
484 | }
|
485 |
|
486 |
|
487 | function hide() {
|
488 | if (!this._.visible) {
|
489 | return this;
|
490 | }
|
491 | pause.call(this);
|
492 | this.clear();
|
493 | this._.visible = false;
|
494 | return this;
|
495 | }
|
496 |
|
497 |
|
498 | function clear$1() {
|
499 | this._.engine.clear(this._.stage, this._.runningList);
|
500 | this._.runningList = [];
|
501 | return this;
|
502 | }
|
503 |
|
504 |
|
505 | function resize$1() {
|
506 | this._.width = this.container.offsetWidth;
|
507 | this._.height = this.container.offsetHeight;
|
508 | this._.engine.resize(this._.stage, this._.width, this._.height);
|
509 | this._.duration = this._.width / this._.speed;
|
510 | return this;
|
511 | }
|
512 |
|
513 | var speed = {
|
514 | get: function() {
|
515 | return this._.speed;
|
516 | },
|
517 | set: function(s) {
|
518 | if (typeof s !== 'number' ||
|
519 | isNaN(s) ||
|
520 | !isFinite(s) ||
|
521 | s <= 0) {
|
522 | return this._.speed;
|
523 | }
|
524 | this._.speed = s;
|
525 | if (this._.width) {
|
526 | this._.duration = this._.width / s;
|
527 | }
|
528 | return s;
|
529 | }
|
530 | };
|
531 |
|
532 | function Danmaku(opt) {
|
533 | opt && init$1.call(this, opt);
|
534 | }
|
535 | Danmaku.prototype.destroy = function() {
|
536 | return destroy.call(this);
|
537 | };
|
538 | Danmaku.prototype.emit = function(cmt) {
|
539 | return emit.call(this, cmt);
|
540 | };
|
541 | Danmaku.prototype.show = function() {
|
542 | return show.call(this);
|
543 | };
|
544 | Danmaku.prototype.hide = function() {
|
545 | return hide.call(this);
|
546 | };
|
547 | Danmaku.prototype.clear = function() {
|
548 | return clear$1.call(this);
|
549 | };
|
550 | Danmaku.prototype.resize = function() {
|
551 | return resize$1.call(this);
|
552 | };
|
553 | Object.defineProperty(Danmaku.prototype, 'speed', speed);
|
554 |
|
555 | return Danmaku;
|
556 |
|
557 | })));
|