Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[bug]: incorrect SVG rotation transform generated #2317

Open
1 of 5 tasks
wchargin opened this issue Sep 9, 2024 · 1 comment
Open
1 of 5 tasks

[bug]: incorrect SVG rotation transform generated #2317

wchargin opened this issue Sep 9, 2024 · 1 comment
Labels
template: bug This issue might be a bug

Comments

@wchargin
Copy link

wchargin commented Sep 9, 2024

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

@wchargin wchargin added the template: bug This issue might be a bug label Sep 9, 2024
@wchargin
Copy link
Author

wchargin commented Sep 9, 2024

Extra tags for searching: rotation transform with extra commas and blank values / spaces, rotation transform with very small numbers.

I'm using value = Math.round(value * 100) / 100 as a workaround, but that's obviously pretty shoddy.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
template: bug This issue might be a bug
Projects
None yet
Development

No branches or pull requests

1 participant