mirror of
https://github.com/YusufB5/ASCILINE.git
synced 2026-06-14 22:25:13 +02:00
feat: added pause mechanism and updated user interface
This commit is contained in:
parent
f0a411e349
commit
444334cfba
2 changed files with 85 additions and 2 deletions
65
app.js
65
app.js
|
|
@ -16,7 +16,7 @@ const audioEl = document.getElementById('ascii-audio');
|
|||
const volumeSlider = document.getElementById('volume-slider');
|
||||
|
||||
// ── STATE ──
|
||||
let state = 'IDLE'; // IDLE | PLAYING
|
||||
let state = 'IDLE'; // IDLE | PLAYING | PAUSED
|
||||
let ws = null;
|
||||
const frameBuffer = [];
|
||||
const BUFFER_SIZE = 4;
|
||||
|
|
@ -26,6 +26,7 @@ let frameInterval = 1000 / targetFps;
|
|||
let renderMode = 1;
|
||||
let pixelMode = false;
|
||||
let readyToRender = false;
|
||||
let pauseStartTime = 0;
|
||||
|
||||
// Grid & Dimensions
|
||||
let gridCols = 0, gridRows = 0;
|
||||
|
|
@ -242,7 +243,7 @@ function connectWebSocket() {
|
|||
ws.onopen = () => { statusEl.textContent = 'Buffering...'; };
|
||||
|
||||
ws.onclose = () => {
|
||||
if (state === 'PLAYING') {
|
||||
if (state === 'PLAYING' || state === 'PAUSED') {
|
||||
statusEl.textContent = 'Stream Ended.';
|
||||
statusEl.style.color = '#888';
|
||||
if (audioEl) audioEl.pause();
|
||||
|
|
@ -361,19 +362,79 @@ function finishStream() {
|
|||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
player.textContent = '';
|
||||
player.style.display = 'none';
|
||||
container.classList.remove('paused');
|
||||
overlay.classList.remove('hidden');
|
||||
statusEl.textContent = 'Ready';
|
||||
statusEl.style.color = 'rgba(255,255,255,0.6)';
|
||||
readyToRender = false;
|
||||
pauseStartTime = 0;
|
||||
frameBuffer.length = 0;
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════
|
||||
// PAUSE / RESUME
|
||||
// ═══════════════════════════════════════
|
||||
|
||||
function togglePause() {
|
||||
if (state === 'PLAYING') {
|
||||
state = 'PAUSED';
|
||||
pauseStartTime = performance.now();
|
||||
// Live stream approach: mute audio instead of pausing it,
|
||||
// so the master clock keeps ticking with the server.
|
||||
if (audioEl && !audioEl.paused) {
|
||||
audioEl.dataset.prePauseVolume = audioEl.volume;
|
||||
audioEl.volume = 0;
|
||||
}
|
||||
container.classList.add('paused');
|
||||
statusEl.textContent = '❚❚ PAUSED';
|
||||
statusEl.style.color = '#888';
|
||||
} else if (state === 'PAUSED') {
|
||||
state = 'PLAYING';
|
||||
pauseStartTime = 0;
|
||||
|
||||
// Restore audio volume
|
||||
if (audioEl && !audioEl.paused) {
|
||||
audioEl.volume = audioEl.dataset.prePauseVolume !== undefined
|
||||
? parseFloat(audioEl.dataset.prePauseVolume)
|
||||
: (volumeSlider ? volumeSlider.value : 1.0);
|
||||
}
|
||||
|
||||
// Flush stale buffer frames — A/V sync catch-up handles the rest
|
||||
frameBuffer.length = 0;
|
||||
|
||||
container.classList.remove('paused');
|
||||
statusEl.textContent = 'Resuming...';
|
||||
statusEl.style.color = 'var(--accent-color)';
|
||||
|
||||
// Restart render loop
|
||||
lastRenderTime = performance.now();
|
||||
lastFpsUpdate = performance.now();
|
||||
frameCount = 0;
|
||||
requestAnimationFrame(renderFrame);
|
||||
}
|
||||
}
|
||||
|
||||
// ── EVENT LISTENERS ──
|
||||
overlay.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
startStream();
|
||||
});
|
||||
|
||||
// ── PAUSE TOGGLE (click on player area) ──
|
||||
container.addEventListener('click', (e) => {
|
||||
if (e.target.closest('#play-overlay')) return;
|
||||
if (window.getSelection().toString().length > 0) return;
|
||||
togglePause();
|
||||
});
|
||||
|
||||
// ── KEYBOARD: Space to toggle pause ──
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.code === 'Space' && (state === 'PLAYING' || state === 'PAUSED')) {
|
||||
e.preventDefault();
|
||||
togglePause();
|
||||
}
|
||||
});
|
||||
|
||||
if (volumeSlider) {
|
||||
volumeSlider.addEventListener('input', () => {
|
||||
if (audioEl) audioEl.volume = volumeSlider.value;
|
||||
|
|
|
|||
22
style.css
22
style.css
|
|
@ -260,4 +260,26 @@ body {
|
|||
border-radius: 50%;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* ── PAUSE EFFECT ──────────────────────── */
|
||||
@keyframes pause-tremble {
|
||||
0%, 100% { transform: translate(0, 0); }
|
||||
15% { transform: translate(0px, 1px); }
|
||||
35% { transform: translate(1px, 0px); }
|
||||
55% { transform: translate(1px, -1px); }
|
||||
75% { transform: translate(0px, 0px); }
|
||||
}
|
||||
#player-container.paused {
|
||||
filter: saturate(0.35) brightness(0.65) contrast(1.15);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#player-container.paused #ascii-canvas,
|
||||
#player-container.paused #ascii-player {
|
||||
animation: pause-tremble 0.12s steps(3) infinite;
|
||||
}
|
||||
|
||||
#player-container.paused #ascii-player {
|
||||
pointer-events: none;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue