|
96 | 96 | }); |
97 | 97 | } |
98 | 98 |
|
99 | | - // Maps a mouse event to a key by dividing the pre into 19 character columns. |
| 99 | + // Maps a clientX coordinate to a key by dividing the pre into 19 character columns. |
100 | 100 | // h: cols 0–5, d: cols 6–11, i: cols 12–17, col 18 is the trailing '|' (ignored). |
101 | | - function keyFromMouse(e: MouseEvent): string | null { |
| 101 | + function keyFromPoint(clientX: number): string | null { |
102 | 102 | const rect = logoPre.getBoundingClientRect(); |
103 | | - const col = Math.floor((e.clientX - rect.left) / (rect.width / 19)); |
| 103 | + const col = Math.floor((clientX - rect.left) / (rect.width / 19)); |
104 | 104 | return col < 6 ? "h" : col < 12 ? "d" : col < 18 ? "i" : null; |
105 | 105 | } |
106 | 106 |
|
107 | | - function keyFromTouch(touch: Touch): string | null { |
108 | | - const rect = logoPre.getBoundingClientRect(); |
109 | | - const col = Math.floor((touch.clientX - rect.left) / (rect.width / 19)); |
110 | | - return col < 6 ? "h" : col < 12 ? "d" : col < 18 ? "i" : null; |
| 107 | + function releaseAll() { |
| 108 | + if (pressedKeys.size === 0) return; |
| 109 | + pressedKeys.clear(); |
| 110 | + render(); |
| 111 | + } |
| 112 | + |
| 113 | + function pressTouches(e: TouchEvent) { |
| 114 | + e.preventDefault(); |
| 115 | + pressedKeys.clear(); |
| 116 | + for (const touch of e.touches) { |
| 117 | + const key = keyFromPoint(touch.clientX); |
| 118 | + if (key) pressedKeys.add(key); |
| 119 | + } |
| 120 | + render(); |
111 | 121 | } |
112 | 122 |
|
113 | 123 | // Mouse |
114 | 124 | logoPre.addEventListener("mousemove", (e) => { |
115 | | - const key = keyFromMouse(e); |
| 125 | + const key = keyFromPoint(e.clientX); |
116 | 126 | if (key !== hoveredKey) { |
117 | 127 | hoveredKey = key; |
118 | 128 | applyClasses(); |
|
129 | 139 | applyClasses(); |
130 | 140 | }); |
131 | 141 | logoPre.addEventListener("mousedown", (e) => { |
132 | | - const key = keyFromMouse(e); |
| 142 | + const key = keyFromPoint(e.clientX); |
133 | 143 | if (key) { |
134 | 144 | pressedKeys.add(key); |
135 | 145 | render(); |
136 | 146 | } |
137 | 147 | }); |
138 | | - document.addEventListener("mouseup", () => { |
139 | | - if (pressedKeys.size === 0) return; |
140 | | - pressedKeys.clear(); |
141 | | - render(); |
142 | | - }); |
| 148 | + document.addEventListener("mouseup", releaseAll); |
143 | 149 |
|
144 | 150 | // Touch |
145 | | - logoPre.addEventListener( |
146 | | - "touchstart", |
147 | | - (e) => { |
148 | | - e.preventDefault(); |
149 | | - pressedKeys.clear(); |
150 | | - for (const touch of e.touches) { |
151 | | - const key = keyFromTouch(touch); |
152 | | - if (key) pressedKeys.add(key); |
153 | | - } |
154 | | - render(); |
155 | | - }, |
156 | | - { passive: false }, |
157 | | - ); |
158 | | - logoPre.addEventListener( |
159 | | - "touchmove", |
160 | | - (e) => { |
161 | | - e.preventDefault(); |
162 | | - pressedKeys.clear(); |
163 | | - for (const touch of e.touches) { |
164 | | - const key = keyFromTouch(touch); |
165 | | - if (key) pressedKeys.add(key); |
166 | | - } |
167 | | - render(); |
168 | | - }, |
169 | | - { passive: false }, |
170 | | - ); |
171 | | - document.addEventListener("touchend", () => { |
172 | | - if (pressedKeys.size === 0) return; |
173 | | - pressedKeys.clear(); |
174 | | - render(); |
175 | | - }); |
176 | | - document.addEventListener("touchcancel", () => { |
177 | | - pressedKeys.clear(); |
178 | | - render(); |
179 | | - }); |
| 151 | + logoPre.addEventListener("touchstart", pressTouches, { passive: false }); |
| 152 | + logoPre.addEventListener("touchmove", pressTouches, { passive: false }); |
| 153 | + document.addEventListener("touchend", releaseAll); |
| 154 | + document.addEventListener("touchcancel", releaseAll); |
180 | 155 |
|
181 | 156 | // Keyboard — skip when focus is in a text input to avoid interfering with typing |
182 | 157 | const KEYS = new Set(["h", "d", "i"]); |
|
211 | 186 | .hero-logo-letter { |
212 | 187 | color: var(--green); |
213 | 188 | transition: all 150ms linear; |
214 | | - &.hovered { |
215 | | - color: var(--mauve); |
216 | | - } |
| 189 | + &.hovered, |
217 | 190 | &.pressed { |
218 | 191 | color: var(--mauve); |
219 | 192 | } |
|
0 commit comments