fix: refresh timeline axes when switching songs
Publish GitHub Pages output / publish (push) Successful in 43s
Publish GitHub Pages output / publish (push) Successful in 43s
This commit is contained in:
@@ -33,6 +33,14 @@
|
||||
$: yTicks = Array.from({ length: chartMaximumScore / 500_000 + 1 }, (_, index) => index * 500_000);
|
||||
$: hoveredPoint = hoverTime === null ? null : pointAtTime(points, hoverTime);
|
||||
|
||||
// A different chart invalidates the previous pointer/keyboard selection.
|
||||
$: resetHover(notes, durationSeconds);
|
||||
|
||||
function resetHover(_notes: readonly NoteScorePoint[], _duration: number): void {
|
||||
hoverTime = null;
|
||||
tooltipPercent = 0;
|
||||
}
|
||||
|
||||
function buildPoints(source: readonly NoteScorePoint[]): ScorePoint[] {
|
||||
let averageScore = 0;
|
||||
let maximumScore = 0;
|
||||
@@ -45,27 +53,27 @@
|
||||
return result;
|
||||
}
|
||||
|
||||
function x(timeSeconds: number, width = viewWidth): number {
|
||||
return LEFT + Math.max(0, Math.min(1, timeSeconds / Math.max(1, durationSeconds))) * (width - LEFT - RIGHT);
|
||||
function x(timeSeconds: number, width: number, duration: number): number {
|
||||
return LEFT + Math.max(0, Math.min(1, timeSeconds / Math.max(1, duration))) * (width - LEFT - RIGHT);
|
||||
}
|
||||
|
||||
function y(score: number, scaleMaximum = chartMaximumScore): number {
|
||||
function y(score: number, scaleMaximum: number): number {
|
||||
return TOP + (1 - score / scaleMaximum) * PLOT_HEIGHT;
|
||||
}
|
||||
|
||||
function linePath(source: readonly ScorePoint[], field: 'averageScore' | 'maximumScore', duration: number, scaleMaximum: number, width: number): string {
|
||||
if (source.length === 0) return '';
|
||||
const commands = [`M ${x(source[0].timeSeconds, width).toFixed(2)} ${y(source[0][field], scaleMaximum).toFixed(2)}`];
|
||||
const commands = [`M ${x(source[0].timeSeconds, width, duration).toFixed(2)} ${y(source[0][field], scaleMaximum).toFixed(2)}`];
|
||||
for (let index = 1; index < source.length; index += 1) {
|
||||
const previous = source[index - 1];
|
||||
const point = source[index];
|
||||
commands.push(
|
||||
`L ${x(point.timeSeconds, width).toFixed(2)} ${y(previous[field], scaleMaximum).toFixed(2)}`,
|
||||
`L ${x(point.timeSeconds, width).toFixed(2)} ${y(point[field], scaleMaximum).toFixed(2)}`
|
||||
`L ${x(point.timeSeconds, width, duration).toFixed(2)} ${y(previous[field], scaleMaximum).toFixed(2)}`,
|
||||
`L ${x(point.timeSeconds, width, duration).toFixed(2)} ${y(point[field], scaleMaximum).toFixed(2)}`
|
||||
);
|
||||
}
|
||||
const last = source.at(-1)!;
|
||||
commands.push(`L ${x(duration, width).toFixed(2)} ${y(last[field], scaleMaximum).toFixed(2)}`);
|
||||
commands.push(`L ${x(duration, width, duration).toFixed(2)} ${y(last[field], scaleMaximum).toFixed(2)}`);
|
||||
return commands.join(' ');
|
||||
}
|
||||
|
||||
@@ -127,7 +135,7 @@
|
||||
</small>
|
||||
</span>
|
||||
{#each yTicks as tick}
|
||||
<span class="axis-value" style={`top: ${y(tick)}px;`}>{formatAxisScore(tick)}</span>
|
||||
<span class="axis-value" style={`top: ${y(tick, chartMaximumScore)}px;`}>{formatAxisScore(tick)}</span>
|
||||
{/each}
|
||||
</strong>
|
||||
<div
|
||||
@@ -153,17 +161,17 @@
|
||||
aria-label="시간에 따른 누적 평균 점수와 최고 점수 그래프"
|
||||
>
|
||||
{#each timeTicks as tick}
|
||||
<line class="time-grid-line" x1={x(tick, viewWidth)} x2={x(tick, viewWidth)} y1={TOP} y2={HEIGHT - BOTTOM} />
|
||||
<line class="time-grid-line" x1={x(tick, viewWidth, durationSeconds)} x2={x(tick, viewWidth, durationSeconds)} y1={TOP} y2={HEIGHT - BOTTOM} />
|
||||
{/each}
|
||||
{#each yTicks as tick}
|
||||
<line class="grid-line" x1={LEFT} x2={viewWidth - RIGHT} y1={y(tick)} y2={y(tick)} />
|
||||
<line class="grid-line" x1={LEFT} x2={viewWidth - RIGHT} y1={y(tick, chartMaximumScore)} y2={y(tick, chartMaximumScore)} />
|
||||
{/each}
|
||||
<path class="score-line maximum-line" d={maximumPath} />
|
||||
<path class="score-line average-line" d={averagePath} />
|
||||
{#if hoveredPoint && hoverTime !== null}
|
||||
<line class="cursor-line" x1={x(hoverTime, viewWidth)} x2={x(hoverTime, viewWidth)} y1={TOP} y2={HEIGHT - BOTTOM} />
|
||||
<circle class="point maximum-point" cx={x(hoverTime, viewWidth)} cy={y(hoveredPoint.maximumScore)} r="5" />
|
||||
<circle class="point average-point" cx={x(hoverTime, viewWidth)} cy={y(hoveredPoint.averageScore)} r="5" />
|
||||
<line class="cursor-line" x1={x(hoverTime, viewWidth, durationSeconds)} x2={x(hoverTime, viewWidth, durationSeconds)} y1={TOP} y2={HEIGHT - BOTTOM} />
|
||||
<circle class="point maximum-point" cx={x(hoverTime, viewWidth, durationSeconds)} cy={y(hoveredPoint.maximumScore, chartMaximumScore)} r="5" />
|
||||
<circle class="point average-point" cx={x(hoverTime, viewWidth, durationSeconds)} cy={y(hoveredPoint.averageScore, chartMaximumScore)} r="5" />
|
||||
{/if}
|
||||
</svg>
|
||||
|
||||
@@ -184,7 +192,7 @@
|
||||
.legend { display: flex; gap: 9px; color: #aab2bf; font-size: 10px; font-weight: 700; }
|
||||
.legend span, .tooltip span { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
|
||||
.axis-value { position: absolute; right: 7px; color: #747f90; font-size: 10px; font-weight: 650; transform: translateY(-50%); white-space: nowrap; }
|
||||
.score-track { position: relative; flex: 1 0 auto; height: 220px; overflow: hidden; border-bottom: 1px solid #465062; background: #10141a; }
|
||||
.score-track { position: relative; flex: 1 0 0; height: 220px; overflow: hidden; border-bottom: 1px solid #465062; background: #10141a; }
|
||||
i { display: inline-block; flex: none; width: 8px; height: 8px; border-radius: 50%; }
|
||||
i.average { background: #f08db8; }
|
||||
i.maximum { background: #64cbe4; }
|
||||
|
||||
Reference in New Issue
Block a user