diff --git a/Readme.md b/Readme.md
index e7ee1fcc8..1cf9ec678 100644
--- a/Readme.md
+++ b/Readme.md
@@ -9,7 +9,8 @@
- 日本語への対応
- 全体的な UI の見直しとブラッシュアップ
-- Danmaku(コメント)描画周りの修正
+- Danmaku(コメント)描画周りの大幅な修正
+- Danmaku(コメント)のサイズ変更 (big, medium, small) に対応
- スマホ・タブレットや日本語対応により崩れる CSS の修正
- スマホ・タブレット向け UI の最適化と改善
- スマホ・タブレット向けの早送り・巻き戻しボタンの追加
diff --git a/src/css/danmaku.scss b/src/css/danmaku.scss
index 779b53b32..17534623a 100644
--- a/src/css/danmaku.scss
+++ b/src/css/danmaku.scss
@@ -31,13 +31,19 @@
box-decoration-break: clone;
-webkit-box-decoration-break: clone;
}
+ &.dplayer-danmaku-size-big {
+ font-size: calc(var(--dplayer-danmaku-font-size) * 1.25);
+ }
+ &.dplayer-danmaku-size-small {
+ font-size: calc(var(--dplayer-danmaku-font-size) * 0.8);
+ }
}
.dplayer-danmaku-right {
position: absolute;
right: 0;
transform: translateX(100%);
&.dplayer-danmaku-move {
- animation-name: 'danmaku';
+ animation-name: danmaku;
animation-timing-function: linear;
animation-play-state: paused;
}
@@ -54,7 +60,7 @@
transform: translateX(-50%);
visibility: hidden;
&.dplayer-danmaku-move {
- animation-name: 'danmaku-center';
+ animation-name: danmaku-center;
animation-timing-function: linear;
animation-play-state: paused;
}
diff --git a/src/js/danmaku.js b/src/js/danmaku.js
index 8fd2ad9bf..c13ff1d8b 100644
--- a/src/js/danmaku.js
+++ b/src/js/danmaku.js
@@ -11,7 +11,7 @@ class Danmaku {
bottom: {},
};
this.danIndex = 0;
- this.danFontSize = '24px';
+ this.danFontSize = 24; // 24px
this.dan = [];
this.showing = true;
this._opacity = this.options.opacity;
@@ -34,9 +34,7 @@ class Danmaku {
this.events && this.events.trigger('danmaku_load_start', endpoints);
this._readAllEndpoints(endpoints, (results) => {
- this.dan = [].concat
- .apply([], results)
- .sort((a, b) => a.time - b.time);
+ this.dan = [].concat.apply([], results).sort((a, b) => a.time - b.time);
window.requestAnimationFrame(() => {
this.frame();
});
@@ -94,6 +92,7 @@ class Danmaku {
text: dan.text,
color: dan.color,
type: dan.type,
+ size: dan.size,
};
this.options.apiBackend.send({
@@ -106,7 +105,8 @@ class Danmaku {
text: this.htmlEncode(danmakuData.text),
color: danmakuData.color,
type: danmakuData.type,
- border: `2px solid ${this.options.borderColor}`,
+ size: danmakuData.size,
+ border: true,
});
this.events && this.events.trigger('danmaku_send', danmakuData);
@@ -151,30 +151,39 @@ class Danmaku {
*
* @param {Object Array} dan - {text, color, type}
* text - danmaku content
- * color - danmaku color, default: `#fff`
+ * color - danmaku color, default: `#ffeaea`
* type - danmaku type, `right` `top` `bottom`, default: `right`
+ * size - danmaku size, `medium` `big` `small`, default: `medium`
*/
draw(dan) {
if (this.showing) {
- // adjust the comment size according to the screen size
+
+ // if the dan variable is an object, create and assign an array of only one object
+ if (Object.prototype.toString.call(dan) !== '[object Array]') {
+ dan = [dan];
+ }
+
+ // adjust the font size according to the screen size
const ratioRate = 1.25; // magic!
- const ratio = (this.container.offsetWidth / 1024) * ratioRate < 1 ? (this.container.offsetWidth / 1024) * ratioRate : 1;
- const itemFontSize = this.options.fontSize * ratio;
- const itemHeight = itemFontSize + 6 * ratio; // 6 is the vertical margin of danmaku
+ let ratio = this.container.offsetWidth / 1024 * ratioRate;
+ if (ratio >= 1) ratio = 1; // ratio should not exceed 1
+ let itemFontSize = this.options.fontSize * ratio;
+ const itemHeight = itemFontSize + (6 * ratio); // 6 is the vertical margin of danmaku
const danWidth = this.container.offsetWidth;
- const danHeight = this.container.offsetWidth;
+ const danHeight = this.container.offsetHeight;
const itemY = parseInt(danHeight / itemHeight);
- const danItemRight = (ele) => {
- const eleWidth = ele.offsetWidth || parseInt(ele.style.width);
- const eleRight = ele.getBoundingClientRect().right || this.container.getBoundingClientRect().right + eleWidth;
- return this.container.getBoundingClientRect().right - eleRight;
+ const danItemRight = (danmakuItem) => {
+ const danmakuItemWidth = danmakuItem.offsetWidth || parseInt(danmakuItem.style.width);
+ const danmakuItemRight =
+ danmakuItem.getBoundingClientRect().right || this.container.getBoundingClientRect().right + danmakuItemWidth;
+ return this.container.getBoundingClientRect().right - danmakuItemRight;
};
const danSpeed = (width) => (danWidth + width) / 5;
- const getTunnel = (ele, type, width) => {
+ const getTunnel = (danmakuItem, type, width) => {
const tmp = danWidth / danSpeed(width);
for (let i = 0; this.unlimited || i < itemY; i++) {
@@ -189,16 +198,16 @@ class Danmaku {
break;
}
if (j === item.length - 1) {
- this.danTunnel[type][i + ''].push(ele);
- ele.addEventListener('animationend', () => {
+ this.danTunnel[type][i + ''].push(danmakuItem);
+ danmakuItem.addEventListener('animationend', () => {
this.danTunnel[type][i + ''].splice(0, 1);
});
return i % itemY;
}
}
} else {
- this.danTunnel[type][i + ''] = [ele];
- ele.addEventListener('animationend', () => {
+ this.danTunnel[type][i + ''] = [danmakuItem];
+ danmakuItem.addEventListener('animationend', () => {
this.danTunnel[type][i + ''].splice(0, 1);
});
return i % itemY;
@@ -207,93 +216,127 @@ class Danmaku {
return -1;
};
- if (Object.prototype.toString.call(dan) !== '[object Array]') {
- dan = [dan];
- }
-
const docFragment = document.createDocumentFragment();
for (let i = 0; i < dan.length; i++) {
+
+ // Whether the type is numeric (for compatibility)
+ if (isFinite(dan[i].color)) {
+ dan[i].color = utils.number2Color(dan[i].color);
+ }
if (isFinite(dan[i].type)) {
- // Whether the type is numeric
dan[i].type = utils.number2Type(dan[i].type);
}
+
+ // set default danmaku color
if (!dan[i].color) {
- dan[i].color = 16777215; // 0xFFFFFF
+ dan[i].color = '#ffeaea'; // white
}
- const item = document.createElement('div');
- item.classList.add('dplayer-danmaku-item');
- item.classList.add(`dplayer-danmaku-${dan[i].type}`);
- item.addEventListener('animationend', () => {
- this.container.removeChild(item);
- });
- // show line break
- dan[i].text = dan[i].text.replace(/\n/g, '
');
- if (dan[i].border) {
- item.innerHTML = `${dan[i].text}`;
- } else {
- item.innerHTML = dan[i].text;
+ // set default danmaku type
+ if (!dan[i].type || (dan[i].type !== 'right' && dan[i].type !== 'top' && dan[i].type !== 'bottom')) {
+ dan[i].type = 'right';
}
- // set danmaku color
- item.style.color = utils.number2Color(dan[i].color);
+ // set default danmaku size
+ if (!dan[i].size) {
+ dan[i].size = 'medium';
+ }
+
+ // set danmaku size
+ // used to calculate danmaku width
+ // danmaku size doesn't affect itemHeight
+ switch (dan[i].size) {
+ case 'big':
+ itemFontSize = itemFontSize * 1.25;
+ break;
+ case 'small':
+ itemFontSize = itemFontSize * 0.8;
+ break;
+ }
const itemWidth = (() => {
let measure = 0;
// returns the width of the widest line
- dan[i].text.split('
').forEach((text) => {
- const result = this._measure(text, itemFontSize);
+ for (const line of dan[i].text.split('\n')) {
+ const result = this._measure(line, itemFontSize);
if (result > measure) {
measure = result;
}
- });
+ }
return measure;
})();
- let tunnel;
-
- // adjust
- switch (dan[i].type) {
- case 'right':
- tunnel = getTunnel(item, dan[i].type, itemWidth);
- if (tunnel >= 0) {
- item.style.width = itemWidth + 1 + 'px';
- item.style.top = itemHeight * tunnel + 8 + 'px';
- item.style.transform = `translateX(-${danWidth}px)`;
- item.style.willChange = 'transform';
- }
- break;
- case 'top':
- tunnel = getTunnel(item, dan[i].type);
- if (tunnel >= 0) {
- item.style.top = itemHeight * tunnel + 8 + 'px';
- item.style.willChange = 'visibility';
- }
- break;
- case 'bottom':
- tunnel = getTunnel(item, dan[i].type);
- if (tunnel >= 0) {
- item.style.bottom = itemHeight * tunnel + 8 + 'px';
- item.style.willChange = 'visibility';
- }
- break;
- default:
- console.error(`Can't handled danmaku type: ${dan[i].type}`);
- }
- if (tunnel >= 0) {
- // move
- item.classList.add('dplayer-danmaku-move');
- item.style.animationDuration = this._danAnimation(dan[i].type);
+ // repeat for each line of danmaku
+ // if danmaku type is bottom, the order must be reversed
+ const lines = dan[i].text.split('\n');
+ for (const line of (dan[i].type === 'bottom') ? lines.reverse() : lines) {
+
+ const danmakuItem = document.createElement('div');
+ danmakuItem.classList.add('dplayer-danmaku-item');
+ danmakuItem.classList.add(`dplayer-danmaku-${dan[i].type}`); // set danmaku type (CSS)
+ danmakuItem.classList.add(`dplayer-danmaku-size-${dan[i].size}`); // set danmaku size (CSS)
- // insert
- docFragment.appendChild(item);
+ // set danmaku color
+ danmakuItem.style.color = dan[i].color;
+
+ // set danmaku text
+ if (dan[i].border) {
+ danmakuItem.innerHTML = `${line}`;
+ } else {
+ danmakuItem.innerHTML = line;
+ }
+
+ // set event to remove this danmaku
+ danmakuItem.addEventListener('animationend', () => {
+ this.container.removeChild(danmakuItem);
+ });
+
+ // ensure and adjust danmaku position
+ const tunnel = getTunnel(danmakuItem, dan[i].type, itemWidth);
+ switch (dan[i].type) {
+ case 'right':
+ if (tunnel >= 0) {
+ danmakuItem.style.width = itemWidth + 1 + 'px';
+ danmakuItem.style.top = itemHeight * tunnel + 8 + 'px';
+ danmakuItem.style.transform = `translateX(-${danWidth}px)`;
+ danmakuItem.style.willChange = 'transform';
+ }
+ break;
+ case 'top':
+ if (tunnel >= 0) {
+ danmakuItem.style.width = itemWidth + 1 + 'px';
+ danmakuItem.style.top = itemHeight * tunnel + 8 + 'px';
+ danmakuItem.style.willChange = 'visibility';
+ }
+ break;
+ case 'bottom':
+ if (tunnel >= 0) {
+ danmakuItem.style.width = itemWidth + 1 + 'px';
+ danmakuItem.style.bottom = itemHeight * tunnel + 8 + 'px';
+ danmakuItem.style.willChange = 'visibility';
+ }
+ break;
+ default:
+ console.error(`Can't handled danmaku type: ${dan[i].type}`);
+ }
+
+ if (tunnel >= 0) {
+ // move
+ danmakuItem.classList.add('dplayer-danmaku-move');
+ danmakuItem.style.animationDuration = this._danAnimation(dan[i].type);
+
+ // insert
+ docFragment.appendChild(danmakuItem);
+ }
}
}
+ // set base danmaku font size
this.container.style.setProperty('--dplayer-danmaku-font-size', `${itemFontSize}px`);
- this.container.appendChild(docFragment);
+ // draw danmaku
+ this.container.appendChild(docFragment);
return docFragment;
}
}
@@ -310,7 +353,7 @@ class Danmaku {
if (!this.context || this.danFontSize !== itemFontSize) {
this.danFontSize = itemFontSize;
this.context = document.createElement('canvas').getContext('2d');
- this.context.font = 'bold ' + this.danFontSize + 'px ' + '"Segoe UI", Arial';
+ this.context.font = `bold ${this.danFontSize}px "Segoe UI", Arial`;
}
return this.context.measureText(text).width;
}
diff --git a/src/js/i18n.js b/src/js/i18n.js
index 9dad6f430..8f237f62f 100644
--- a/src/js/i18n.js
+++ b/src/js/i18n.js
@@ -151,7 +151,7 @@ const tranTxt = {
'Please input danmaku content!': 'コメント内容を入力して下さい!',
'Set danmaku color': 'コメントの色',
'Set danmaku type': 'コメントの位置',
- 'Set danmaku size': 'コメントの大きさ',
+ 'Set danmaku size': 'コメントのサイズ',
'Show danmaku': 'コメントを表示',
'Video load failed': '動画の読み込みに失敗しました…',
'Danmaku load failed': 'コメントの読み込みに失敗しました…',