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

移动端滚动动画无法完整执行:滚动始终差若干像素

解决移动设备滚动无法到底部的滚动动画问题

问题根源

  1. 动态视口高度计算偏差:window.innerHeight在移动设备上会随浏览器工具栏(如Safari地址栏)的展开/收缩变化,组件挂载时的初始值可能不准确,导致originY计算错误。
  2. 滚动进度未达100%:浏览器在移动设备上的滚动边界计算存在误差,scrollYProgress可能无法真正达到1,导致最后一个元素的动画无法触发完成。
  3. 动画属性冲突:同时使用animate={{ y: 20 }}和style={{ translateY: translateCardY }},两个属性都会控制元素垂直位置,引发逻辑冲突。

修复方案及代码调整

1. 动态获取准确视口高度

使用useState和useEffect监听视口变化,替换静态的window.innerHeight:

import { useState, useEffect } from 'react';
import { motion, useTransform, useScroll } from "motion/react";

export default function Cards() {
  // 动态获取视口高度
  const [viewportHeight, setViewportHeight] = useState(window.innerHeight);

  useEffect(() => {
    const handleResize = () => setViewportHeight(window.innerHeight);
    window.addEventListener('resize', handleResize);
    window.addEventListener('orientationchange', handleResize);
    return () => {
      window.removeEventListener('resize', handleResize);
      window.removeEventListener('orientationchange', handleResize);
    };
  }, []);

  const imgs = [
    "../img-18.jpg",
    "../img-12.jpg",
    "../img-9.jpg",
    "../img-14.jpg",
    "../img-13.jpg",
  ];

  const colors = [
    "bg-red-900",
    "bg-blue-900",
    "bg-teal-900",
    "bg-green-900",
    "bg-amber-900",
  ];

  const content = [
    {
      title: "Title 1",
      description: "Description 1",
      img: imgs[0],
      color: colors[0],
    },
    {
      title: "Title 2",
      description: "Description 2",
      img: imgs[1],
      color: colors[1],
    },
    {
      title: "Title 3",
      description: "Description 3",
      img: imgs[2],
      color: colors[2],
    },
    {
      title: "Title 4",
      description: "Description 4",
      img: imgs[3],
      color: colors[3],
    },
    {
      title: "Title 5",
      description: "Description 5",
      img: imgs[4],
      color: colors[4],
    },
  ];

  const { scrollYProgress } = useScroll();

  return (
    <div className="flex justify-center w-screen h-[300lvh] bg-gray-900 text-white relative">
      {content.map((item, index) => {
        // 用动态视口高度替换window.innerHeight
        const originY = (index + 1) * viewportHeight;

        // 调整滚动进度的终点,留小余量确保能触发
        const progressEnd = Math.min((index + 1) / content.length, 0.99);

        const translateCardY = useTransform(
          scrollYProgress,
          [0, progressEnd],
          [originY + "px", (index + 1) * 20 + "px"]
        );

        const scaleCard = useTransform(
          scrollYProgress,
          [0, 0.99],
          [1, 0.8 + (index / content.length) * 0.2]
        );

        return (
          <motion.div
            key={index}
            className="w-4/5 h-[400px] fixed top-0 flex items-center justify-center"
            // 移除冲突的animate属性,统一用style里的translateY
            style={{ translateY: translateCardY, scale: scaleCard }}
          >
            <motion.div
              className={`flex flex-row items-center justify-between w-full h-[300px] rounded-lg overflow-hidden ${item.color}`}
            >
              <img
                src={item.img}
                alt={item.title}
                className="w-1/2 h-full object-cover"
              />
              <div className="p-4 text-center w-1/2">
                <h2 className="text-xl font-bold">{item.title}</h2>
                <p className="text-sm">{item.description}</p>
              </div>
            </motion.div>
          </motion.div>
        );
      })}
    </div>
  );
}

2. 额外优化建议

  • 给滚动容器添加min-h-[100dvh]确保基础高度足够,避免出现滚动区域不足的情况:
    <div className="flex justify-center w-screen min-h-[100dvh] h-[300lvh] bg-gray-900 text-white relative">
    
  • 若仍有问题,可尝试给全局添加样式消除默认边距:
    html, body {
      margin: 0;
      padding: 0;
      overflow-x: hidden;
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 08:29:54