使用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} /> </> ) } }
使用说明
- 在Framer中创建容纳Sticky组件的容器Frame,给该容器添加
ref(可通过Framer的"Override"或代码模式设置) - 将动画组件的
containerRef属性绑定到容器的ref - 确保容器有足够的滚动高度(比如设置为组件高度的2倍),且组件设置为Sticky定位
这样修改后,动画会基于容器的滚动进度触发,Sticky组件在固定状态时也能正常响应容器的滚动。
内容的提问来源于stack exchange,提问作者David Stančík
相关产品推荐
相关产品推荐

