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.