| 167 | }; |
| 168 | |
| 169 | const doAnimate = fnName => { |
| 170 | animationRef.current = requestAnimationFrame(() => doAnimate(fnName)); |
| 171 | const timeNow = performance.now(); |
| 172 | const timePassed = timeNow - timePreviousRef.current; |
| 173 | const timeInterval = 1000 / 60; |
| 174 | |
| 175 | if (timePassed < timeInterval) return; |
| 176 | timePreviousRef.current = timeNow - (timePassed % timeInterval); |
| 177 | |
| 178 | const ctx = canvasRef.current?.getContext('2d'); |
| 179 | if (!ctx || !canvasRef.current) return; |
| 180 | |
| 181 | ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height); |
| 182 | |
| 183 | let allIdle = true; |
| 184 | for (let i = 0; i < pixelsRef.current.length; i++) { |
| 185 | const pixel = pixelsRef.current[i]; |
| 186 | pixel[fnName](); |
| 187 | if (!pixel.isIdle) { |
| 188 | allIdle = false; |
| 189 | } |
| 190 | } |
| 191 | if (allIdle) { |
| 192 | cancelAnimationFrame(animationRef.current); |
| 193 | } |
| 194 | }; |
| 195 | |
| 196 | const handleAnimation = name => { |
| 197 | cancelAnimationFrame(animationRef.current); |