mirror of
https://github.com/YusufB5/ASCILINE.git
synced 2026-06-29 22:59:38 +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');
|
const volumeSlider = document.getElementById('volume-slider');
|
||||||
|
|
||||||
// ── STATE ──
|
// ── STATE ──
|
||||||
let state = 'IDLE'; // IDLE | PLAYING
|
let state = 'IDLE'; // IDLE | PLAYING | PAUSED
|
||||||
let ws = null;
|
let ws = null;
|
||||||
const frameBuffer = [];
|
const frameBuffer = [];
|
||||||
const BUFFER_SIZE = 4;
|
const BUFFER_SIZE = 4;
|
||||||
|
|
@ -26,6 +26,7 @@ let frameInterval = 1000 / targetFps;
|
||||||
let renderMode = 1;
|
let renderMode = 1;
|
||||||
let pixelMode = false;
|
let pixelMode = false;
|
||||||
let readyToRender = false;
|
let readyToRender = false;
|
||||||
|
let pauseStartTime = 0;
|
||||||
|
|
||||||
// Grid & Dimensions
|
// Grid & Dimensions
|
||||||
let gridCols = 0, gridRows = 0;
|
let gridCols = 0, gridRows = 0;
|
||||||
|
|
@ -242,7 +243,7 @@ function connectWebSocket() {
|
||||||
ws.onopen = () => { statusEl.textContent = 'Buffering...'; };
|
ws.onopen = () => { statusEl.textContent = 'Buffering...'; };
|
||||||
|
|
||||||
ws.onclose = () => {
|
ws.onclose = () => {
|
||||||
if (state === 'PLAYING') {
|
if (state === 'PLAYING' || state === 'PAUSED') {
|
||||||
statusEl.textContent = 'Stream Ended.';
|
statusEl.textContent = 'Stream Ended.';
|
||||||
statusEl.style.color = '#888';
|
statusEl.style.color = '#888';
|
||||||
if (audioEl) audioEl.pause();
|
if (audioEl) audioEl.pause();
|
||||||
|
|
@ -361,19 +362,79 @@ function finishStream() {
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
player.textContent = '';
|
player.textContent = '';
|
||||||
player.style.display = 'none';
|
player.style.display = 'none';
|
||||||
|
container.classList.remove('paused');
|
||||||
overlay.classList.remove('hidden');
|
overlay.classList.remove('hidden');
|
||||||
statusEl.textContent = 'Ready';
|
statusEl.textContent = 'Ready';
|
||||||
statusEl.style.color = 'rgba(255,255,255,0.6)';
|
statusEl.style.color = 'rgba(255,255,255,0.6)';
|
||||||
readyToRender = false;
|
readyToRender = false;
|
||||||
|
pauseStartTime = 0;
|
||||||
frameBuffer.length = 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 ──
|
// ── EVENT LISTENERS ──
|
||||||
overlay.addEventListener('click', (e) => {
|
overlay.addEventListener('click', (e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
startStream();
|
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) {
|
if (volumeSlider) {
|
||||||
volumeSlider.addEventListener('input', () => {
|
volumeSlider.addEventListener('input', () => {
|
||||||
if (audioEl) audioEl.volume = volumeSlider.value;
|
if (audioEl) audioEl.volume = volumeSlider.value;
|
||||||
|
|
|
||||||
22
style.css
22
style.css
|
|
@ -260,4 +260,26 @@ body {
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
border: none;
|
border: none;
|
||||||
cursor: pointer;
|
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