fix: media progress bar initial width and duration labels

This commit is contained in:
2026-08-16 21:15:00 +08:00
parent 729b4ba302
commit f3f52030f4
2 changed files with 37 additions and 7 deletions
+10
View File
@@ -301,6 +301,16 @@ body {
transition: width .3s linear; transition: width .3s linear;
} }
/* 时长文字:当前时长 / 总时长 */
/* Time labels: current / total duration */
.media-card .time-row {
display: flex;
justify-content: space-between;
font-size: 11px;
color: var(--text-dim);
margin-top: 4px;
}
/* 无时长信息时的活动脉冲进度条 */ /* 无时长信息时的活动脉冲进度条 */
/* Indeterminate pulse when no duration is available */ /* Indeterminate pulse when no duration is available */
.media-card .bar.indeterminate { .media-card .bar.indeterminate {
+26 -6
View File
@@ -161,6 +161,7 @@ function renderMediaCard(info) {
const playIcon = info.playing ? ICON_PAUSE : ICON_PLAY; const playIcon = info.playing ? ICON_PAUSE : ICON_PLAY;
// title/artist 来自任意应用元数据,转义后插入,防 XSS // title/artist 来自任意应用元数据,转义后插入,防 XSS
// title/artist come from arbitrary app metadata; escape before insert (XSS) // title/artist come from arbitrary app metadata; escape before insert (XSS)
const pct = info.duration ? Math.min(100, (info.position / info.duration) * 100) : 0;
el.innerHTML = ` el.innerHTML = `
<div class="media-layout"> <div class="media-layout">
${coverHtml} ${coverHtml}
@@ -169,7 +170,11 @@ function renderMediaCard(info) {
<div class="ar">${escapeHtml(info.artist)}</div> <div class="ar">${escapeHtml(info.artist)}</div>
</div> </div>
</div> </div>
<div class="prog"><div class="bar"></div></div> <div class="prog"><div class="bar" style="width:${pct}%"></div></div>
<div class="time-row">
<span class="t-cur">${fmtDuration(info.position)}</span>
<span class="t-total">${fmtDuration(info.duration)}</span>
</div>
<div class="controls"> <div class="controls">
<button class="ctl" data-act="prev" title="上一首">${ICON_PREV}</button> <button class="ctl" data-act="prev" title="上一首">${ICON_PREV}</button>
<button class="ctl play" data-act="play" title="播放/暂停">${playIcon}</button> <button class="ctl play" data-act="play" title="播放/暂停">${playIcon}</button>
@@ -209,20 +214,35 @@ function updateMediaProgressState(info) {
let mediaTicker = null; let mediaTicker = null;
function startMediaTicker() { function startMediaTicker() {
if (mediaTicker) return; if (mediaTicker) return;
mediaTicker = setInterval(() => { const tick = () => {
if (!mediaProgressState) return;
const bar = document.querySelector('.media-card .bar'); const bar = document.querySelector('.media-card .bar');
if (!bar || !mediaProgressState) return; const cur = document.querySelector('.media-card .t-cur');
const { position, duration, playing, updatedAt } = mediaProgressState; const { position, duration, playing, updatedAt } = mediaProgressState;
if (!duration) { if (!duration) {
// 无时长信息(部分 App 不提供 duration):显示活动脉冲而非空白 // 无时长信息(部分 App 不提供 duration):显示活动脉冲而非空白
// No duration (some apps omit it): show an indeterminate pulse instead of blank // No duration (some apps omit it): show an indeterminate pulse instead of blank
bar.classList.add('indeterminate'); if (bar) bar.classList.add('indeterminate');
return; return;
} }
bar.classList.remove('indeterminate');
const pos = playing ? position + (Date.now() - updatedAt) : position; const pos = playing ? position + (Date.now() - updatedAt) : position;
if (bar) {
bar.classList.remove('indeterminate');
bar.style.width = Math.min(100, (pos / duration) * 100) + '%'; bar.style.width = Math.min(100, (pos / duration) * 100) + '%';
}, 1000); }
if (cur) cur.textContent = fmtDuration(pos);
};
tick(); // 立即执行一次,避免初始 1 秒内进度条空白
mediaTicker = setInterval(tick, 1000);
}
// 时长格式化:毫秒 -> m:ss(未知返回 --:--
// Duration format: ms -> m:ss (--:-- when unknown)
function fmtDuration(ms) {
if (!ms || ms <= 0) return '--:--';
const s = Math.floor(ms / 1000);
const m = Math.floor(s / 60);
return m + ':' + String(s % 60).padStart(2, '0');
} }
// 布局方向:默认上下(封面在上、信息在下),横条(宽>高)时左右 // 布局方向:默认上下(封面在上、信息在下),横条(宽>高)时左右