
本文详解如何使用 react 与 css 实现平滑、无缝的垂直向上文字轮播动画,支持动态内容、无闪烁跳变,并让新旧条目在切换时自然重叠过渡。
本文详解如何使用 react 与 css 实现平滑、无缝的垂直向上文字轮播动画,支持动态内容、无闪烁跳变,并让新旧条目在切换时自然重叠过渡。
在构建 CMS 驱动的内容展示组件(如品牌标语轮播、实时更新的标签云或艺人名滚动栏)时,一个常见但易被忽视的细节是:滚动切换不能“硬切”,而应呈现“新项上浮、旧项淡出”的连续视觉流。本文将手把手带你实现真正接近专业 Marquee 的无缝向上滚动效果——关键不在于“动得快”,而在于“换得柔”。
核心思路:三态定位 + 循环索引映射
我们摒弃传统 @keyframes 动画或 marquee 标签(已废弃且不可控),转而采用 CSS 定位 + 类名驱动状态切换 的方案。每个文本项始终存在于 DOM 中,仅通过 transform: translateY() 和 opacity 控制其可视状态,共定义三种状态:
-
.slider-item:默认隐藏在视口下方(translateY(100%),opacity: 0) -
.slider-item.active:当前显示项,居中可见(translateY(0),opacity: 1) -
.slider-item.prev-active:上一帧的活跃项,正向上移出视口(translateY(-100%),opacity: 0)
⚠️ 关键难点在于:当 currentIndex 从 texts.length - 1 回到 0 时,“上一项”应自动指向数组末尾(即 texts[texts.length - 1]),而非索引 -1。因此我们使用模运算逻辑判断前一项:
(index + 1) % texts.length === currentIndex
该表达式确保循环闭环:当 currentIndex === 0,只有 index === texts.length - 1 的元素满足条件,完美触发“末尾项向上退出”的动画。
完整实现代码(React + CSS)
// TextSlider.js
import React, { useState, useEffect } from 'react';
const TextSlider = ({ texts }) => {
const [currentIndex, setCurrentIndex] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
setCurrentIndex(prev => (prev + 1) % texts.length);
}, 2700);
return () => clearInterval(interval);
}, [texts.length]);
return (
<div classname="text-slider">
{texts.map((text, index) => (
<div key="{index}" classname="{`slider-item" currentindex : texts.length="==">
<h1 style="{{" fontsize: margin:>{text}</h1>
<img style="max-width:90%" width: aspectratio: borderradius: src="https://upload.wikimedia.org/wikipedia/en/1/1e/Troye_Sivan_-_Bloom_%28Official_Album_Cover%29.png?x-oss-process=image/resize,p_40" alt="{text}">
</div>
))}
</div>
);
};
export default TextSlider;
/* styles.css */
.text-slider {
height: 140px; /* 建议 ≥ 单项高度,留出过渡空间 */
overflow: hidden;
position: relative;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.slider-item {
position: absolute;
width: 100%;
top: 0;
left: 0;
opacity: 0;
transform: translateY(100%);
transition: transform 0.5s cubic-bezier(0.33, 1, 0.68, 1),
opacity 0.5s ease;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 0.5rem;
pointer-events: none; /* 防止非 active 项干扰交互 */
}
.slider-item.active {
opacity: 1;
transform: translateY(0);
z-index: 2;
}
.slider-item.prev-active {
opacity: 0;
transform: translateY(-100%);
z-index: 1;
}
注意事项与优化建议
- ✅ 性能优先:所有动画均基于
transform和opacity,触发 GPU 加速,避免重排(reflow)。 - ✅ 响应式适配:若需适配移动端,可将
height改为min-height或用vh单位,并在@media中调整字体大小与间距。 - ⚠️ 无障碍访问:当前为纯视觉轮播,如需屏幕阅读器支持,建议添加
aria-live="polite"到.text-slider,并在状态变更时更新aria-label。 - ? 动态内容安全:
texts数组长度变化时,useEffect依赖[texts.length]可确保计时器自动重置,避免索引越界。 - ? 自定义缓动:示例中
cubic-bezier(0.33, 1, 0.68, 1)提供轻微“弹性收尾”,比标准ease更显自然;你可根据品牌调性调整贝塞尔曲线。
最终效果:文字如水流般持续上涌,新旧条目在视口中央短暂交叠(因 prev-active 在完全消失前已开始上移),彻底消除传统轮播的“闪断感”。无论是五条文案还是五十条,逻辑完全一致——真正的无限滚动,始于精准的状态建模。










