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

Framer Motion水平轮播视差效果失效问题排查

问题:Framer Motion水平滚动轮播中图片视差效果不生效

我尝试使用Framer Motion的useScroll和useTransform钩子实现带有图片视差效果的水平滚动轮播,但视差效果未生效——滚动时图片的X位置无明显变化。父组件的水平滚动功能正常,但ParallaxHoriz组件内的scrollYProgress值始终保持恒定,视差变换未按预期触发。

父组件代码(HorizontalScrollCarousel)

import { motion, useTransform, useScroll } from "framer-motion";
import { useRef } from "react";
import ParallaxHoriz from '../../animations/ParallaxHoriz/ParallaxHoriz';
import "./HorizontalScrolling.css";

const HorizontalScrollCarousel = () => {
  const targetRef = useRef(null);
  const { scrollYProgress } = useScroll({
    target: targetRef,
  });

  const x = useTransform(scrollYProgress, [0, 1], ["0%", "-100%"]);

  return (
    <section ref={targetRef} className="horizontal-scroll-carousel">
      <div className="carousel-container">
        <motion.div style={{ x }} className="carousel-content">
          <div className="carousel-card-container">
            {cards.map((card) => (
              <div key={card.id} className="card">
                <div className="card-image-container">
                  <ParallaxHoriz>
                    <div
                      className="card-image"
                      style={{
                        backgroundImage: `url(${card.url})`,
                        width: "100%",
                        height: "100%",
                      }}
                    ></div>
                  </ParallaxHoriz>
                </div>
              </div>
            ))}
          </div>
        </motion.div>
      </div>
    </section>
  );
};

export default HorizontalScrollCarousel;

const cards = [
  { url: "image1.jpg", id: 1 },
  { url: "image2.jpg", id: 2 },
  // Additional cards...
];

视差组件代码(ParallaxHoriz)

import React from 'react';
import { motion, useScroll, useTransform } from 'framer-motion';

const ParallaxHoriz = ({ children }) => {
  const ref = React.useRef(null);
  const { scrollYProgress } = useScroll({
    target: ref,
    offset: ["start end", "end start"],
  });

  const x = useTransform(scrollYProgress, [0, 1], ["-20%", "20%"]);

  return (
    <div ref={ref} style={{ overflow: "hidden", width: "100%", height: "100%" }}>
      <motion.div
        style={{
          width: "120%",
          height: "100%",
          x,
          position: "absolute",
          left: "-10%",
        }}
      >
        {children}
      </motion.div>
    </div>
  );
};

export default ParallaxHoriz;

CSS样式

.horizontal-scroll-carousel {
  position: relative;
  height: 1000vh;
  background-color: #fdf7f0;
}

.carousel-container {
  position: sticky;
  top: 0;
  height: 100vh;
  display: flex;
  overflow: hidden;
}

.carousel-content {
  display: flex;
  will-change: transform;
}

.carousel-card-container {
  display: flex;
  gap: 16px;
}

.card {
  display: flex;
  height: 100vh;
  width: 100vw;
}

.card-image {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}

问题根源

  1. 滚动上下文错误:ParallaxHoriz组件监听自身元素的滚动,但实际页面滚动是由父组件horizontal-scroll-carousel(1000vh高度)触发的,子组件自身没有滚动空间,导致scrollYProgress始终不变。
  2. 目标元素无滚动范围:ParallaxHoriz的ref绑定在高度100%的容器上,其父元素是sticky定位的carousel-container(高度100vh),该容器不会随页面滚动,因此子组件无法捕获滚动进度变化。

解决方案

步骤1:传递父组件的滚动进度给子组件

将父组件中获取的scrollYProgress作为props传递给ParallaxHoriz,让子组件使用正确的全局滚动上下文:

// 修改HorizontalScrollCarousel中的卡片渲染部分
{cards.map((card) => (
  <div key={card.id} className="card">
    <div className="card-image-container">
      {/* 传递父组件的scrollYProgress */}
      <ParallaxHoriz scrollYProgress={scrollYProgress}>
        <div
          className="card-image"
          style={{
            backgroundImage: `url(${card.url})`,
            width: "100%",
            height: "100%",
          }}
        ></div>
      </ParallaxHoriz>
    </div>
  </div>
))}

步骤2:修改子组件使用传入的滚动进度

移除子组件自身的useScroll钩子,直接使用父组件传递的滚动进度计算视差变换:

// 修改ParallaxHoriz组件
import React from 'react';
import { motion, useTransform } from 'framer-motion';

const ParallaxHoriz = ({ children, scrollYProgress }) => {
  // 使用父组件传递的scrollYProgress计算视差
  const x = useTransform(scrollYProgress, [0, 1], ["-20%", "20%"]);

  return (
    <div style={{ overflow: "hidden", width: "100%", height: "100%" }}>
      <motion.div
        style={{
          width: "120%",
          height: "100%",
          x,
          position: "absolute",
          left: "-10%",
        }}
      >
        {children}
      </motion.div>
    </div>
  );
};

export default ParallaxHoriz;

可选优化:限制视差触发范围

如果希望每张卡片的视差仅在进入/离开视口时生效,可以结合useInView钩子控制:

// 优化后的ParallaxHoriz组件
import React from 'react';
import { motion, useTransform, useInView } from 'framer-motion';

const ParallaxHoriz = ({ children, scrollYProgress }) => {
  const ref = React.useRef(null);
  const isInView = useInView(ref, { once: false, margin: "-100px" });

  const x = useTransform(
    scrollYProgress,
    [0, 1],
    ["-20%", "20%"],
    { clamp: false, ease: "linear" }
  );

  return (
    <div ref={ref} style={{ overflow: "hidden", width: "100%", height: "100%" }}>
      <motion.div
        style={{
          width: "120%",
          height: "100%",
          x: isInView ? x : 0,
          position: "absolute",
          left: "-10%",
        }}
      >
        {children}
      </motion.div>
    </div>
  );
};

export default ParallaxHoriz;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 08:27:33