diff --git a/app/react/components/dom/dialogue.jsx b/app/react/components/dom/dialogue.jsx index 83f25e7..d024f8c 100644 --- a/app/react/components/dom/dialogue.jsx +++ b/app/react/components/dom/dialogue.jsx @@ -1,7 +1,8 @@ -import {useEffect, useMemo, useRef, useState} from 'react'; +import {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {useDomScale} from '@/react/context/dom-scale.js'; import {useRadians} from '@/react/context/radians.js'; +import useAnimationFrame from '@/react/hooks/use-animation-frame.js'; import {RESOLUTION} from '@/util/constants.js'; import {render} from '@/util/dialogue.js'; @@ -56,20 +57,13 @@ export default function Dialogue({ clearTimeout(handle); } }, [caret, dialogue]); - useEffect(() => { - let handle; - function track() { - if (ref.current) { - const {height, width} = ref.current.getBoundingClientRect(); - setDimensions({h: height / domScale, w: width / domScale}); - } - handle = requestAnimationFrame(track); + const updateDimensions = useCallback(() => { + if (ref.current) { + const {height, width} = ref.current.getBoundingClientRect(); + setDimensions({h: height / domScale, w: width / domScale}); } - handle = requestAnimationFrame(track); - return () => { - cancelAnimationFrame(handle); - }; - }, [dialogue, domScale, ref]); + }, [domScale]); + useAnimationFrame(updateDimensions); const localRender = useMemo( () => render(dialogue.letters, styles.letter), [dialogue.letters], diff --git a/app/react/components/dom/dialogue.module.css b/app/react/components/dom/dialogue.module.css index 596e01c..ecfa5a0 100644 --- a/app/react/components/dom/dialogue.module.css +++ b/app/react/components/dom/dialogue.module.css @@ -17,6 +17,7 @@ ; top: 0; transform: translate(-50%, -50%); + transition: translate 100ms; user-select: none; max-width: 66%; } diff --git a/app/react/components/particle-worker.js b/app/react/components/particle-worker.js index 2dd8920..b7e7d33 100644 --- a/app/react/components/particle-worker.js +++ b/app/react/components/particle-worker.js @@ -17,9 +17,8 @@ addEventListener('message', (particle) => { .onEnd(() => {}); }); -let last = Date.now(); -function tick() { - const now = Date.now(); +let last = performance.now(); +function tick(now) { const elapsed = (now - last) / 1000; last = now; if (ecs.get(1)) { diff --git a/app/react/components/pixi/emitter.jsx b/app/react/components/pixi/emitter.jsx deleted file mode 100644 index b74a0f8..0000000 --- a/app/react/components/pixi/emitter.jsx +++ /dev/null @@ -1,52 +0,0 @@ -import {Container} from '@pixi/display'; -import {PixiComponent} from '@pixi/react'; -import * as particles from '@pixi/particle-emitter'; - -const EmitterInternal = PixiComponent('Emitter', { - $$emitter: undefined, - $$raf: undefined, - create() { - return new Container(); - }, - applyProps(container, oldProps, newProps) { - if (!this.$$emitter) { - const {onComplete, particle} = newProps; - this.$$emitter = new particles.Emitter(container, particle); - this.$$emitter._completeCallback = onComplete; - let last = Date.now(); - const render = () => { - this.$$raf = requestAnimationFrame(render); - const now = Date.now(); - this.$$emitter.update((now - last) / 1000); - last = now; - }; - this.$$emitter.emit = true; - render(); - } - }, - willUnmount() { - if (this.$$emitter) { - this.$$emitter.emit = false; - cancelAnimationFrame(this.$$raf); - } - } -}); - -export default function Emitter({entity}) { - const {Emitter} = entity; - const emitters = []; - for (const id in Emitter.emitting) { - const particle = Emitter.emitting[id]; - emitters.push( - { - delete Emitter.emitting[id]; - }} - particle={particle} - /> - ); - } - return <>{emitters}; -} - diff --git a/app/routes/_main-menu.play.$.$/route.jsx b/app/routes/_main-menu.play.$.$/route.jsx index 0457eb1..9fa1ad9 100644 --- a/app/routes/_main-menu.play.$.$/route.jsx +++ b/app/routes/_main-menu.play.$.$/route.jsx @@ -1,5 +1,5 @@ import {json} from "@remix-run/node"; -import {useEffect, useState} from 'react'; +import {useCallback, useEffect, useState} from 'react'; import {useOutletContext, useParams} from 'react-router-dom'; import Ui from '@/react/components/ui.jsx'; @@ -9,6 +9,7 @@ import DebugContext from '@/react/context/debug.js'; import EcsContext from '@/react/context/ecs.js'; import MainEntityContext from '@/react/context/main-entity.js'; import RadiansContext from '@/react/context/radians.js'; +import useAnimationFrame from '@/react/hooks/use-animation-frame.js'; import {juggleSession} from '@/server/session.server.js'; import {TAU} from '@/util/math.js'; @@ -29,23 +30,10 @@ export default function PlaySpecific() { const params = useParams(); const [type, url] = params['*'].split('/'); const [radians, setRadians] = useState(0); - useEffect(() => { - let handle; - let last; - const spin = (ts) => { - if ('undefined' === typeof last) { - last = ts; - } - const elapsed = (ts - last) / 1000; - last = ts; - setRadians((radians) => radians + (elapsed * TAU)); - handle = requestAnimationFrame(spin); - }; - handle = requestAnimationFrame(spin); - return () => { - cancelAnimationFrame(handle); - }; + const spin = useCallback((elapsed) => { + setRadians((radians) => radians + (elapsed * TAU)); }, []); + useAnimationFrame(spin); useEffect(() => { if (!Client) { return;