Ajuste le maintien de demarrage du cube

This commit is contained in:
2026-04-12 19:43:31 +02:00
parent 2dc41cc758
commit a6e007762d
2 changed files with 90 additions and 5 deletions

55
app.js
View File

@@ -7,7 +7,7 @@ const DEFAULT_BLOCK_DURATION_MS = 180000;
const DEFAULT_MOVE_LIMIT_MS = 20000; const DEFAULT_MOVE_LIMIT_MS = 20000;
const TIME_MODE_INITIAL_CLOCK_MS = 600000; const TIME_MODE_INITIAL_CLOCK_MS = 600000;
const CUBE_TIME_CAP_MS = 120000; const CUBE_TIME_CAP_MS = 120000;
const CUBE_START_HOLD_MS = 300; const CUBE_START_HOLD_MS = 2000;
const PRESETS = { const PRESETS = {
fast: { fast: {
@@ -584,6 +584,7 @@ function initCubePage() {
white: createCubeHoldIntent(), white: createCubeHoldIntent(),
black: createCubeHoldIntent(), black: createCubeHoldIntent(),
}; };
let cubeHoldAnimationFrameId = null;
const openModal = () => toggleModal(refs.helpModal, true); const openModal = () => toggleModal(refs.helpModal, true);
const closeModal = () => toggleModal(refs.helpModal, false); const closeModal = () => toggleModal(refs.helpModal, false);
@@ -693,6 +694,7 @@ function initCubePage() {
clearCubeHoldTimeout(holdIntent); clearCubeHoldTimeout(holdIntent);
holdIntent.armed = true; holdIntent.armed = true;
holdIntent.ready = false; holdIntent.ready = false;
holdIntent.startedAt = Date.now();
holdIntent.pointerId = event.pointerId; holdIntent.pointerId = event.pointerId;
holdIntent.timeoutId = window.setTimeout(() => { holdIntent.timeoutId = window.setTimeout(() => {
holdIntent.timeoutId = null; holdIntent.timeoutId = null;
@@ -711,6 +713,7 @@ function initCubePage() {
} }
render(); render();
ensureCubeHoldAnimation();
} }
function handleCubePressEnd(color, button, event) { function handleCubePressEnd(color, button, event) {
@@ -771,7 +774,44 @@ function initCubePage() {
clearCubeHoldTimeout(holdIntent); clearCubeHoldTimeout(holdIntent);
holdIntent.armed = false; holdIntent.armed = false;
holdIntent.ready = false; holdIntent.ready = false;
holdIntent.startedAt = 0;
holdIntent.pointerId = null; holdIntent.pointerId = null;
stopCubeHoldAnimationIfIdle();
}
function ensureCubeHoldAnimation() {
if (cubeHoldAnimationFrameId !== null) {
return;
}
const tick = () => {
cubeHoldAnimationFrameId = null;
if (!isCubeHoldAnimating()) {
render();
return;
}
render();
cubeHoldAnimationFrameId = window.requestAnimationFrame(tick);
};
cubeHoldAnimationFrameId = window.requestAnimationFrame(tick);
}
function stopCubeHoldAnimationIfIdle() {
if (cubeHoldAnimationFrameId === null || isCubeHoldAnimating()) {
return;
}
window.cancelAnimationFrame(cubeHoldAnimationFrameId);
cubeHoldAnimationFrameId = null;
}
function isCubeHoldAnimating() {
return Object.entries(cubeHoldState).some(([color, holdIntent]) => {
const playerState = match.cube.playerState[color];
return holdIntent.armed && !holdIntent.ready && !playerState.running && match.cube.times[color] === null;
});
} }
function clearCubeHoldTimeout(holdIntent) { function clearCubeHoldTimeout(holdIntent) {
@@ -793,12 +833,16 @@ function initCubePage() {
const time = match.cube.times[color]; const time = match.cube.times[color];
const holdArmed = holdIntent.armed && !playerState.running && time === null && match.phase === "cube" && !match.result; const holdArmed = holdIntent.armed && !playerState.running && time === null && match.phase === "cube" && !match.result;
const holdReady = holdIntent.ready && holdArmed; const holdReady = holdIntent.ready && holdArmed;
const holdProgress = holdArmed
? Math.min((Date.now() - holdIntent.startedAt) / CUBE_START_HOLD_MS, 1)
: 0;
name.textContent = playerName(match, color); name.textContent = playerName(match, color);
result.textContent = formatCubePlayerTime(match, color); result.textContent = formatCubePlayerTime(match, color);
cap.textContent = renderCubeCap(match, time); cap.textContent = renderCubeCap(match, time);
button.classList.toggle("cube-hold-arming", holdArmed && !holdReady); button.classList.toggle("cube-hold-arming", holdArmed && !holdReady);
button.classList.toggle("cube-hold-ready", holdReady); button.classList.toggle("cube-hold-ready", holdReady);
button.style.setProperty("--cube-hold-progress", `${holdProgress}`);
if (match.result) { if (match.result) {
button.textContent = resultText(match); button.textContent = resultText(match);
@@ -836,15 +880,15 @@ function initCubePage() {
} }
if (holdArmed) { if (holdArmed) {
button.textContent = "Maintenez..."; button.textContent = "Maintenez 2 s...";
button.disabled = false; button.disabled = false;
hint.textContent = "Gardez le doigt pose un court instant pour armer le depart."; hint.textContent = "Gardez le doigt pose 2 secondes, jusqu'a la fin de la barre.";
return; return;
} }
button.textContent = "Maintenir pour demarrer"; button.textContent = "Maintenir 2 s pour demarrer";
button.disabled = false; button.disabled = false;
hint.textContent = "Maintenez la grande zone, puis relachez pour lancer votre chrono."; hint.textContent = "Maintenez la grande zone 2 secondes, puis relachez pour lancer votre chrono.";
} }
function render() { function render() {
@@ -1787,6 +1831,7 @@ function createCubeHoldIntent() {
return { return {
armed: false, armed: false,
ready: false, ready: false,
startedAt: 0,
pointerId: null, pointerId: null,
timeoutId: null, timeoutId: null,
}; };

View File

@@ -711,9 +711,38 @@ textarea:focus {
} }
body[data-page="cube"] .zone-button { body[data-page="cube"] .zone-button {
position: relative;
overflow: hidden;
touch-action: none; touch-action: none;
-webkit-user-select: none; -webkit-user-select: none;
user-select: none; user-select: none;
--cube-hold-progress: 0;
}
body[data-page="cube"] .zone-button::before,
body[data-page="cube"] .zone-button::after {
content: "";
position: absolute;
left: 1rem;
right: 1rem;
bottom: 0.9rem;
height: 0.38rem;
border-radius: 999px;
pointer-events: none;
}
body[data-page="cube"] .zone-button::before {
background: rgba(255, 255, 255, 0.12);
opacity: 0;
transition: opacity 120ms ease;
}
body[data-page="cube"] .zone-button::after {
background: linear-gradient(90deg, rgba(255, 255, 255, 0.4), currentColor);
transform: scaleX(var(--cube-hold-progress));
transform-origin: left center;
opacity: 0;
transition: opacity 120ms ease;
} }
body[data-page="cube"] .zone-button.cube-hold-arming { body[data-page="cube"] .zone-button.cube-hold-arming {
@@ -722,6 +751,13 @@ body[data-page="cube"] .zone-button.cube-hold-arming {
box-shadow: inset 0 0 0 1px currentColor; box-shadow: inset 0 0 0 1px currentColor;
} }
body[data-page="cube"] .zone-button.cube-hold-arming::before,
body[data-page="cube"] .zone-button.cube-hold-arming::after,
body[data-page="cube"] .zone-button.cube-hold-ready::before,
body[data-page="cube"] .zone-button.cube-hold-ready::after {
opacity: 1;
}
body[data-page="cube"] .zone-button.cube-hold-ready { body[data-page="cube"] .zone-button.cube-hold-ready {
transform: none; transform: none;
filter: brightness(1.08); filter: brightness(1.08);
@@ -730,6 +766,10 @@ body[data-page="cube"] .zone-button.cube-hold-ready {
0 0 24px rgba(255, 255, 255, 0.08); 0 0 24px rgba(255, 255, 255, 0.08);
} }
body[data-page="cube"] .zone-button.cube-hold-ready::after {
transform: scaleX(1);
}
.zone-button:hover { .zone-button:hover {
transform: translateY(-2px); transform: translateY(-2px);
filter: brightness(1.04); filter: brightness(1.04);