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

react-spring animations with react-three-fiber are choppy when using frameloop="demand" #1707

Closed
dmaslan opened this issue Oct 6, 2021 · 11 comments
Labels
area: three Relates to the three package template: bug This issue might be a bug

Comments

@dmaslan
Copy link

dmaslan commented Oct 6, 2021

🐛 Bug Report

Continuing from this thread in the @react-three/fiber GH discussions. When using react-spring animations in a @react-three/fiber scene, adding the frameloop="demand" prop to the canvas (to boost performance with on-demand rendering) causes animations to become very choppy or sometimes not run at all. This is contrary to the expected behavior described in the r3f docs, which say that react-spring should automatically invalidate the frame loop at every frame of the animation, triggering the animation to render.

This issue seems to have started with something that changed in version 9.2.2. Changing the dependency to v9.2.1 fixes the issue.

To Reproduce

  1. Create a basic scene with react three fiber.
  2. Add an animation with @react-spring/three.
  3. Add the frameloop=demand prop to the <Canvas> element

Expected behavior

Smooth animation, as described in the react three fiber docs.

Link to repro (highly encouraged)

Forked a demo from the react three fiber website and added frameloop=demand. The demo should animate when the ball is clicked. Animation is smooth when the frameloop=demand prop is removed.

Environment

  • react-spring v9.2.4
  • react v17.02
  • @react-three/fiber v7.06
@joshuaellis joshuaellis added template: bug This issue might be a bug area: three Relates to the three package labels Oct 6, 2021
@joshuaellis joshuaellis added this to the v9.X.X milestone Oct 6, 2021
@joshuaellis
Copy link
Member

It's a complicated issue, rs under VR needs to be ran by r3f, so when you set the frameLoop to demand, and you're not calling that invalidate function then it won't run either.

We do this because of this #1518

Where r3f can run in non-brower ENVs. There's probably a solution to this that would resolve the issue, but i'd argue the docs are a little inaccurate.

@joshuaellis
Copy link
Member

So, to add. Your assumption is that despite you declaring demand on your r3f renderer, react-spring a different library should be able to control such a thing. Which i'm not sure is right to do... 🤔

@dmaslan
Copy link
Author

dmaslan commented Dec 16, 2021

So, to add. Your assumption is that despite you declaring demand on your r3f renderer, react-spring a different library should be able to control such a thing. Which i'm not sure is right to do... 🤔

Yes, that's what I would expect after reading the docs, that react spring animations have the invalidate function built in. But I see your point.

@joshuaellis
Copy link
Member

Those docs should probably be changed.

I think to resolve this we need to understand how people use invalidate and demand in their apps. Including those using VR.

Do you have an example to hand how you're using it in your app?

@dmaslan
Copy link
Author

dmaslan commented Dec 16, 2021

Sure, right now I'm using it in a super basic way. I have a model that can be rotated around with OrbitControls (from @react-three/drei) and certain elements should change in scale when hovered over.

So the basic setup is:

<Canvas frameloop="demand">
  <OrbitControls />
  <Model />
  ...
</Canvas>

And then, inside the Model component I have components like:

const Marker = (props) => {
  const [hovered, setHovered] = useState(false)
  const hoverAnimation = useSpring({ scale: hovered ? [2, 2, 2] : [1, 1, 1] })

  const handlePointerOver = (e) => {
    e.stopPropagation()
    setHovered(true)
  };

  const handlePointerOut = (e) => {
    e.stopPropagation()
    setHovered(false)
  };

  return (
    <a.mesh
      {...props}
      scale={hoverAnimation.scale}
      onPointerOver={handlePointerOver}
      onPointerOut={handlePointerOut}
    >
      <a.sphereGeometry args={[0.2, 32, 32]} />
    </a.mesh>
  )
}

So, in my app, unless the model is being actively rotated or hovered over, it is basically static and doesn't need to be rendering at 60 fps and firing up the fans on my computer, which is why I prefer the use frameloop=demand. However, when a user does hover over one of the Marker components, I would like it to render the spring animation.

I'm not so familiar with how to manually call the invalidate function as needed, so maybe that would be an easy solution in my simple scenario, but it does seem like having react spring do this behind the scenes would be convenient, at least in my case.

@chuckdries
Copy link

In the docs section for frameloop="demand", it says

Drei's controls do this automatically for you. If you use react-spring to animate your scene then it will also take care of it.

Which I take to mean react-three-fiber is calling invalidate for me, but from this thread I take it that's incorrect? What does that phrase in the docs mean? React-spring doesn't seem to have a way for me to manually call invalidate when it moves, so I think I'm not able to use manual frames.

@joshuaellis
Copy link
Member

Those docs are incorrect, I'm not sure who wrote them. But r3f runs spring so you can use it in XR environments.

It's a problem we're wanting to solve.

@joshuaellis joshuaellis removed this from the v9.X.X milestone Jan 3, 2022
@looeee
Copy link

looeee commented Feb 2, 2022

I'm running into this issue as well. Here's a simple workaround:

const { invalidate } = useThree();

const anim = useSpring({
	// ... 
	onChange: () => invalidate(),
});

despite you declaring demand on your r3f renderer, react-spring a different library should be able to control such a thing. Which i'm not sure is right to do... 🤔

This is reasonably - to be fair I was surprised to find that react spring automatically invalidate the R3F loop. I wouldn't expect it to.

However, the docs do need to be updated if this is incorrect.

@joshuaellis
Copy link
Member

joshuaellis commented Feb 2, 2022

Thanks @looeee

However, the docs do need to be updated if this is incorrect.

I believe someone submitted a PR for this yesterday.

Thanks for the pointer as well. To anyone interested i've updated this sandbox with the workaround – https://codesandbox.io/s/react-spring-animations-forked-tmnpk?file=/src/index.js

I also think this might be the best solution as we ootb support XR and WebGL three.js renderers, and if someone wants to go down the manual route then they need to take it upon themselves to call the invalidate function (which can be seen either coming from useThree or importing). This would be something the react-spring docs should reflect...

cameron-martin pushed a commit to cameron-martin/react-spring that referenced this issue May 10, 2022
pmndrs#1707)

* Fix: ResponsiveSwarmPlotCanvas does not work with valueScale type time

* Update packages/swarmplot/src/SwarmPlotCanvas.tsx

Co-authored-by: therightstuff <adam@industrialcuriosity.com>

* Update packages/swarmplot/src/SwarmPlotCanvas.tsx

Co-authored-by: therightstuff <adam@industrialcuriosity.com>

Co-authored-by: Raphaël Benitte <501642+plouc@users.noreply.github.com>
Co-authored-by: therightstuff <adam@industrialcuriosity.com>
@daralthus
Copy link

@react-spring/three: 9.4.5
@react-three/fiber: 8.0.20
react-spring: 9.4.5

Unfortunately it seems react-spring/web animations also stop when using r3f frameloop="demand" in a completely separate component outside the canvas. It hardly makes sense: why would I need to call invalidate, if I don't want the canvas to render a new frame??

@joshuaellis
Copy link
Member

Whilst I understand there is still an issue with three & web targets not playing nicely together, I'm going to close this issue in favour of #1586.

My view still stands if you want to use demand in @react-three/fiber than you are responsible for invalidating your frames and running that frame loop.

@joshuaellis joshuaellis closed this as not planned Won't fix, can't repro, duplicate, stale Jun 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: three Relates to the three package template: bug This issue might be a bug
Projects
None yet
Development

No branches or pull requests

5 participants