skip to Main Content

I’ve got parent component generating x "snowflake" components. Each of the snowflakes has its own ref attributes, that invokes "animatesnowflake" within useEffect hook. The animation function itself, has some dynamic values, however both starting Y and gsap.to y values are the same for each snowflake ( when not animating snowflakes are set in the same Y position on the webpage). Not sure why, but gsap is not animating correctly as some y values are starting as expected, some are playing half of the animation, some are starting in the wrong Y point, anybody knows why gsap is "freakin out" here? Below is the code for parent component:

export default function Snow() {
    let l = 50;
    let arr = new Array(l);
    for( let i = 0; i < l; i++){
        i%2 === 0 ? arr[i] = 0 : arr[i] = 1
    }

 const animateSnowflake = (el)=>{
        let h = window.innerHeight;
        let w = window.innerWidth;
        let delay = gsap.utils.random(1, 5, 1);
        let duration = gsap.utils.random(3, 8, 1);
        let startX = gsap.utils.random(0, w, 1);
        let startY = -200;
        let y = h;
        let x = gsap.utils.random(startX-5, startX+5, 1)
        let s = gsap.utils.random(0.5, 1, 0.1)

        let tl = gsap.timeline();
        tl.set(el, {
            y: startY,
            x: startX,
            scale: 1
        })
        console.log(y)
        tl.to(el, {
            y: y+200,
            scale: s*3,
            duration: duration,
            delay: delay,
            ease: 'none',
            repeat: -1

        })
    }

   return (
        <div className = 'Snow'>

            {arr.map(el=>{
                if(el === 1) return <Snowflake1 animateSnowflake = {animateSnowflake}/>;
                return <Snowflake2  animateSnowflake = {animateSnowflake}/>
            })}

        </div>
    )
}

And here’s code for 1 of the snowflakes ( only differencce between 1 and 2 is svg ):

export default function Snowflake2({animateSnowflake}) {
    const ref = useRef(null);

    useEffect(() => {
        animateSnowflake(ref.current)
    }, []);
    return (
        <div ref = {ref}>
            <svg 
            </svg>
        </div>

    )
}

2

Answers


  1. Chosen as BEST ANSWER

    So the problem is bloody "react strict mode" when using create react app. jTook me 10 hours to actually figure it out... "React StrictMode" is causing rerenders hence gsap is behaving in unpredicted manners...


  2. Here’s a small example with no freak-out that I can see; since you’re already touching the DOM directly by way of GSAP, you can as well communicate whether you’re already animating that DIV with a dataset attribute.

    👉 See live here on CodeSandbox.

    import "./styles.css";
    import gsap from "gsap";
    import React from "react";
    
    function animateSnowflake(el) {
      if (!el) return;
    
      if (el.dataset.snowing) {
        return;
      }
      el.dataset.snowing = 1;
    
      let h = window.innerHeight;
      let w = window.innerWidth;
      let delay = gsap.utils.random(1, 5, 1);
      let duration = gsap.utils.random(3, 8, 1);
      let startX = gsap.utils.random(0, w, 1);
      let startY = -200;
      let y = h;
      let x = gsap.utils.random(startX - 5, startX + 5, 1);
      let s = gsap.utils.random(0.5, 1, 0.1);
    
      let tl = gsap.timeline();
      tl.set(el, {
        y: startY,
        x: startX,
        scale: 1,
      });
      tl.to(el, {
        y: y + 200,
        x,
        scale: s * 3,
        duration: duration,
        delay: delay,
        ease: "none",
        repeat: -1,
      });
    }
    
    function Snowflake({ id }) {
      // Could use id % 2 to change between SVGs here
      return (
        <div ref={animateSnowflake} style={{ position: "absolute" }}>
          {id}
        </div>
      );
    }
    
    export default function App() {
      const [numFlakes, setNumFlakes] = React.useState(50);
      const flakes = [];
      for (let i = 0; i < numFlakes; i++) {
        flakes.push(<Snowflake key={i} id={i} />);
      }
      return (
        <div>
          <label>
            <input
              type="range"
              min={0}
              max={1000}
              value={numFlakes}
              onChange={(e) => setNumFlakes(e.target.valueAsNumber)}
            />
            {numFlakes} flakes
          </label>
          {flakes}
        </div>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search