skip to Main Content

I have created an animation that takes a couple of seconds to play. The animation should only run when the element is in view, so I’ve used whileInView. This works correctly, but when I scroll down (so the element is out of view), the animation seems to run backwards, because when I scroll up again, I can see that the animation does not start from the beginning but from the point that it reached when it ran backwards and when the element came into view again. I want the animation to run from the beginning instead.

I’ve created a small sandbox to demonstrate the issue:

https://codesandbox.io/s/dreamy-cache-mk2dq2?file=/src/App.js

import "./styles.css";
import { motion } from "framer-motion";

export default function App() {
  return (
    <div style={{ height: "1000px" }}>
      <motion.div
        id="box"
        initial={{ x: 0 }}
        whileInView={{ x: 300 }}
        transition={{ duration: 5, ease: "linear" }}
      >
        <p>
          The box should be moving now. Scroll down and then up again. The
          animation will not instantly reset but run backwards.
        </p>
      </motion.div>
    </div>
  );
}

When you open the sandbox, the blue box will move. The animation takes 5 seconds. Scroll down and then up again (within five seconds). The animation will run from some point but not from the start.

How can I make the animation run from the start whenever the element comes into view?

2

Answers


  1. Chosen as BEST ANSWER

    p7dxb's answer gave me an idea. I found a very simple solution. You just have to set different transition values for initial and whileInView. Use a duration of 0 seconds for initial:

    import "./styles.css";
    import { motion } from "framer-motion";
    
    export default function App() {
      return (
        <div style={{ height: "1000px" }}>
          <motion.div
            id="box"
            initial={{ x: 0, transition: { duration: 0 } }}
            whileInView={{ x: 300, transition: { duration: 5, ease: "linear" } }}
          >
            <p>
              The box should be moving now. Scroll down and then up again. The
              animation will not instantly reset but run backwards.
            </p>
          </motion.div>
        </div>
      );
    }
    

    Here's the new sandbox: https://codesandbox.io/s/objective-elgamal-p49wdq


    • I used useInView to track when the div was scrolled past with useEffect to be called when this changed. useInView reference

    • When its in view it runs your animation & when out of view does a quick reverse animation (maybe this second part can be done better but it works).

    • I also used useAnimate instead of the div definitions see more about useAnimate here.

    • I edited the App.js on your codesandbox but don’t have an account to save and share a link. If you paste the below code in your sandbox’s App.js it should give you what you are after

    import "./styles.css";
    import { motion, useInView, useAnimate  } from "framer-motion";
    import { useEffect, useRef } from "react";
    
    export default function App() {
      const [scope, animate] = useAnimate()
      const motionDiv = useRef(null)
      const isInView = useInView(motionDiv)
    
    
      useEffect(()=>{
        if(isInView){
          animate("div", { x: 300 }, { duration:5, ease: "linear" })
        }else{
          animate("div", { x: 0 }, { duration:0 })
        }
      },[isInView, animate])
    
      return (
        <div ref={scope}  style={{ height: "1000px" }} >
        <div ref={motionDiv}>
          <motion.div
            id="box"
          >
            <p>
              The box should be moving now. Scroll down and then up again. The
              animation will not instantly reset but run backwards.
            </p>
          </motion.div>
        </div>
        </div>
      );
    }
    
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search