From 80d8c22c52a847cc96c3aa163239b1899cf5774d Mon Sep 17 00:00:00 2001 From: Miodec Date: Thu, 8 Jan 2026 17:22:16 +0100 Subject: [PATCH] animation wip --- frontend/src/ts/signals/live-states.ts | 4 --- frontend/src/ts/test/live-counter.tsx | 37 +++++++++++++++++++++++-- frontend/src/ts/test/live-states.tsx | 38 +++++++++++++++----------- frontend/src/ts/test/test-timer.ts | 4 +-- frontend/src/ts/test/test-ui.ts | 24 ++++++++++++++-- 5 files changed, 80 insertions(+), 27 deletions(-) delete mode 100644 frontend/src/ts/signals/live-states.ts diff --git a/frontend/src/ts/signals/live-states.ts b/frontend/src/ts/signals/live-states.ts deleted file mode 100644 index 7440f8bbd..000000000 --- a/frontend/src/ts/signals/live-states.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { createSignal } from "solid-js"; -export const [getWpm, setWpm] = createSignal("0"); -export const [getAcc, setAcc] = createSignal("100%"); -export const [getBurst, setBurst] = createSignal("0"); diff --git a/frontend/src/ts/test/live-counter.tsx b/frontend/src/ts/test/live-counter.tsx index 13c470041..8fc45dc10 100644 --- a/frontend/src/ts/test/live-counter.tsx +++ b/frontend/src/ts/test/live-counter.tsx @@ -1,11 +1,44 @@ -import { Accessor, JSXElement } from "solid-js"; +import { Accessor, createEffect, JSXElement } from "solid-js"; +import { animate } from "animejs"; +import { isFocused } from "./focus"; export function LiveCounter(props: { value: Accessor; + visible: Accessor<{ + value: boolean; + withAnimation: boolean; + }>; class?: string; }): JSXElement { + let divRef: HTMLDivElement | undefined; + + createEffect(() => { + if (divRef === undefined) return; + const visibleState = props.visible(); + const focusState = isFocused(); + if (visibleState.value && focusState) { + if (visibleState.withAnimation) { + animate(divRef, { + opacity: [0, 1], + duration: 125, + }); + } else { + divRef.style.opacity = "1"; + } + } else { + if (visibleState.withAnimation) { + animate(divRef, { + opacity: [1, 0], + duration: 125, + }); + } else { + divRef.style.opacity = "0"; + } + } + }); + return ( -
+
{props.value()}
); diff --git a/frontend/src/ts/test/live-states.tsx b/frontend/src/ts/test/live-states.tsx index c5cbad303..22b4408f0 100644 --- a/frontend/src/ts/test/live-states.tsx +++ b/frontend/src/ts/test/live-states.tsx @@ -1,35 +1,41 @@ -import { createMemo } from "solid-js"; +import { createMemo, createSignal } from "solid-js"; import { qsr } from "../utils/dom"; import { LiveCounter } from "./live-counter"; import { render } from "solid-js/web"; -import { getAcc, getBurst, getWpm } from "../signals/live-states"; import { getLiveAccStyle, getLiveBurstStyle, getLiveSpeedStyle, } from "../signals/config"; -import { isActive } from "./test-state"; -import { isFocused } from "./focus"; -const isTestRunning = createMemo(() => isActive() && isFocused()); +const [getWpm, setLiveStatWpm] = createSignal("0"); +const [getAcc, setLiveStatAcc] = createSignal("100%"); +const [getBurst, setLiveStatBurst] = createSignal("0"); + +const [wpmVisible, setWpmVisible] = createSignal({ + value: false, + withAnimation: true, +}); + +export { setLiveStatWpm, setLiveStatAcc, setLiveStatBurst, setWpmVisible }; const liveWpmText = createMemo(() => - isTestRunning() && getLiveSpeedStyle() === "text" ? getWpm() : "", + getLiveSpeedStyle() === "text" ? getWpm() : "", ); const liveWpmMini = createMemo(() => - isTestRunning() && getLiveSpeedStyle() === "mini" ? getWpm() : "", + getLiveSpeedStyle() === "mini" ? getWpm() : "", ); const liveAccText = createMemo(() => - isTestRunning() && getLiveAccStyle() === "text" ? getAcc() : "", + getLiveAccStyle() === "text" ? getAcc() : "", ); const liveAccMini = createMemo(() => - isTestRunning() && getLiveAccStyle() === "mini" ? getAcc() : "", + getLiveAccStyle() === "mini" ? getAcc() : "", ); const liveBurstText = createMemo(() => - isTestRunning() && getLiveBurstStyle() === "text" ? getBurst() : "", + getLiveBurstStyle() === "text" ? getBurst() : "", ); const liveBurstMini = createMemo(() => - isTestRunning() && getLiveBurstStyle() === "mini" ? getBurst() : "", + getLiveBurstStyle() === "mini" ? getBurst() : "", ); export function mountLiveCounters(): void { @@ -37,9 +43,9 @@ export function mountLiveCounters(): void { render( () => (
- + {/* - + */}
), textWrapper.native, @@ -49,9 +55,9 @@ export function mountLiveCounters(): void { render( () => (
- - - + + {/* + */}
), miniWrapper.native, diff --git a/frontend/src/ts/test/test-timer.ts b/frontend/src/ts/test/test-timer.ts index 4c67aa4dd..b8bb36d49 100644 --- a/frontend/src/ts/test/test-timer.ts +++ b/frontend/src/ts/test/test-timer.ts @@ -20,8 +20,8 @@ import * as SoundController from "../controllers/sound-controller"; import { clearLowFpsMode, setLowFpsMode } from "../anim"; import { createTimer } from "animejs"; import { requestDebouncedAnimationFrame } from "../utils/debounced-animation-frame"; -import { setWpm } from "../signals/live-states"; import Format from "../utils/format"; +import { setLiveStatWpm } from "./live-states"; let lastLoop = 0; const newTimer = createTimer({ @@ -236,7 +236,7 @@ function timerStep(): void { // already using raf TimerProgress.update(); - setWpm( + setLiveStatWpm( Format.typingSpeed(Config.blindMode ? wpmAndRaw.raw : wpmAndRaw.wpm, { showDecimalPlaces: false, }), diff --git a/frontend/src/ts/test/test-ui.ts b/frontend/src/ts/test/test-ui.ts index 6b62a53cb..a95ba5157 100644 --- a/frontend/src/ts/test/test-ui.ts +++ b/frontend/src/ts/test/test-ui.ts @@ -55,7 +55,12 @@ import * as ModesNotice from "../elements/modes-notice"; import * as Last10Average from "../elements/last-10-average"; import * as MemoryFunboxTimer from "./funbox/memory-funbox-timer"; import { qsr } from "../utils/dom"; -import { setAcc, setBurst } from "../signals/live-states"; +import { + setLiveStatAcc, + setLiveStatBurst, + setLiveStatWpm, + setWpmVisible, +} from "./live-states"; export const updateHintsPositionDebounced = Misc.debounceUntilResolved( updateHintsPosition, @@ -1679,7 +1684,7 @@ function afterAnyTestInput( const acc: number = Numbers.roundTo2(TestStats.calculateAccuracy()); if (!isNaN(acc)) { - setAcc(Format.percentage(Config.blindMode ? 100 : acc)); + setLiveStatAcc(Format.percentage(Config.blindMode ? 100 : acc)); } if (Config.mode !== "time") { @@ -1784,7 +1789,9 @@ export async function afterTestWordChange( const lastBurst = TestInput.burstHistory[TestInput.burstHistory.length - 1]; if (Numbers.isSafeNumber(lastBurst)) { - setBurst(Format.typingSpeed(lastBurst, { showDecimalPlaces: false })); + setLiveStatBurst( + Format.typingSpeed(lastBurst, { showDecimalPlaces: false }), + ); } if (direction === "forward") { // @@ -1814,6 +1821,10 @@ export function onTestStart(): void { Monkey.show(); TimerProgress.show(); TimerProgress.update(); + setWpmVisible({ + value: true, + withAnimation: true, + }); } export function onTestRestart(source: "testPage" | "resultPage"): void { @@ -1822,6 +1833,13 @@ export function onTestRestart(source: "testPage" | "resultPage"): void { getInputElement().style.left = "0"; TestConfig.show(); Focus.set(false); + setWpmVisible({ + value: false, + withAnimation: false, + }); + setLiveStatWpm("0"); + setLiveStatBurst(""); + setLiveStatAcc(""); TimerProgress.instantHide(); TimerProgress.reset(); Monkey.instantHide();