play/pause button
This commit is contained in:
@@ -35,6 +35,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="transport">
|
||||
<button id="playpause" type="button">⏸ Pause</button>
|
||||
</div>
|
||||
|
||||
<ol id="list"></ol>
|
||||
<p id="empty">Nothing queued yet.</p>
|
||||
|
||||
|
||||
@@ -129,6 +129,37 @@ h1 {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* --- transport ---------------------------------------------------------- */
|
||||
|
||||
#transport {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
#playpause {
|
||||
width: 100%;
|
||||
font: inherit;
|
||||
font-weight: 700;
|
||||
padding: 12px 24px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #43213a;
|
||||
background: var(--panel);
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Paused is the state that wants acting on, so that is the one wearing the
|
||||
accent. */
|
||||
#playpause.paused {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
#playpause:disabled {
|
||||
opacity: 0.4;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
/* --- list --------------------------------------------------------------- */
|
||||
|
||||
#list {
|
||||
|
||||
+28
-2
@@ -1,7 +1,13 @@
|
||||
import { connect, formatDuration } from '/shared/socket.js';
|
||||
import { BACKGROUNDS, AUTO_BACKGROUND, ANIMATIONS, AUTO_ANIMATION } from '/config.js';
|
||||
|
||||
let state = { items: [], currentId: null, background: AUTO_BACKGROUND, animation: AUTO_ANIMATION };
|
||||
let state = {
|
||||
items: [],
|
||||
currentId: null,
|
||||
background: AUTO_BACKGROUND,
|
||||
animation: AUTO_ANIMATION,
|
||||
paused: false,
|
||||
};
|
||||
// Last playhead report, kept with its arrival time so the countdown can be
|
||||
// extrapolated smoothly between the 1 Hz updates.
|
||||
let progress = { id: null, currentTime: 0, at: 0 };
|
||||
@@ -12,6 +18,7 @@ const errorBox = document.getElementById('error');
|
||||
const statusBox = document.getElementById('status');
|
||||
const backgroundBox = document.getElementById('backgrounds');
|
||||
const animationBox = document.getElementById('animations');
|
||||
const playPause = document.getElementById('playpause');
|
||||
|
||||
const socket = connect({ onMessage, onStatus });
|
||||
|
||||
@@ -38,9 +45,26 @@ function showError(message) {
|
||||
function render() {
|
||||
list.replaceChildren(...state.items.map(renderItem));
|
||||
empty.hidden = state.items.length > 0;
|
||||
renderTransport();
|
||||
renderTotals();
|
||||
}
|
||||
|
||||
// --- play / pause -------------------------------------------------------
|
||||
|
||||
playPause.addEventListener('click', () => {
|
||||
socket.send({ type: 'setPaused', paused: !state.paused });
|
||||
});
|
||||
|
||||
/** Mirrors the server's flag rather than toggling locally, so every phone in
|
||||
* the room agrees about what the player is doing. */
|
||||
function renderTransport() {
|
||||
const paused = Boolean(state.paused);
|
||||
playPause.textContent = paused ? '▶ Play' : '⏸ Pause';
|
||||
playPause.title = paused ? 'Resume playback' : 'Pause playback';
|
||||
playPause.classList.toggle('paused', paused);
|
||||
playPause.disabled = state.items.length === 0;
|
||||
}
|
||||
|
||||
function renderItem(item, index) {
|
||||
const li = document.createElement('li');
|
||||
li.className = 'item';
|
||||
@@ -117,7 +141,9 @@ function renderTotals() {
|
||||
/** Playhead of the current item, extrapolated since the last report. */
|
||||
function playheadFor(item) {
|
||||
if (progress.id !== item.id) return 0;
|
||||
const elapsed = (Date.now() - progress.at) / 1000;
|
||||
// Paused, the playhead is standing still — extrapolating would run the
|
||||
// countdown down between reports and then jump it back on the next one.
|
||||
const elapsed = state.paused ? 0 : (Date.now() - progress.at) / 1000;
|
||||
return Math.min(progress.currentTime + elapsed, item.duration ?? Infinity);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user