最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to add a delay to an animation if it's in the viewport initially, otherwise have a different delay? - S

programmeradmin0浏览0评论

I'm trying to build a website using React and framer motion and I'm running into a challenge.

If an element is in the viewport when the page initially loads, I want there to be a delay on the animation. But if the element isn't in the viewport, and the user has to scroll to see it, I want there to be a different delay value.

So for example, on my website I have a header Lorem ipsum and some text that I render first. Then I have a Projects header that renders second. And lastly, a My Project block that renders third.

This looks great if the window height is big enough so that everything loads in at once.

But if the window height is smaller, like in the GIF below, the My Project block takes too long to load in.

I'd like to have it so that it loads in faster like this:

I tried searching online to see if someone has had this problem before, but I couldn't find any posts online about this.

Any ideas on how to solve this?

My code:

// page.tsx

<main>
  <div>
    <motion.h1
      initial={{ opacity: 0, y: 10 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ ease: 'linear', duration: 0.5, delay: 0.15 }}
    >
      Lorem ipsum
    </motion.h1>
    <motion.div
      initial={{ opacity: 0, y: 10 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ ease: 'linear', duration: 0.5, delay: 0.15 }}
    >
      <h1>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
        tempor incididunt ut labore
      </h1>
      <h1>
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
      </h1>
    </motion.div>
  </div>
  <motion.h2
    initial={{ opacity: 0, y: 10 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ ease: 'linear', duration: 0.5, delay: 0.8 }}
  >
    Projects
  </motion.h2>
  <div>
    <Card
      title={cards[0].title}
      description={cards[0].description}
      index={0}
    />
  </div>
</main>
// Card.tsx

type CardProps = {
  title: string;
  description: string;
  index: number;
};

export default function Card({
  title,
  image,
  altText,
  slug,
  index,
  description,
}: CardProps) {
  delay =  1.05 + index * 0.2; // default delay
  // delay = 0.25; // delay I want if it's not in the viewport on first page load

  return (
    <motion.div
      initial={{ opacity: 0, y: 10 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{
        ease: 'linear',
        duration: 0.5,
        delay: delay,
      }}
    >
      <p>{title}</p>
      <p>{description}</p>
      <a>
        Learn more
        <svg>...</svg>
      </a>
    </motion.div>
  );
}

I'm trying to build a website using React and framer motion and I'm running into a challenge.

If an element is in the viewport when the page initially loads, I want there to be a delay on the animation. But if the element isn't in the viewport, and the user has to scroll to see it, I want there to be a different delay value.

So for example, on my website I have a header Lorem ipsum and some text that I render first. Then I have a Projects header that renders second. And lastly, a My Project block that renders third.

This looks great if the window height is big enough so that everything loads in at once.

But if the window height is smaller, like in the GIF below, the My Project block takes too long to load in.

I'd like to have it so that it loads in faster like this:

I tried searching online to see if someone has had this problem before, but I couldn't find any posts online about this.

Any ideas on how to solve this?

My code:

// page.tsx

<main>
  <div>
    <motion.h1
      initial={{ opacity: 0, y: 10 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ ease: 'linear', duration: 0.5, delay: 0.15 }}
    >
      Lorem ipsum
    </motion.h1>
    <motion.div
      initial={{ opacity: 0, y: 10 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ ease: 'linear', duration: 0.5, delay: 0.15 }}
    >
      <h1>
        Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
        tempor incididunt ut labore
      </h1>
      <h1>
        Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
      </h1>
    </motion.div>
  </div>
  <motion.h2
    initial={{ opacity: 0, y: 10 }}
    animate={{ opacity: 1, y: 0 }}
    transition={{ ease: 'linear', duration: 0.5, delay: 0.8 }}
  >
    Projects
  </motion.h2>
  <div>
    <Card
      title={cards[0].title}
      description={cards[0].description}
      index={0}
    />
  </div>
</main>
// Card.tsx

type CardProps = {
  title: string;
  description: string;
  index: number;
};

export default function Card({
  title,
  image,
  altText,
  slug,
  index,
  description,
}: CardProps) {
  delay =  1.05 + index * 0.2; // default delay
  // delay = 0.25; // delay I want if it's not in the viewport on first page load

  return (
    <motion.div
      initial={{ opacity: 0, y: 10 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{
        ease: 'linear',
        duration: 0.5,
        delay: delay,
      }}
    >
      <p>{title}</p>
      <p>{description}</p>
      <a>
        Learn more
        <svg>...</svg>
      </a>
    </motion.div>
  );
}
Share Improve this question asked Jan 31 at 23:41 mattmatt 3204 silver badges13 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 0

I solved it! It took me a while to think of a solution because useInView and IntersectionObserver will tell you when something enters the viewport, but they don't tell you if an element is in the view port on first page load.

The solution is surprisingly simple and concise:

const ref = useRef<HTMLDivElement>(null);
const [delay, setDelay] = useState(1.05 + index * 0.2);

useEffect(() => {
  if (ref.current) {
    if (ref.current.getBoundingClientRect().top > window.innerHeight) {
      setDelay(0.25);
    }
  }
}, []);

return (
  <motion.div
    ref={ref}
    initial={{ opacity: 0, y: 10 }}
    whileInView={{ opacity: 1, y: 0 }}
    viewport={{ once: true }}
    transition={{
      ease: 'linear',
      duration: 0.5,
      delay: delay,
    }}
  >
    // ...
)

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论