PageSourceSearch

https://forthehearts.net/fth/glyphs/GlyphAnimator.js

js forthehearts.net collected 2026-10-02 07:54:15 UTC 3,639 bytes, 100 lines download raw bytes

1// =============================================================================
2// glyphs/GlyphAnimator.ts
3// =============================================================================
4export class GlyphAnimator {
5    displayElement;
6    onGlyphChange;
7    glyphs = [];
8    sequentialGlyphs = [];
9    currentIndex = 0;
10    isAnimating = false;
11    animationFrameId = null;
12    isRandomOrder = false;
13    // Animation timing
14    lastFrameTime = 0;
15    interval = 100;
16    constructor(options) {
17        this.displayElement = options.displayElement ?? document.createElement('div');
18        this.onGlyphChange = options.onGlyphChange;
19    }
20    async setGlyphsFromFont(font) {
21        const chars = [];
22        for (let i = 0; i < font.glyphs.length; i++) {
23            const glyph = font.glyphs.get(i);
24            if (glyph.name === '.notdef' || glyph.unicode === undefined)
25                continue;
26            chars.push(String.fromCodePoint(glyph.unicode));
27        }
28        this.glyphs = chars;
29        this.sequentialGlyphs = [...chars];
30        this.currentIndex = 0;
31        this.isRandomOrder = false;
32    }
33    start(interval) {
34        if (this.glyphs.length === 0) {
35            console.error('No glyphs available for animation');
36            return;
37        }
38        this.interval = interval;
39        this.isAnimating = true;
40        this.lastFrameTime = performance.now();
41        this.animate();
42    }
43    stop() {
44        this.isAnimating = false;
45        if (this.animationFrameId !== null) {
46            cancelAnimationFrame(this.animationFrameId);
47            this.animationFrameId = null;
48        }
49    }
50    animate(currentTime = performance.now()) {
51        if (!this.isAnimating)
52            return;
53        const elapsed = currentTime - this.lastFrameTime;
54        if (elapsed >= this.interval) {
55            const currentChar = this.glyphs[this.currentIndex];
56            if (currentChar !== undefined) {
57                this.displayElement.textContent = currentChar;
58                this.onGlyphChange?.(currentChar);
59            }
60            this.currentIndex = (this.currentIndex + 1) % this.glyphs.length;
61            this.lastFrameTime = currentTime - (elapsed % this.interval);
62        }
63        this.animationFrameId = requestAnimationFrame((t) => this.animate(t));
64    }
65    moveForward(steps = 1) {
66        this.currentIndex = (this.currentIndex + steps) % this.glyphs.length;
67        const currentChar = this.glyphs[this.currentIndex];
68        if (currentChar !== undefined) {
69            this.displayElement.textContent = currentChar;
70            this.onGlyphChange?.(currentChar);
71        }
72    }
73    moveBack(steps = 1) {
74        this.currentIndex = (this.currentIndex - steps + this.glyphs.length) % this.glyphs.length;
75        const currentChar = this.glyphs[this.currentIndex];
76        if (currentChar !== undefined) {
77            this.displayElement.textContent = currentChar;
78            this.onGlyphChange?.(currentChar);
79        }
80    }
81    toggleOrder() {
82        if (this.isRandomOrder) {
83            this.glyphs = [...this.sequentialGlyphs];
84        }
85        else {
86            this.glyphs = this.shuffleArray([...this.glyphs]);
87        }
88        this.isRandomOrder = !this.isRandomOrder;
89        this.currentIndex = 0;
90    }
91    shuffleArray(array) {
92        for (let i = array.length - 1; i > 0; i--) {
93            const j = Math.floor(Math.random() * (i + 1));
94            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
95            [array[i], array[j]] = [array[j], array[i]];
96        }
97        return array;
98    }
99}
100//# sourceMappingURL=GlyphAnimator.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.