You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何结合React Intersection Observer与Framer Motion useTransform实现视口滚动触发的多区块复用动画

Using Framer Motion with React Intersection Observer for Scroll-Triggered Animations

Hey there! I’ve worked through your scenario and I’ll walk you through how to combine React Intersection Observer with Framer Motion’s useTransform for smooth scroll-triggered animations across your page sections.

Core Idea

The goal is to use React Intersection Observer to detect when a section enters (or moves within) the viewport, then feed that visibility state or scroll progress into Framer Motion’s useTransform to drive your animations. Let’s break this down into two common use cases: one-time enter animations, and continuous scroll-driven animations.


1. One-Time Enter Animation (Triggered on First View)

This is perfect for sections that animate once when they first scroll into view. Here’s how to adjust your component:

First, make sure you’re importing the right hooks from both libraries:

import { motion, useTransform, useAnimation } from "framer-motion";
import { useInView } from "react-intersection-observer";

Then create a reusable Section component that ties everything together:

const Section = ({ children }) => {
  // Track visibility of the section
  const [ref, inView] = useInView({
    triggerOnce: true, // Animate only once
    threshold: 0.1 // Trigger when 10% of the section is in view
  });

  // Map visibility state to animation values with useTransform
  const opacity = useTransform(inView, [false, true], [0, 1]);
  const yPosition = useTransform(inView, [false, true], [50, 0]);

  return (
    <motion.section
      ref={ref}
      style={{ opacity, y: yPosition }}
      transition={{ duration: 0.6, ease: "easeOut" }}
      className="section"
    >
      {children}
    </motion.section>
  );
};

How this works:

  • useInView gives us a ref to attach to our section, and an inView boolean that tells us if the section is visible.
  • useTransform takes the inView state and maps it to our desired animation properties: when the section is hidden (false), it’s transparent (opacity: 0) and shifted down (y: 50); when visible (true), it fades in and slides up to its natural position.
  • The transition prop controls the timing and easing of the animation for a polished feel.

2. Continuous Scroll-Driven Animation (Animate as You Scroll)

If you want animations that respond to how much of the section is visible (e.g., scaling or fading as the user scrolls through the section), we can use the intersectionRatio from the Intersection Observer entry:

const Section = ({ children }) => {
  const [ref, inView, entry] = useInView({
    triggerOnce: false, // Keep tracking as the user scrolls
    threshold: 0 // Start tracking as soon as any part of the section enters view
  });

  // Get the ratio of the section that's visible (0 = none, 1 = fully visible)
  const scrollProgress = entry?.intersectionRatio || 0;

  // Map scroll progress to animation values
  const scale = useTransform(scrollProgress, [0, 1], [0.8, 1]);
  const opacity = useTransform(scrollProgress, [0, 0.2, 1], [0, 0.5, 1]);

  return (
    <motion.section
      ref={ref}
      style={{ opacity, scale }}
      className="section"
    >
      {children}
    </motion.section>
  );
};

How this works:

  • The entry object from useInView includes intersectionRatio, a value between 0 and 1 representing how much of the element is in the viewport.
  • useTransform uses this ratio to create smooth transitions: as the section scrolls into view, it scales from 80% to 100% and fades from fully transparent to opaque (with a midpoint at 20% visibility).

Key Tips

  • Reusability: Wrap this logic into a reusable component like AnimatedSection so you can apply the same animation to all your page sections without repeating code.
  • Customization: Adjust the threshold in useInView to control when the animation starts (e.g., 0.5 triggers when half the section is visible).
  • Variants Alternative: If you prefer using Framer Motion’s variants, you can pair useAnimation controls with the inView state instead of useTransform:
    const controls = useAnimation();
    useEffect(() => {
      controls.start(inView ? "visible" : "hidden");
    }, [controls, inView]);
    
    return (
      <motion.section
        ref={ref}
        variants={{
          hidden: { opacity: 0, y: 50 },
          visible: { opacity: 1, y: 0 }
        }}
        animate={controls}
        initial="hidden"
        transition={{ duration: 0.6 }}
      >
        {children}
      </motion.section>
    );
    

内容的提问来源于stack exchange,提问作者DanBilly

火山引擎 最新活动