Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions frontend/src/html/pages/settings.html
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,7 @@
<button data-config-value="hide">hide</button>
<button data-config-value="fade">fade</button>
<button data-config-value="dots">dots</button>
<button data-config-value="tumble">tumble</button>
</div>
</div>
<div class="section" data-config-name="tapeMode">
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/styles/animations.scss
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,17 @@
color: transparent;
}
}

@keyframes typedEffectTumble {
0% {
transform: translate(0, 0) rotate(0deg);
opacity: 1;
}
25% {
opacity: 1;
}
100% {
transform: translate(var(--fall-x), 100vh) rotate(var(--fall-rotation));
opacity: 0;
}
}
13 changes: 13 additions & 0 deletions frontend/src/styles/test.scss
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,12 @@
}
}

&.typed-effect-tumble {
.word.typed:not(.error) {
opacity: 0;
}
}

&.typed-effect-dots {
/* transform already typed letters into appropriately colored dots */

Expand Down Expand Up @@ -602,6 +608,13 @@
}
}

.tumble-clone {
animation: typedEffectTumble 1s cubic-bezier(0.5, 0, 1, 1) forwards;
display: inline-block;
pointer-events: none;
z-index: 1000;
}

.word {
position: relative;
font-size: 1em;
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/ts/test/test-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ import * as ThemeController from "../controllers/theme-controller";
import * as ModesNotice from "../elements/modes-notice";
import * as Last10Average from "../elements/last-10-average";
import * as MemoryFunboxTimer from "./funbox/memory-funbox-timer";
import * as TypedEffects from "./typed-effects";
import {
ElementsWithUtils,
ElementWithUtils,
Expand Down Expand Up @@ -147,6 +148,7 @@ export function updateActiveElement(
if (previousActiveWord !== null) {
if (direction === "forward") {
previousActiveWord.addClass("typed");
TypedEffects.onWordTyped(previousActiveWord);
Ligatures.set(previousActiveWord, true);
} else if (direction === "back") {
//
Expand All @@ -164,6 +166,7 @@ export function updateActiveElement(
newActiveWord.addClass("active");
newActiveWord.removeClass("error");
newActiveWord.removeClass("typed");
newActiveWord.setStyle({ opacity: "" });
Ligatures.set(newActiveWord, false);

activeWordTop = newActiveWord.getOffsetTop();
Expand Down Expand Up @@ -495,6 +498,7 @@ function updateWordWrapperClasses(): void {
}

function showWords(): void {
TypedEffects.clear();
wordsEl.setHtml("");

if (Config.mode === "zen") {
Expand Down Expand Up @@ -1937,6 +1941,7 @@ export function onTestRestart(source: "testPage" | "resultPage"): void {
}

export function onTestFinish(): void {
TypedEffects.clear();
Caret.hide();
LiveSpeed.hide();
LiveAcc.hide();
Expand Down
53 changes: 53 additions & 0 deletions frontend/src/ts/test/typed-effects.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { Config } from "../config/store";
import { ElementWithUtils, qsa, qsr } from "../utils/dom";

const wordsEl = qsr(".pageTest #words");

export function onWordTyped(word: ElementWithUtils): void {
switch (Config.typedEffect) {
case "tumble":
triggerTumble(word);
return;
default:
return;
}
}

export function clear(): void {
qsa(".tumble-clone").remove();
wordsEl.qsa(".word").setStyle({ opacity: "" });
}

function triggerTumble(word: ElementWithUtils): void {
if (word.hasClass("error")) return;

const rect = word.native.getBoundingClientRect();
if (rect.width === 0 && rect.height === 0) return;

const computedStyle = window.getComputedStyle(word.native);
const clone = word.native.cloneNode(true) as HTMLElement;
const randomRotation = (Math.random() - 0.5) * 45;
const randomX = (Math.random() - 0.5) * 100;

clone.classList.add("tumble-clone");
clone.style.position = "fixed";
clone.style.top = `${rect.top}px`;
clone.style.left = `${rect.left}px`;
clone.style.width = `${rect.width}px`;
clone.style.height = `${rect.height}px`;
clone.style.fontSize = computedStyle.fontSize;
clone.style.fontFamily = computedStyle.fontFamily;
clone.style.color = computedStyle.color;
clone.style.margin = "0";
clone.style.pointerEvents = "none";
clone.style.zIndex = "1000";
clone.style.setProperty("--fall-rotation", `${randomRotation}deg`);
clone.style.setProperty("--fall-x", `${randomX}px`);

document.body.appendChild(clone);
word.setStyle({ opacity: "0" });

clone.addEventListener("animationend", () => {
clone.remove();
});
}
8 changes: 7 additions & 1 deletion packages/schemas/src/configs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,13 @@ export const HighlightModeSchema = z.enum([
]);
export type HighlightMode = z.infer<typeof HighlightModeSchema>;

export const TypedEffectSchema = z.enum(["keep", "hide", "fade", "dots"]);
export const TypedEffectSchema = z.enum([
"keep",
"hide",
"fade",
"dots",
"tumble",
]);
export type TypedEffect = z.infer<typeof TypedEffectSchema>;

export const TapeModeSchema = z.enum(["off", "letter", "word"]);
Expand Down
Loading