mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-23 05:41:20 -08:00
1 parent
5ba1569f67
commit
e3658aed6f
6 files changed
+272
-27
No files matched your search
@@ -11,6 +11,7 @@ export default class Danmuku {
|
||||
this.$player = template.$player;
|
||||
|
||||
this.art = art;
|
||||
this.danmus = [];
|
||||
this.queue = [];
|
||||
this.option = {};
|
||||
this.$refs = [];
|
||||
@@ -61,6 +62,7 @@ export default class Danmuku {
|
||||
maxWidth: 400,
|
||||
mount: undefined,
|
||||
theme: 'dark',
|
||||
heatmap: false,
|
||||
beforeEmit: () => true,
|
||||
};
|
||||
}
|
||||
@@ -84,6 +86,7 @@ export default class Danmuku {
|
||||
maxWidth: 'number',
|
||||
mount: 'undefined|htmldivelement',
|
||||
theme: 'string',
|
||||
heatmap: 'boolean',
|
||||
beforeEmit: 'function',
|
||||
};
|
||||
}
|
||||
@@ -236,23 +239,22 @@ export default class Danmuku {
|
||||
|
||||
async load() {
|
||||
try {
|
||||
let danmus = [];
|
||||
if (typeof this.option.danmuku === 'function') {
|
||||
danmus = await this.option.danmuku();
|
||||
this.danmus = await this.option.danmuku();
|
||||
} else if (typeof this.option.danmuku.then === 'function') {
|
||||
danmus = await this.option.danmuku;
|
||||
this.danmus = await this.option.danmuku;
|
||||
} else if (typeof this.option.danmuku === 'string') {
|
||||
danmus = await bilibiliDanmuParseFromUrl(this.option.danmuku);
|
||||
this.danmus = await bilibiliDanmuParseFromUrl(this.option.danmuku);
|
||||
} else {
|
||||
danmus = this.option.danmuku;
|
||||
this.danmus = this.option.danmuku;
|
||||
}
|
||||
|
||||
this.utils.errorHandle(Array.isArray(danmus), 'Danmuku need return an array as result');
|
||||
this.art.emit('artplayerPluginDanmuku:loaded', danmus);
|
||||
this.utils.errorHandle(Array.isArray(this.danmus), 'Danmuku need return an array as result');
|
||||
this.art.emit('artplayerPluginDanmuku:loaded', this.danmus);
|
||||
|
||||
this.queue = [];
|
||||
this.$danmuku.innerText = '';
|
||||
danmus.forEach((danmu) => this.emit(danmu));
|
||||
this.danmus.forEach((danmu) => this.emit(danmu));
|
||||
} catch (error) {
|
||||
this.art.emit('artplayerPluginDanmuku:error', error);
|
||||
throw error;
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
const lib = {
|
||||
map(value, inMin, inMax, outMin, outMax) {
|
||||
return ((value - inMin) * (outMax - outMin)) / (inMax - inMin) + outMin;
|
||||
},
|
||||
range(start, end, tick) {
|
||||
const s = Math.round(start / tick) * tick;
|
||||
return Array.from(
|
||||
{
|
||||
length: Math.floor((end - start) / tick),
|
||||
},
|
||||
(v, k) => {
|
||||
return k * tick + s;
|
||||
},
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
const line = (pointA, pointB) => {
|
||||
const lengthX = pointB[0] - pointA[0];
|
||||
const lengthY = pointB[1] - pointA[1];
|
||||
return {
|
||||
length: Math.sqrt(Math.pow(lengthX, 2) + Math.pow(lengthY, 2)),
|
||||
angle: Math.atan2(lengthY, lengthX),
|
||||
};
|
||||
};
|
||||
|
||||
export default function heatmap(art, danmuku) {
|
||||
art.controls.add({
|
||||
name: 'heatmap',
|
||||
position: 'top',
|
||||
html: '',
|
||||
style: {
|
||||
height: '50px',
|
||||
width: '100%',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
mounted($heatmap) {
|
||||
function update() {
|
||||
const svg = {
|
||||
w: $heatmap.offsetWidth,
|
||||
h: $heatmap.offsetHeight,
|
||||
};
|
||||
|
||||
const options = {
|
||||
xMin: 0,
|
||||
xMax: svg.w,
|
||||
yMin: 0,
|
||||
yMax: 128,
|
||||
scale: 0.2,
|
||||
minHeight: Math.floor(svg.h * 0.1),
|
||||
sampling: Math.floor(svg.w / 100),
|
||||
fill: 'rgba(255, 255, 255, 0.5)',
|
||||
smoothing: 0.2,
|
||||
flattening: 0,
|
||||
};
|
||||
|
||||
const points = [];
|
||||
const gap = art.duration / svg.w;
|
||||
for (let x = 0; x <= svg.w; x += options.sampling) {
|
||||
const y = danmuku.danmus.filter(
|
||||
({ time }) => time > x * gap && time <= (x + options.sampling) * gap,
|
||||
).length;
|
||||
points.push([x, y + options.minHeight]);
|
||||
}
|
||||
|
||||
const yPoints = points.map((point) => point[1]);
|
||||
const yMin = Math.min(...yPoints);
|
||||
const yMax = Math.max(...yPoints);
|
||||
const yMid = (yMin + yMax) / 2;
|
||||
|
||||
for (let i = 0; i < points.length; i++) {
|
||||
const point = points[i];
|
||||
const y = point[1];
|
||||
point[1] = y * (y > yMid ? 1 + options.scale : 1 - options.scale);
|
||||
}
|
||||
|
||||
const controlPoint = (current, previous, next, reverse) => {
|
||||
const p = previous || current;
|
||||
const n = next || current;
|
||||
const o = line(p, n);
|
||||
const flat = lib.map(Math.cos(o.angle) * options.flattening, 0, 1, 1, 0);
|
||||
const angle = o.angle * flat + (reverse ? Math.PI : 0);
|
||||
const length = o.length * options.smoothing;
|
||||
const x = current[0] + Math.cos(angle) * length;
|
||||
const y = current[1] + Math.sin(angle) * length;
|
||||
return [x, y];
|
||||
};
|
||||
|
||||
const bezierCommand = (point, i, a) => {
|
||||
const cps = controlPoint(a[i - 1], a[i - 2], point);
|
||||
const cpe = controlPoint(point, a[i - 1], a[i + 1], true);
|
||||
const close = i === a.length - 1 ? ' z' : '';
|
||||
return `C ${cps[0]},${cps[1]} ${cpe[0]},${cpe[1]} ${point[0]},${point[1]}${close}`;
|
||||
};
|
||||
|
||||
const pointsPositions = points.map((e) => {
|
||||
const x = lib.map(e[0], options.xMin, options.xMax, 0, svg.w);
|
||||
const y = lib.map(e[1], options.yMin, options.yMax, svg.h, 0);
|
||||
return [x, y];
|
||||
});
|
||||
|
||||
const pathD = pointsPositions.reduce(
|
||||
(acc, e, i, a) =>
|
||||
i === 0
|
||||
? `M ${a[a.length - 1][0]},${svg.h} L ${e[0]},${svg.h} L ${e[0]},${e[1]}`
|
||||
: `${acc} ${bezierCommand(e, i, a)}`,
|
||||
'',
|
||||
);
|
||||
|
||||
$heatmap.innerHTML = `
|
||||
<svg viewBox="0 0 ${svg.w} ${svg.h}">
|
||||
<path style="fill: var(--art-progress-color, ${options.fill})" d="${pathD}"></path>
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
art.on('ready', update);
|
||||
art.on('resize', update);
|
||||
art.emit('artplayerPluginDanmuku:loaded', update);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import Danmuku from './danmuku';
|
||||
import setting from './setting';
|
||||
import heatmap from './heatmap';
|
||||
|
||||
function checkVersion(art) {
|
||||
const {
|
||||
@@ -17,6 +18,7 @@ export default function artplayerPluginDanmuku(option) {
|
||||
checkVersion(art);
|
||||
const danmuku = new Danmuku(art, option);
|
||||
setting(art, danmuku);
|
||||
if (option.heatmap) heatmap(art, danmuku);
|
||||
return {
|
||||
name: 'artplayerPluginDanmuku',
|
||||
emit: danmuku.emit.bind(danmuku),
|
||||
|
||||
Reference in new issue
Block a user