Jump to content

paulorero last won the day on June 21 2020

paulorero had the most liked content!


  • Posts

  • Joined

  • Last visited

  • Days Won


Posts posted by paulorero

  1. To bring this thread to an end. I made your code @Rodrigo to work with functional components without killing the tween on prop caused re-renders..




    In order to use dynamically updating props in the spinning Tween, I used React.useRef to save those values. Refs dont trigger rerender and therefor the tween won't be killed and reinitialized or something. They can be used in the useEffect hook without it running again.


      React.useEffect(() => {
        if (typeof stopAt !== "number") return;
        stopAtRef.current = stopAt;
      }, [stopAt]);
    onComplete: () => {
      console.log("onComplete", { stopAt, stopAtRef: stopAtRef.current });
      // The stop position can be checked on every tween iteration without useEffect running again
      if (loopIterationRef.current >= 2 && typeof stopAtRef.current === "number") {
        gsap.to(wheelRef.current, {
          rotation: `+=${stopAtRef.current + 360}`,
          duration: (1.8 * (stopAtRef.current + 360)) / 360,
          onComplete: () => {
            if (onStopSpinning) {
      } else {


    I really appreciate your help. This forum and gsap are just great.

    • Like 2
    • Thanks 1
  2. Quote

    Hi Paul, I am not across the whole thread but just looked at your demo and see that you are trying to manage the spinning state in both the App and Wheel components. Shouldn't you just have one spinning state?

    Yes good point. But in this case it's really secondary and I implemented it just to disable some buttons while spinning

  3. I made the stop position customizable and pass it into the wheel componenent. There I moved away from the infinite spinning with onRepeat and used onComplete to check each time if it's time to stop. It kinda works and the correct stop position is guaranteed. 

    What is your oppinion to this approach?




    Now all the tweens are inside componentDidMount() - is this the way to go? How would you implement a reset feature e.g. after the wheel stopped, one can play it again.

  4. This looks great at first glance!


    I'll have a look to the whole stopping angle stuff. You used the timescale technice which is a bit tricky for stopping at a specific angle that's why I used different tweens in the first place.


    Will come back soon to this thread.


    Thank you!! :) 




  5. I tried a lot but anything will result in a rerender of the component which will kill the animation...


    Is it possible to use the same timeline/tween at the same "play head" after a rerender happened?  I see no way to achieve this. Can't imagine I'm the only one with this problem. 


    How would you handle this in a class component? I dont see a way to do it either.



  6. I dont really get how this whole GSAP instance inside useState should work when we initiate them inside a useEffect. How do you suggest the dynamic props are passed into the instance?


    In this Pen I added the instances into react useState. It's not working because they are not added into the dependency array. When I do it also fails.

    See the Pen YzwGgMw by paulborm (@paulborm) on CodePen



    Also another alternative that I would try is to tween the timescale of the animation, in order to create a smooth start/end for the animation, like this sample:

    Timescale does not work in this case because the wheel must stop at an asynchronus point.

  7. Quote

    Another alternative, something that I'd do, is to use a class component for this one, but that is just my opinion on the subject. I know hooks are the stuff all the cool kids are using and imposing, but sometimes a class component is the easiest solution IMHO.

    I agree with you. Unfortunately I am not able to use a class component in this case.


    Adding the GSAP instances into the state doesn't really work for me (maybe I did something wrong).

    Setting tweens which include the dynamic props, requires the useEffect to include them in it's dependency array. Which then leads to a lot of rerenders, calulation and loops.

     const [spinning, setSpinning] = React.useState(false);
      const [stopTween, setStopTween] = React.useState(null);
      const [startTween, setStartTween] = React.useState(null);
      const [loopTween, setLoopTween] = React.useState(null);
      const circleRef = React.useRef(null);
      const loopIteration = React.useRef(0);
      const stopRotation = 360 + stopAt;
      const handleStartSpinning = () => {
      React.useEffect(() => {
          gsap.to(circleRef.current, {
            rotation: `+=${stopRotation}`,
            // Calculate the new duration ...
            // ... to make the transition between spinning and ...
            // ... stopping as smooth as possible.
            duration: stopRotation / 360,
            paused: true,
      }, [stopRotation]);
      React.useEffect(() => {
          gsap.to(circleRef.current, {
            rotation: "+=360",
            ease: "none",
            duration: 0.5,
            onComplete: () => {
              // The props won't update in here...
              if (
                loopIteration.current >= fullSpins &&
                typeof stopAt === "number"
              ) {
              } else {
            paused: true,
      }, [stopTween, loopTween, fullSpins, stopAt]);
      React.useEffect(() => {
          gsap.to(circleRef.current, {
            rotation: "+=360",
            ease: "power1.in",
            duration: 1,
            onComplete: () => loopTween.play(),
            paused: true,
      }, [loopTween]);
      React.useEffect(() => {
        if (spinning) {
      }, [spinning, startTween]);



    4 minutes ago, ZachSaucier said:

    If I'm understanding you correctly you can just add an else in your useEffect and play the stop animation:


    With this solution the wheel won't stop smoothly as it is supposed to be.


    The wheel needs to start spinning, spin infite until it gets a stop position and then stop smoothly on the provided position.

    Just like this but in React:

    See the Pen XWmygvB by paulborm (@paulborm) on CodePen

  9. Hi there,



    • How to prevent the gsap animations to be aborted in react because of a rerender/prop change
    • How to use dynamic props/state inside a gsap tween without beeing interrupted


    I'm building a spinng wheel with dynamic start- and end-points. You guys already helped me a lot with this:


    Now I need to implement this into an acutal react application. The biggest problem is, that I use dynamic Properties inside my gsap tweens to calculate e.g. the stop position or when to stop animation.

    React rerenders my component and aborts the whole animation as soon as a property changes. Of course react should do that - but how to I keep my animation running?


    What my code should do:

    • start spinning by clicking the "Start Spinning" Button
    • Wheel is spinning infinite
    • Stop wheel by clicking the "Stop Spinning" Button
    • Wheel at least the minimum amount (5) and then stops at the set position


    What my code actually does:

    • start spinning by clicking the "Start Spinning" Button
    • Wheel is spinning infinite
    • Clicking "Stop Spinning" does not work ->
      • triggers in my local invironment a rerender and aborts the animation
      • in codepen it flickers and then nothing happens (the stop position is never passed into the tween)
    • ...


    In the codepen it actually does not rerender but the updated prop won't be passed into the tween.

      const loopAnim = gsap.to(circleRef.current, {
        rotation: "+=360",
        ease: "none",
        duration: 0.5,
        onComplete: () => {
          // The props won't update in here...
          if (loopIteration.current >= fullSpins && typeof stopAt === "number") {
        } else {
        paused: true


    See the Pen ZEQpMwY by paulborm (@paulborm) on CodePen

  10. Hi ZachSaucier, thank you for your answer :)


    Something like that also was my first try. I like your solution because the start and loop phase blend nicely.

    The end phase how ever is not as smooth because the tween needs to travel more distance in a fixed duration...


    Is there a way to combine these two approaches (using timeScale, using multiple tweens in timeline)?

    My goal would be an dynamic animation like the example using `timeScale`. But I have no idea how to achieve something like this.


    It would be cool if we could just append the final rotation onto the tween in the "pause" event handler.

    var rotateCD,
        playBtn  = document.getElementById("play"),
        pauseBtn = document.getElementById("pause"),
        audio    = document.createElement("audio");
    rotateCD = gsap
    .to("#creature", {
      rotation: 360, 
      ease: 'none',
      repeat: -1,
      paused: true
    play.onclick = function() {
      gsap.to(rotateCD, {timeScale: 1, duration:3});
    pause.onclick = function() {
      // Adding the final rotation here would be ideal
      gsap.to(rotateCD, { rotation: 36, timeScale: 0, duration:3, onComplete: function() { this.pause(); }});


  11. Hi there,


    I want to create an animated wheel which starts at a specific degree and ends at a specific degree. Until it's requested to stop/pause it needs to spin infinite.


    So basically there are three stages "starting", "spinning", "stopping". The difficult task is to start and stop spinning with some momentum to feel natural.


    I have found multiple very helpful forum posts about that topic. They all stop at a random degree.


    Especially this seams to be a very similar problem but the solution is missing the momentum at start and stop position. 


    Thanks in advance :)


    See the Pen abzwvGm by mikeK (@mikeK) on CodePen