Description
Which react-spring target are you using?
-
@react-spring/web
-
@react-spring/three
-
@react-spring/native
-
@react-spring/konva
-
@react-spring/zdog
What version of react-spring are you using?
@react-spring/[email protected]
What's Wrong?
I am calling useSpring
with transform: "rotate(...)"
where the from
and to
transforms are both valid, but the resulting transform that react-spring gives is rotate(0.566259767453662rotate(, , ), 2rotate(, , ), 20rotate(, , ))
, which is an invalid value that fails to render properly.
To Reproduce
Download this HTML file and open it in your browser: https://gist.github.com/wchargin/0a49246fcb9792904878013c53987d1f
You'll see the following:
springbrokenff.mov
This HTML file draws a little speedometer needle and animates it from facing left to facing right. But when it gets to facing straight up, the animation stalls and only recovers once it's no longer animating at all:
<!doctype html>
<div id="root"></div>
<script type="module">
import React, { useEffect, useState } from "https://esm.sh/[email protected]";
import ReactDOM from "https://esm.sh/[email protected]";
import { animated, useSpring } from "https://esm.sh/@react-spring/[email protected]";
import { usePrevious } from "https://esm.sh/@uidotdev/[email protected]";
const USE_SPRING = true;
const h = React.createElement;
const rescale = (fromMin, fromMax, speed, toMin, toMax) => {
const fromRange = fromMax - fromMin;
const toRange = toMax - toMin;
return toMin + (speed - fromMin) * (toRange / fromRange);
};
const MIN_SPEED = 0;
const MAX_SPEED = 5;
const MIN_ANGLE = -90;
const MAX_ANGLE = 90;
const NEEDLE_WIDTH = 3.72;
const NEEDLE_HEIGHT = 22;
const ROTATION_Y = NEEDLE_HEIGHT - 2;
const getAngle = (speed) => {
return rescale(MIN_SPEED, MAX_SPEED, speed, MIN_ANGLE, MAX_ANGLE);
};
const App = () => {
const [speed, setSpeed] = useState(0);
const prevSpeed = usePrevious(speed);
useEffect(() => {
const id = setInterval(() => {
const delta = 1 / 50;
setSpeed((speed) => {
if (speed > 5) {
clearInterval(id);
}
return speed + delta;
});
}, 20);
}, []);
const springArgs = {
config: {
duration: 50,
},
from: {
transform: `rotate(${getAngle(prevSpeed)}, ${Math.round(NEEDLE_WIDTH / 2)}, ${ROTATION_Y})`,
},
to: {
transform: `rotate(${getAngle(speed)}, ${Math.round(NEEDLE_WIDTH / 2)}, ${ROTATION_Y})`,
},
};
console.log(springArgs.from.transform, springArgs.to.transform);
const animationProps = useSpring(springArgs);
return h(
"svg",
{},
h(
"g",
{ transform: "translate(100,0) scale(5)" },
h(animated.path, {
width: NEEDLE_WIDTH,
height: NEEDLE_HEIGHT,
d: "M1.5,0L1.5,0c0,0,2.3,17.5,2.1,20s-3.1,2.8-3.6,0S1.5,0,1.5,0z",
transform: USE_SPRING
? animationProps.transform
: springArgs.to.transform,
}),
),
);
};
ReactDOM.createRoot(root).render(h(App));
</script>
You can set USE_SPRING = false
to always use the "latest" transform value, which moves properly. This demonstrates that it's not an issue with the input that I'm passing to useSpring
.
Tested in Firefox 130 and Chrome 128 on macOS.
Expected Behaviour
The needle should animate smoothly without crashing in the middle.
Link to repo
https://gist.github.com/wchargin/0a49246fcb9792904878013c53987d1f