You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Framer Motion定位元素(或父容器)的技术问题

解决Framer中Sticky组件追踪容器滚动进度的问题

问题背景

使用图片序列动画代码实现滚动驱动的变体切换:创建组件并粘贴代码后,滚动时根据进度切换变体实现流畅动画。但遇到两个问题:

  • 将组件设为Sticky并放入高度为其2倍的容器后,组件仅在脱离固定状态时才追踪滚动
  • 改为追踪窗口滚动后,无法实现仅基于容器滚动进度触发动画的需求

解决思路

核心是让动画逻辑追踪容器的滚动而非组件自身或窗口,具体步骤:

  • 为容纳组件的容器创建独立的ref,用于监听其滚动事件
  • 修改useScroll的目标为容器ref,并调整offset参数匹配容器的滚动范围
  • 确保滚动进度计算基于容器的滚动距离,而非组件自身或窗口的滚动

修改后的代码

import React, { useState, ComponentType, useRef, useEffect } from "react"
import { useScroll, useMotionValueEvent } from "framer-motion"

const VARIANT_LENGTH = 61

export const withScrollVariants = (Component): ComponentType => {
    const variants = Array.from(
        { length: VARIANT_LENGTH },
        (_, i) => `Variant ${i}`
    )

    return (props) => {
        const [variantIndex, setVariantIndex] = useState(0)
        const [imagesLoaded, setImagesLoaded] = useState(false)
        // 新增容器ref,通过props传入容器的引用
        const containerRef = props.containerRef || useRef(null)
        const componentRef = useRef(null)

        // 监听容器的滚动进度,offset设置为容器的可见区间
        const { scrollYProgress } = useScroll({
            target: containerRef,
            offset: ["start start", "end end"],
        })

        useEffect(() => {
            const imageSources = []
            document.querySelectorAll("img").forEach((imgElement) => {
                imageSources.push(imgElement.src)
            })

            const imagePromises = imageSources.map((src) => {
                return new Promise((resolve, reject) => {
                    const img = new Image()
                    img.onload = () => resolve(src)
                    img.onerror = () => reject(src)
                    img.src = src
                })
            })

            Promise.all(imagePromises)
                .then(() => setImagesLoaded(true))
                .catch(() => setImagesLoaded(false))
        }, [])

        useMotionValueEvent(scrollYProgress, "change", (currentProgress) => {
            if (!imagesLoaded) return
            // 根据容器滚动进度计算变体索引,确保区间正确
            const newVariantIndex = Math.max(
                1,
                Math.min(
                    VARIANT_LENGTH - 1,
                    Math.round(currentProgress * (VARIANT_LENGTH - 2)) + 1
                )
            )
            if (newVariantIndex !== variantIndex) {
                setVariantIndex(newVariantIndex)
            }
        })

        return (
            <>
                <div style={{ display: "none" }}>
                    {variants.map((variant) => (
                        <Component key={variant} {...props} variant={variant} />
                    ))}
                </div>
                <Component
                    {...props}
                    variant={variants[variantIndex]}
                    ref={componentRef}
                />
            </>
        )
    }
}

使用说明

  1. 在Framer中创建容纳Sticky组件的容器Frame,给该容器添加ref(可通过Framer的"Override"或代码模式设置)
  2. 将动画组件的containerRef属性绑定到容器的ref
  3. 确保容器有足够的滚动高度(比如设置为组件高度的2倍),且组件设置为Sticky定位

这样修改后,动画会基于容器的滚动进度触发,Sticky组件在固定状态时也能正常响应容器的滚动。

内容的提问来源于stack exchange,提问作者David Stančík

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 22:23:26