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

React图片无限滚动动画结束时出现跳变问题求助

无限图片滚动跳变问题修复方案

问题说明

尝试实现图片无限滚动动画时,动画周期结束后图片出现异常跳变,但相同逻辑在文本滚动上正常运行,目标是实现无缝循环的滚动效果。

现有代码

CSS 代码

.outter-scroller{
    overflow: hidden;
    -webkit-mask: linear-gradient(
        90deg,
        transparent,
        white 20%,
        white 80%,
        transparent
      );
      mask: linear-gradient(90deg, transparent, white 20%, white 80%, transparent);
    }
.inner-scroller{
    width: max-content;
    flex-wrap: nowrap;
    animation: scroll 20s linear infinite;
    animation-fill-mode: forwards;
}

JSX 代码

import { useEffect } from "react"

const sourceImages = ['src/assets/ic1.jpg', 'src/assets/ic2.jpg', 'src/assets/ic3.jpg', 'src/assets/ic4.jpg', 'src/assets/ic4.jpg', 'src/assets/ic5.jpg', 'src/assets/ic6.jpg', 'src/assets/ic7.jpg', 'src/assets/ic8.jpg']

const ImageSectionAnimatedAuto = () => {
    useEffect(() => {
        const handleAnimation = () => {
            const scrollerInner = document.querySelector('.inner-scroller')
            const scrollerContent = Array.from(scrollerInner.children)
            scrollerContent.forEach((item) => {
                const duplicatedItem = item.cloneNode(true)
                scrollerInner.appendChild(duplicatedItem)
            })
        }
        handleAnimation()
    }, [])
    return (
        <section className="bg-[#1d283a] w-full h-[100vh] flex flex-col justify-center items-center">
            <h1 className="text-6xl text-white text-center">Infinite scroll</h1>
            <div className="outter-scroller md:w-[60vw] w-[600px] py-10">
                <ul className="inner-scroller flex gap-5">
                    {["HTML", "CSS", "JAVASCRIPT", "SSG", "WEBDEV", "ANIMATION", "UI/UX"].map((item) => (
                        <li className="m-0 text-white rounded-2xl px-6 py-2 bg-slate-500 shadow-lg" key={item}>{item}</li>
                    ))}
                </ul>
            </div>
             <div className="outter-scroller py-5 flex w-[80vw] overflow-hidden">
                <div className="inner-scroller flex gap-5">
                    {sourceImages.map((image, _i) => (
                        <img key={_i} src={image} className="aspect-video w-[200px] rounded-xl" />
                    ))}
                </div>
            </div>
        </section>
    )
}

export default ImageSectionAnimatedAuto

问题根源与修复步骤

1. 缺失关键帧动画定义

现有CSS中没有@keyframes scroll,导致动画无执行逻辑;同时要确保位移距离等于原始内容的宽度,让动画结束时克隆内容刚好衔接,避免跳变。

2. 错误的animation-fill-mode设置

animation-fill-mode: forwards会让动画停在最后一帧,无限滚动不需要这个属性,直接移除。

3. 图片加载时机问题

图片是异步加载的,初始执行克隆逻辑时图片可能未加载完成,导致.inner-scroller宽度计算错误,动画位移偏差。需等待所有图片加载完成后再执行克隆和动画设置。

修复后的完整代码

CSS 代码

.outter-scroller{
    overflow: hidden;
    -webkit-mask: linear-gradient(
        90deg,
        transparent,
        white 20%,
        white 80%,
        transparent
      );
      mask: linear-gradient(90deg, transparent, white 20%, white 80%, transparent);
    }
.inner-scroller{
    width: max-content;
    flex-wrap: nowrap;
    animation: scroll 20s linear infinite;
}

/* 新增关键帧动画,位移距离为原始内容宽度的负值 */
@keyframes scroll {
    to {
        transform: translateX(-50%);
    }
}

JSX 代码

import { useEffect, useRef } from "react"

const sourceImages = ['src/assets/ic1.jpg', 'src/assets/ic2.jpg', 'src/assets/ic3.jpg', 'src/assets/ic4.jpg', 'src/assets/ic4.jpg', 'src/assets/ic5.jpg', 'src/assets/ic6.jpg', 'src/assets/ic7.jpg', 'src/assets/ic8.jpg']

const ImageSectionAnimatedAuto = () => {
    const imageScrollerRef = useRef(null)

    useEffect(() => {
        const handleAnimation = async () => {
            const scrollerInner = imageScrollerRef.current
            if (!scrollerInner) return

            // 等待所有图片加载完成
            const images = scrollerInner.querySelectorAll('img')
            await Promise.all(Array.from(images).map(img => {
                if (img.complete) return Promise.resolve()
                return new Promise(resolve => img.onload = resolve)
            }))

            // 克隆原始内容
            const scrollerContent = Array.from(scrollerInner.children)
            scrollerContent.forEach(item => {
                const duplicatedItem = item.cloneNode(true)
                scrollerInner.appendChild(duplicatedItem)
            })
        }
        handleAnimation()
    }, [])

    return (
        <section className="bg-[#1d283a] w-full h-[100vh] flex flex-col justify-center items-center">
            <h1 className="text-6xl text-white text-center">Infinite scroll</h1>
            <div className="outter-scroller md:w-[60vw] w-[600px] py-10">
                <ul className="inner-scroller flex gap-5">
                    {["HTML", "CSS", "JAVASCRIPT", "SSG", "WEBDEV", "ANIMATION", "UI/UX"].map((item) => (
                        <li className="m-0 text-white rounded-2xl px-6 py-2 bg-slate-500 shadow-lg" key={item}>{item}</li>
                    ))}
                </ul>
            </div>
             <div className="outter-scroller py-5 flex w-[80vw] overflow-hidden">
                {/* 使用ref获取图片滚动容器,避免全局选择器冲突 */}
                <div ref={imageScrollerRef} className="inner-scroller flex gap-5">
                    {sourceImages.map((image, _i) => (
                        <img key={_i} src={image} className="aspect-video w-[200px] rounded-xl" />
                    ))}
                </div>
            </div>
        </section>
    )
}

export default ImageSectionAnimatedAuto

额外说明

  • 使用transform: translateX(-50%)是因为克隆了一份原始内容,总宽度为原始的2倍,位移50%刚好让克隆内容衔接原始内容起点,实现无缝循环。
  • 使用useRef替代document.querySelector,避免多个.inner-scroller时的选择器冲突。
  • 等待图片加载完成后再克隆元素,确保容器宽度计算准确,动画位移无偏差。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 06:43:15