CSS keyframes实现滚动轮播 动画循环衔接存在延迟跳变问题
无缝自动轮播跳变问题修复
问题根源
你当前代码出现循环跳变有3个核心原因:
- 动画类错误添加到了每个独立的图片容器上,多个元素各自执行位移动画,无法实现衔接
- CSS伪元素选择器书写错误,
.slider::after匹配不到对应元素,两侧渐变遮罩失效 - 若使用CSS keyframes实现无限滚动,需要保证动画结束位置和起始位置的视觉状态完全一致,否则会出现瞬时跳回的断档感
方案一:修正后的纯CSS实现(无需JS)
这个方案不需要引入requestAnimationFrame,只要调整DOM结构和CSS规则即可实现无缝滚动:
- 新增统一的滚动轨道容器,把两份图片内容都放在轨道内,动画只添加到轨道元素上
- 动画位移距离设置为单份内容的总宽度,滚完100%时刚好第二份内容对齐初始位置,无视觉跳变
- 修正伪元素选择器错误
完整代码如下:
<div style="max-width: 800px;"> <div class="carousel-wrapper"> <div class="carousel-slider"> <!-- 统一的滚动轨道 --> <div class="slides-track"> <div class="image-container"> <a href="#"><img src="https://picsum.photos/id/10/200/85" alt="image1"></a> <a href="#"><img src="https://picsum.photos/id/11/200/85" alt="image2"></a> <a href="#"><img src="https://picsum.photos/id/12/200/85" alt="image3"></a> </div> <div class="image-container"> <a href="#"><img src="https://picsum.photos/id/10/200/85" alt="image1"></a> <a href="#"><img src="https://picsum.photos/id/11/200/85" alt="image2"></a> <a href="#"><img src="https://picsum.photos/id/12/200/85" alt="image3"></a> </div> </div> </div> </div> </div>
.carousel-wrapper { width: 100%; display: flex; align-items: center; justify-content: center; } .carousel-slider { height: 100px; position: relative; overflow: hidden; } .slides-track { height: 100px; display: flex; align-items: center; /* 动画时长根据内容宽度调整,速度保持均匀 */ animation: slideshow 5s linear infinite; } .image-container { display: flex; align-items: center; } .image-container img { height: 85px; padding: 0 8px; } @keyframes slideshow { 0% { transform: translateX(0); } 100% { /* 刚好位移一个图片容器的宽度,此时第二组图和初始位置重合 */ transform: translateX(-50%); } } /* 修正伪元素选择器 */ .carousel-slider::before, .carousel-slider::after { height: 100px; width: 50px; position: absolute; content: ""; z-index: 2; } .carousel-slider::before { left: 0; top: 0; background: linear-gradient(to right, #fff 0%, rgba(255,255,255,0) 100%); } .carousel-slider::after { right: 0; top: 0; background: linear-gradient(to left, #fff 0%, rgba(255,255,255,0) 100%); }
// 纯CSS方案不需要额外JS,移除原来给每个image-container加类的代码即可
方案二:基于requestAnimationFrame的JS实现
如果需要更灵活的滚动控制(比如鼠标悬停暂停、动态调整速度、增减内容),可以用requestAnimationFrame实现,逻辑如下:
- 初始化时获取单份内容宽度、轨道元素,设置滚动速度
- 每帧更新位移值,当位移超过单份内容宽度时直接重置为0,利用两份内容完全一致的特点实现无缝衔接
- 监听页面可见性变化,页面切走时暂停动画减少性能消耗
完整代码如下:
<div style="max-width: 800px;"> <div class="carousel-wrapper"> <div class="carousel-slider"> <div class="slides-track" id="slidesTrack"> <div class="image-container"> <a href="#"><img src="https://picsum.photos/id/10/200/85" alt="image1"></a> <a href="#"><img src="https://picsum.photos/id/11/200/85" alt="image2"></a> <a href="#"><img src="https://picsum.photos/id/12/200/85" alt="image3"></a> </div> <div class="image-container"> <a href="#"><img src="https://picsum.photos/id/10/200/85" alt="image1"></a> <a href="#"><img src="https://picsum.photos/id/11/200/85" alt="image2"></a> <a href="#"><img src="https://picsum.photos/id/12/200/85" alt="image3"></a> </div> </div> </div> </div> </div>
.carousel-wrapper { width: 100%; display: flex; align-items: center; justify-content: center; } .carousel-slider { height: 100px; position: relative; overflow: hidden; } .slides-track { height: 100px; display: flex; align-items: center; /* 用JS控制transform,不需要CSS动画 */ will-change: transform; } .image-container { display: flex; align-items: center; } .image-container img { height: 85px; padding: 0 8px; } .carousel-slider::before, .carousel-slider::after { height: 100px; width: 50px; position: absolute; content: ""; z-index: 2; } .carousel-slider::before { left: 0; top: 0; background: linear-gradient(to right, #fff 0%, rgba(255,255,255,0) 100%); } .carousel-slider::after { right: 0; top: 0; background: linear-gradient(to left, #fff 0%, rgba(255,255,255,0) 100%); }
const track = document.getElementById('slidesTrack'); const singleContentWidth = track.firstElementChild.offsetWidth; let offset = 0; // 滚动速度:像素/帧,可根据需求调整 const speed = 1; let rafId = null; function scroll() { offset += speed; // 位移超过单份内容宽度时重置,实现无缝 if (offset >= singleContentWidth) { offset = 0; } track.style.transform = `translateX(${-offset}px)`; rafId = requestAnimationFrame(scroll); } // 启动动画 rafId = requestAnimationFrame(scroll); // 页面隐藏时暂停,避免无效渲染 document.addEventListener('visibilitychange', () => { if (document.hidden) { cancelAnimationFrame(rafId); } else { rafId = requestAnimationFrame(scroll); } });
两种方案都需要保证两份滚动内容完全一致,这是无缝滚动的核心基础,重置位移时不会产生任何视觉跳变。
内容的提问来源于stack exchange,提问作者Parit Sawjani
相关产品推荐
相关产品推荐

