mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-21 21:01:20 -08:00
update heatmap
This commit is contained in:
1 parent
db5a7d3567
commit
56af4ca4a4
8 files changed
+134
-33
No files matched your search
@@ -86,7 +86,7 @@ export default class Danmuku {
|
||||
maxWidth: 'number',
|
||||
mount: 'undefined|htmldivelement',
|
||||
theme: 'string',
|
||||
heatmap: 'boolean',
|
||||
heatmap: 'object|boolean',
|
||||
beforeEmit: 'function',
|
||||
};
|
||||
}
|
||||
|
||||
@@ -24,18 +24,29 @@ const line = (pointA, pointB) => {
|
||||
};
|
||||
};
|
||||
|
||||
export default function heatmap(art, danmuku) {
|
||||
export default function heatmap(art, danmuku, option) {
|
||||
const { query } = art.constructor.utils;
|
||||
|
||||
art.controls.add({
|
||||
name: 'heatmap',
|
||||
position: 'top',
|
||||
html: '',
|
||||
style: {
|
||||
height: '50px',
|
||||
height: '100px',
|
||||
width: '100%',
|
||||
pointerEvents: 'none',
|
||||
},
|
||||
mounted($heatmap) {
|
||||
let $start = null;
|
||||
let $stop = null;
|
||||
|
||||
function update() {
|
||||
$start = null;
|
||||
$stop = null;
|
||||
$heatmap.innerHTML = '';
|
||||
|
||||
if (danmuku.danmus.length === 0) return;
|
||||
|
||||
const svg = {
|
||||
w: $heatmap.offsetWidth,
|
||||
h: $heatmap.offsetHeight,
|
||||
@@ -46,21 +57,32 @@ export default function heatmap(art, danmuku) {
|
||||
xMax: svg.w,
|
||||
yMin: 0,
|
||||
yMax: 128,
|
||||
scale: 0.2,
|
||||
minHeight: Math.floor(svg.h * 0.1),
|
||||
scale: 0.25,
|
||||
opacity: 0.2,
|
||||
minHeight: Math.floor(svg.h * 0.05),
|
||||
sampling: Math.floor(svg.w / 100),
|
||||
fill: 'rgba(255, 255, 255, 0.5)',
|
||||
smoothing: 0.2,
|
||||
flattening: 0,
|
||||
flattening: 0.2,
|
||||
};
|
||||
|
||||
if (typeof option === 'object') {
|
||||
Object.assign(options, option);
|
||||
}
|
||||
|
||||
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]);
|
||||
points.push([x, y]);
|
||||
}
|
||||
|
||||
const lastPoint = points[points.length - 1];
|
||||
const lastX = lastPoint[0];
|
||||
const lastY = lastPoint[1];
|
||||
if (lastX !== svg.w) {
|
||||
points.push([svg.w, lastY]);
|
||||
}
|
||||
|
||||
const yPoints = points.map((point) => point[1]);
|
||||
@@ -71,7 +93,7 @@ export default function heatmap(art, danmuku) {
|
||||
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);
|
||||
point[1] = y * (y > yMid ? 1 + options.scale : 1 - options.scale) + options.minHeight;
|
||||
}
|
||||
|
||||
const controlPoint = (current, previous, next, reverse) => {
|
||||
@@ -108,15 +130,42 @@ export default function heatmap(art, danmuku) {
|
||||
);
|
||||
|
||||
$heatmap.innerHTML = `
|
||||
<svg viewBox="0 0 ${svg.w} ${svg.h}">
|
||||
<path style="fill: var(--art-progress-color, ${options.fill})" d="${pathD}"></path>
|
||||
</svg>
|
||||
<svg viewBox="0 0 ${svg.w} ${svg.h}">
|
||||
<defs>
|
||||
<linearGradient id="heatmap-solids" x1="0%" y1="0%" x2="100%" y2="0%">
|
||||
<stop offset="0%" style="stop-color:var(--art-theme);stop-opacity:${options.opacity}" />
|
||||
<stop offset="0%" style="stop-color:var(--art-theme);stop-opacity:${options.opacity}" id="heatmap-start" />
|
||||
<stop offset="0%" style="stop-color:var(--art-progress-color);stop-opacity:1" id="heatmap-stop" />
|
||||
<stop offset="100%" style="stop-color:var(--art-progress-color);stop-opacity:1" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<path fill="url(#heatmap-solids)" d="${pathD}"></path>
|
||||
</svg>
|
||||
`;
|
||||
|
||||
$start = query('#heatmap-start', $heatmap);
|
||||
$stop = query('#heatmap-stop', $heatmap);
|
||||
$start.setAttribute('offset', `${art.played * 100}%`);
|
||||
$stop.setAttribute('offset', `${art.played * 100}%`);
|
||||
}
|
||||
|
||||
art.on('video:timeupdate', () => {
|
||||
if ($start && $stop) {
|
||||
$start.setAttribute('offset', `${art.played * 100}%`);
|
||||
$stop.setAttribute('offset', `${art.played * 100}%`);
|
||||
}
|
||||
});
|
||||
|
||||
art.on('setBar', (type, percentage) => {
|
||||
if ($start && $stop && type === 'played') {
|
||||
$start.setAttribute('offset', `${percentage * 100}%`);
|
||||
$stop.setAttribute('offset', `${percentage * 100}%`);
|
||||
}
|
||||
});
|
||||
|
||||
art.on('ready', update);
|
||||
art.on('resize', update);
|
||||
art.emit('artplayerPluginDanmuku:loaded', update);
|
||||
art.on('artplayerPluginDanmuku:loaded', update);
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -18,7 +18,7 @@ export default function artplayerPluginDanmuku(option) {
|
||||
checkVersion(art);
|
||||
const danmuku = new Danmuku(art, option);
|
||||
setting(art, danmuku);
|
||||
if (option.heatmap) heatmap(art, danmuku);
|
||||
if (option.heatmap) heatmap(art, danmuku, option.heatmap);
|
||||
return {
|
||||
name: 'artplayerPluginDanmuku',
|
||||
emit: danmuku.emit.bind(danmuku),
|
||||
|
||||
@@ -119,7 +119,20 @@ type Option = {
|
||||
/**
|
||||
* 是否开启弹幕热度图
|
||||
*/
|
||||
heatmap?: boolean;
|
||||
heatmap?:
|
||||
| boolean
|
||||
| {
|
||||
xMin?: number;
|
||||
xMax?: number;
|
||||
yMin?: number;
|
||||
yMax?: number;
|
||||
scale?: number;
|
||||
opacity?: number;
|
||||
minHeight?: number;
|
||||
sampling?: number;
|
||||
smoothing?: number;
|
||||
flattening?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* 发送弹幕前的自定义校验,返回 true 则可以发送
|
||||
|
||||
Reference in new issue
Block a user