移动端滚动动画无法完整执行:滚动始终差若干像素
解决移动设备滚动无法到底部的滚动动画问题
问题根源
- 动态视口高度计算偏差:
window.innerHeight在移动设备上会随浏览器工具栏(如Safari地址栏)的展开/收缩变化,组件挂载时的初始值可能不准确,导致originY计算错误。 - 滚动进度未达100%:浏览器在移动设备上的滚动边界计算存在误差,
scrollYProgress可能无法真正达到1,导致最后一个元素的动画无法触发完成。 - 动画属性冲突:同时使用
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
相关产品推荐
相关产品推荐

