Ajoute un demarrage par appui long du chrono cube
This commit is contained in:
185
app.js
185
app.js
@@ -7,6 +7,7 @@ const DEFAULT_BLOCK_DURATION_MS = 180000;
|
||||
const DEFAULT_MOVE_LIMIT_MS = 20000;
|
||||
const TIME_MODE_INITIAL_CLOCK_MS = 600000;
|
||||
const CUBE_TIME_CAP_MS = 120000;
|
||||
const CUBE_START_HOLD_MS = 300;
|
||||
|
||||
const PRESETS = {
|
||||
fast: {
|
||||
@@ -579,6 +580,10 @@ function initCubePage() {
|
||||
replayCubeButton: document.querySelector("#replayCubeButton"),
|
||||
resetButton: document.querySelector("#cubeResetButton"),
|
||||
};
|
||||
const cubeHoldState = {
|
||||
white: createCubeHoldIntent(),
|
||||
black: createCubeHoldIntent(),
|
||||
};
|
||||
|
||||
const openModal = () => toggleModal(refs.helpModal, true);
|
||||
const closeModal = () => toggleModal(refs.helpModal, false);
|
||||
@@ -592,17 +597,8 @@ function initCubePage() {
|
||||
}
|
||||
});
|
||||
|
||||
refs.whiteButton?.addEventListener("click", () => {
|
||||
handleCubeTap("white");
|
||||
dirty = true;
|
||||
render();
|
||||
});
|
||||
|
||||
refs.blackButton?.addEventListener("click", () => {
|
||||
handleCubeTap("black");
|
||||
dirty = true;
|
||||
render();
|
||||
});
|
||||
bindCubeButton("white", refs.whiteButton);
|
||||
bindCubeButton("black", refs.blackButton);
|
||||
|
||||
refs.primaryButton?.addEventListener("click", () => {
|
||||
if (match.result) {
|
||||
@@ -641,7 +637,46 @@ function initCubePage() {
|
||||
replaceTo(SETUP_PAGE);
|
||||
});
|
||||
|
||||
function handleCubeTap(color) {
|
||||
function bindCubeButton(color, button) {
|
||||
if (!button) {
|
||||
return;
|
||||
}
|
||||
|
||||
button.addEventListener("pointerdown", (event) => {
|
||||
handleCubePressStart(color, button, event);
|
||||
});
|
||||
|
||||
button.addEventListener("pointerup", (event) => {
|
||||
handleCubePressEnd(color, button, event);
|
||||
});
|
||||
|
||||
button.addEventListener("pointercancel", () => {
|
||||
cancelCubeHold(color);
|
||||
render();
|
||||
});
|
||||
|
||||
button.addEventListener("lostpointercapture", () => {
|
||||
cancelCubeHold(color);
|
||||
render();
|
||||
});
|
||||
|
||||
button.addEventListener("contextmenu", (event) => {
|
||||
event.preventDefault();
|
||||
});
|
||||
|
||||
button.addEventListener("click", (event) => {
|
||||
if (event.detail !== 0) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
handleCubeKeyboardAction(color);
|
||||
dirty = true;
|
||||
render();
|
||||
});
|
||||
}
|
||||
|
||||
function handleCubePressStart(color, button, event) {
|
||||
if (match.result || match.phase !== "cube") {
|
||||
return;
|
||||
}
|
||||
@@ -650,6 +685,79 @@ function initCubePage() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (match.cube.playerState[color].running) {
|
||||
return;
|
||||
}
|
||||
|
||||
const holdIntent = cubeHoldState[color];
|
||||
clearCubeHoldTimeout(holdIntent);
|
||||
holdIntent.armed = true;
|
||||
holdIntent.ready = false;
|
||||
holdIntent.pointerId = event.pointerId;
|
||||
holdIntent.timeoutId = window.setTimeout(() => {
|
||||
holdIntent.timeoutId = null;
|
||||
if (!holdIntent.armed) {
|
||||
return;
|
||||
}
|
||||
|
||||
holdIntent.ready = true;
|
||||
render();
|
||||
}, CUBE_START_HOLD_MS);
|
||||
|
||||
try {
|
||||
button.setPointerCapture(event.pointerId);
|
||||
} catch {
|
||||
// Ignore browsers that do not support pointer capture on buttons.
|
||||
}
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
function handleCubePressEnd(color, button, event) {
|
||||
if (match.result || match.phase !== "cube") {
|
||||
cancelCubeHold(color);
|
||||
render();
|
||||
return;
|
||||
}
|
||||
|
||||
if (match.cube.times[color] !== null) {
|
||||
cancelCubeHold(color);
|
||||
render();
|
||||
return;
|
||||
}
|
||||
|
||||
if (match.cube.playerState[color].running) {
|
||||
captureCubeTime(match, color);
|
||||
dirty = true;
|
||||
render();
|
||||
return;
|
||||
}
|
||||
|
||||
const holdIntent = cubeHoldState[color];
|
||||
const wasReady = holdIntent.pointerId === event.pointerId && holdIntent.ready;
|
||||
cancelCubeHold(color);
|
||||
|
||||
try {
|
||||
button.releasePointerCapture(event.pointerId);
|
||||
} catch {
|
||||
// Ignore browsers that already released the capture.
|
||||
}
|
||||
|
||||
if (!wasReady) {
|
||||
render();
|
||||
return;
|
||||
}
|
||||
|
||||
startCubeTimer(match, color);
|
||||
dirty = true;
|
||||
render();
|
||||
}
|
||||
|
||||
function handleCubeKeyboardAction(color) {
|
||||
if (match.result || match.phase !== "cube" || match.cube.times[color] !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (match.cube.playerState[color].running) {
|
||||
captureCubeTime(match, color);
|
||||
return;
|
||||
@@ -658,6 +766,21 @@ function initCubePage() {
|
||||
startCubeTimer(match, color);
|
||||
}
|
||||
|
||||
function cancelCubeHold(color) {
|
||||
const holdIntent = cubeHoldState[color];
|
||||
clearCubeHoldTimeout(holdIntent);
|
||||
holdIntent.armed = false;
|
||||
holdIntent.ready = false;
|
||||
holdIntent.pointerId = null;
|
||||
}
|
||||
|
||||
function clearCubeHoldTimeout(holdIntent) {
|
||||
if (holdIntent.timeoutId !== null) {
|
||||
window.clearTimeout(holdIntent.timeoutId);
|
||||
holdIntent.timeoutId = null;
|
||||
}
|
||||
}
|
||||
|
||||
function renderCubeZone(color) {
|
||||
const isWhite = color === "white";
|
||||
const button = isWhite ? refs.whiteButton : refs.blackButton;
|
||||
@@ -666,11 +789,16 @@ function initCubePage() {
|
||||
const cap = isWhite ? refs.whiteCap : refs.blackCap;
|
||||
const hint = isWhite ? refs.whiteHint : refs.blackHint;
|
||||
const playerState = match.cube.playerState[color];
|
||||
const holdIntent = cubeHoldState[color];
|
||||
const time = match.cube.times[color];
|
||||
const holdArmed = holdIntent.armed && !playerState.running && time === null && match.phase === "cube" && !match.result;
|
||||
const holdReady = holdIntent.ready && holdArmed;
|
||||
|
||||
name.textContent = playerName(match, color);
|
||||
result.textContent = formatCubePlayerTime(match, color);
|
||||
cap.textContent = renderCubeCap(match, time);
|
||||
button.classList.toggle("cube-hold-arming", holdArmed && !holdReady);
|
||||
button.classList.toggle("cube-hold-ready", holdReady);
|
||||
|
||||
if (match.result) {
|
||||
button.textContent = resultText(match);
|
||||
@@ -700,9 +828,23 @@ function initCubePage() {
|
||||
return;
|
||||
}
|
||||
|
||||
button.textContent = "Demarrer mon chrono";
|
||||
if (holdReady) {
|
||||
button.textContent = "Relachez pour demarrer";
|
||||
button.disabled = false;
|
||||
hint.textContent = "Le chrono partira des que vous levez le doigt.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (holdArmed) {
|
||||
button.textContent = "Maintenez...";
|
||||
button.disabled = false;
|
||||
hint.textContent = "Gardez le doigt pose un court instant pour armer le depart.";
|
||||
return;
|
||||
}
|
||||
|
||||
button.textContent = "Maintenir pour demarrer";
|
||||
button.disabled = false;
|
||||
hint.textContent = "Chaque joueur lance son propre chrono quand il commence vraiment.";
|
||||
hint.textContent = "Maintenez la grande zone, puis relachez pour lancer votre chrono.";
|
||||
}
|
||||
|
||||
function render() {
|
||||
@@ -745,7 +887,7 @@ function initCubePage() {
|
||||
refs.centerValue.textContent = "Chronos lances";
|
||||
refs.spineLabel.textContent = "Arrets";
|
||||
refs.spineHeadline.textContent = "Chaque joueur se chronometre";
|
||||
refs.spineText.textContent = "Chaque joueur demarre quand il veut, puis retape sa zone une fois le cube termine.";
|
||||
refs.spineText.textContent = "Chaque joueur demarre en relachant sa zone, puis retape sa zone une fois le cube termine.";
|
||||
refs.primaryButton.textContent = "Attendre les deux temps";
|
||||
refs.helpStatus.textContent = refs.spineText.textContent;
|
||||
} else if (match.cube.times.white !== null || match.cube.times.black !== null) {
|
||||
@@ -753,7 +895,7 @@ function initCubePage() {
|
||||
refs.centerValue.textContent = "Un temps saisi";
|
||||
refs.spineLabel.textContent = "Suite";
|
||||
refs.spineHeadline.textContent = "Attendre l'autre joueur";
|
||||
refs.spineText.textContent = "Le deuxieme joueur peut encore demarrer puis arreter son propre chrono quand il le souhaite.";
|
||||
refs.spineText.textContent = "Le deuxieme joueur peut encore maintenir puis relacher sa zone pour demarrer son propre chrono.";
|
||||
refs.primaryButton.textContent = "Attendre le deuxieme temps";
|
||||
refs.helpStatus.textContent = refs.spineText.textContent;
|
||||
} else {
|
||||
@@ -761,7 +903,7 @@ function initCubePage() {
|
||||
refs.centerValue.textContent = "Pret";
|
||||
refs.spineLabel.textContent = "Depart libre";
|
||||
refs.spineHeadline.textContent = "Chaque joueur lance son chrono";
|
||||
refs.spineText.textContent = "Au debut de sa resolution, chaque joueur tape sur sa grande zone pour demarrer son propre chrono.";
|
||||
refs.spineText.textContent = "Au debut de sa resolution, chaque joueur maintient sa grande zone puis la relache pour demarrer son propre chrono.";
|
||||
refs.primaryButton.textContent = "En attente des joueurs";
|
||||
refs.helpStatus.textContent = refs.spineText.textContent;
|
||||
}
|
||||
@@ -1641,6 +1783,15 @@ function createCubePlayerState() {
|
||||
};
|
||||
}
|
||||
|
||||
function createCubeHoldIntent() {
|
||||
return {
|
||||
armed: false,
|
||||
ready: false,
|
||||
pointerId: null,
|
||||
timeoutId: null,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeCubePlayerState(playerState) {
|
||||
return {
|
||||
running: Boolean(playerState?.running),
|
||||
|
||||
20
styles.css
20
styles.css
@@ -710,6 +710,26 @@ textarea:focus {
|
||||
text-shadow: 0 0 18px rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
body[data-page="cube"] .zone-button {
|
||||
touch-action: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body[data-page="cube"] .zone-button.cube-hold-arming {
|
||||
transform: none;
|
||||
filter: brightness(0.98);
|
||||
box-shadow: inset 0 0 0 1px currentColor;
|
||||
}
|
||||
|
||||
body[data-page="cube"] .zone-button.cube-hold-ready {
|
||||
transform: none;
|
||||
filter: brightness(1.08);
|
||||
box-shadow:
|
||||
inset 0 0 0 2px currentColor,
|
||||
0 0 24px rgba(255, 255, 255, 0.08);
|
||||
}
|
||||
|
||||
.zone-button:hover {
|
||||
transform: translateY(-2px);
|
||||
filter: brightness(1.04);
|
||||
|
||||
Reference in New Issue
Block a user