fix: media progress bar initial width and duration labels
This commit is contained in:
@@ -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 {
|
||||||
|
|||||||
@@ -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');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 布局方向:默认上下(封面在上、信息在下),横条(宽>高)时左右
|
// 布局方向:默认上下(封面在上、信息在下),横条(宽>高)时左右
|
||||||
|
|||||||
Reference in New Issue
Block a user