示例图


控制速度

设计思路
使用CSS动画和transform属性创建平滑的滚动效果,通过@keyframes控制动画过程,并利用:hover伪类实现悬停暂停功能。
代码
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSS 跑马灯效果 - 完整实现与优化</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
:root {
--primary-color: #4361ee;
--secondary-color: #3a0ca3;
--accent-color: #4cc9f0;
--dark-color: #1a1a2e;
--light-color: #f8f9fa;
--success-color: #4caf50;
--warning-color: #ff9800;
--danger-color: #f44336;
--animation-speed: 20s;
--border-radius: 12px;
--box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
--transition: all 0.3s ease;
}
body {
background: linear-gradient(135deg, var(--dark-color), var(--secondary-color));
color: var(--light-color);
min-height: 100vh;
padding: 40px 20px;
display: flex;
flex-direction: column;
align-items: center;
line-height: 1.6;
}
.container {
max-width: 1200px;
width: 100%;
margin: 0 auto;
}
header {
text-align: center;
margin-bottom: 50px;
padding: 30px;
background: rgba(255, 255, 255, 0.05);
border-radius: var(--border-radius);
backdrop-filter: blur(10px);
box-shadow: var(--box-shadow);
}
h1 {
font-size: 3rem;
margin-bottom: 15px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
background: linear-gradient(90deg, var(--accent-color), var(--primary-color));
-webkit-background-clip: text;
background-clip: text;
color: transparent;
}
.subtitle {
font-size: 1.2rem;
color: rgba(255, 255, 255, 0.8);
max-width: 800px;
margin: 0 auto;
}
h2 {
font-size: 1.8rem;
margin: 40px 0 20px;
color: var(--accent-color);
text-align: center;
position: relative;
padding-bottom: 10px;
}
h2::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100px;
height: 3px;
background: linear-gradient(90deg, transparent, var(--accent-color), transparent);
}
h3 {
font-size: 1.5rem;
margin: 25px 0 15px;
color: var(--accent-color);
}
.section {
margin-bottom: 60px;
}
.marquee-container {
overflow: hidden;
position: relative;
padding: 25px 0;
margin: 30px 0;
background: rgba(255, 255, 255, 0.05);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
border: 1px solid rgba(255, 255, 255, 0.1);
}
.marquee-container::before, .marquee-container::after {
content: '';
position: absolute;
top: 0;
width: 100px;
height: 100%;
z-index: 2;
}
.marquee-container::before {
left: 0;
background: linear-gradient(90deg, rgba(26, 26, 46, 0.9), transparent);
}
.marquee-container::after {
right: 0;
background: linear-gradient(90deg, transparent, rgba(26, 26, 46, 0.9));
}
.marquee {
display: flex;
width: max-content;
animation: marquee var(--animation-speed) linear infinite;
}
.marquee:hover {
animation-play-state: paused;
}
.text-marquee {
font-size: 2.5rem;
font-weight: bold;
white-space: nowrap;
}
.text-marquee span {
padding: 0 30px;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
}
.text-marquee i {
margin-right: 10px;
color: var(--accent-color);
}
.image-marquee {
display: flex;
}
.image-item {
width: 200px;
height: 150px;
margin: 0 15px;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.4);
transition: var(--transition);
position: relative;
}
.image-item:hover {
transform: scale(1.05);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.6);
}
.image-item img {
width: 100%;
height: 100%;
object-fit: cover;
}
.mixed-marquee {
display: flex;
}
.mixed-item {
display: flex;
align-items: center;
margin: 0 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 10px;
padding: 15px;
min-width: 350px;
transition: var(--transition);
}
.mixed-item:hover {
background: rgba(255, 255, 255, 0.15);
transform: translateY(-5px);
}
.mixed-icon {
font-size: 2.5rem;
margin-right: 15px;
color: var(--accent-color);
}
.mixed-content {
flex: 1;
}
.mixed-title {
font-weight: bold;
font-size: 1.2rem;
margin-bottom: 5px;
}
.mixed-desc {
font-size: 0.9rem;
color: rgba(255, 255, 255, 0.7);
}
.marquee-reverse {
animation-direction: reverse;
}
.marquee-fast {
animation-duration: 15s;
}
.marquee-slow {
animation-duration: 30s;
}
.controls {
display: flex;
justify-content: center;
gap: 15px;
margin: 20px 0;
flex-wrap: wrap;
}
button {
padding: 12px 25px;
background: var(--primary-color);
color: white;
border: none;
border-radius: 50px;
font-weight: bold;
cursor: pointer;
transition: var(--transition);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
display: flex;
align-items: center;
gap: 8px;
}
button:hover {
background: var(--secondary-color);
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
}
button.active {
background: var(--accent-color);
color: var(--dark-color);
}
.explanation {
background: rgba(255, 255, 255, 0.05);
padding: 30px;
border-radius: var(--border-radius);
margin: 30px 0;
backdrop-filter: blur(5px);
box-shadow: var(--box-shadow);
}
.code-example {
background: rgba(0, 0, 0, 0.7);
color: #fff;
padding: 20px;
border-radius: 10px;
margin: 20px 0;
font-family: 'Courier New', monospace;
overflow-x: auto;
position: relative;
border-left: 4px solid var(--accent-color);
}
.code-example pre {
margin: 0;
}
.code-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 10px;
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
}
.code-title {
font-weight: bold;
color: var(--accent-color);
}
.copy-btn {
background: rgba(255, 255, 255, 0.1);
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
cursor: pointer;
font-size: 0.8rem;
transition: var(--transition);
}
.copy-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
.note {
background: rgba(76, 201, 240, 0.1);
padding: 15px;
border-radius: 8px;
margin: 15px 0;
border-left: 4px solid var(--accent-color);
}
.features {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 20px;
margin: 30px 0;
}
.feature-card {
background: rgba(255, 255, 255, 0.05);
padding: 25px;
border-radius: var(--border-radius);
text-align: center;
transition: var(--transition);
box-shadow: var(--box-shadow);
}
.feature-card:hover {
transform: translateY(-5px);
background: rgba(255, 255, 255, 0.08);
}
.feature-icon {
font-size: 2.5rem;
color: var(--accent-color);
margin-bottom: 15px;
}
.feature-title {
font-size: 1.3rem;
margin-bottom: 10px;
color: var(--accent-color);
}
.implementation-steps {
margin: 30px 0;
}
.step {
margin-bottom: 25px;
padding: 20px;
background: rgba(255, 255, 255, 0.05);
border-radius: var(--border-radius);
}
.step-number {
display: inline-block;
width: 30px;
height: 30px;
background: var(--accent-color);
color: var(--dark-color);
border-radius: 50%;
text-align: center;
line-height: 30px;
font-weight: bold;
margin-right: 10px;
}
footer {
margin-top: 60px;
text-align: center;
color: rgba(255, 255, 255, 0.7);
font-size: 0.9rem;
padding: 20px;
border-top: 1px solid rgba(255, 255, 255, 0.1);
width: 100%;
}
/* 动画定义 */
@keyframes marquee {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.8s ease forwards;
}
/* 响应式设计 */
@media (max-width: 768px) {
h1 {
font-size: 2.2rem;
}
.text-marquee {
font-size: 1.8rem;
}
.image-item {
width: 160px;
height: 120px;
}
.mixed-item {
min-width: 280px;
}
.features {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
h1 {
font-size: 1.8rem;
}
.text-marquee {
font-size: 1.4rem;
}
.image-item {
width: 120px;
height: 90px;
}
.mixed-item {
min-width: 220px;
padding: 10px;
}
button {
padding: 10px 20px;
font-size: 0.9rem;
}
}
</style>
</head>
<body>
<div class="container">
<header class="fade-in">
<h1>CSS 跑马灯效果</h1>
<p class="subtitle">纯CSS实现的文字、图片和混合内容跑马灯,支持暂停、速度控制和多种动画效果</p>
</header>
<section class="section fade-in">
<h2>文字跑马灯</h2>
<div class="marquee-container">
<div class="marquee text-marquee">
<span><i class="fas fa-star"></i> 欢迎来到CSS跑马灯世界!</span>
<span><i class="fas fa-code"></i> 纯CSS实现</span>
<span><i class="fas fa-bolt"></i> 无需JavaScript!</span>
<span><i class="fas fa-pause"></i> 可悬停暂停</span>
<span><i class="fas fa-star"></i> 欢迎来到CSS跑马灯世界!</span>
<span><i class="fas fa-code"></i> 纯CSS实现</span>
<span><i class="fas fa-bolt"></i> 无需JavaScript!</span>
<span><i class="fas fa-pause"></i> 可悬停暂停</span>
</div>
</div>
</section>
<section class="section fade-in">
<h2>图片跑马灯</h2>
<div class="marquee-container">
<div class="marquee image-marquee">
<div class="image-item"><img src="https://picsum.photos/200/150?random=1" alt="示例图片"></div>
<div class="image-item"><img src="https://picsum.photos/200/150?random=2" alt="示例图片"></div>
<div class="image-item"><img src="https://picsum.photos/200/150?random=3" alt="示例图片"></div>
<div class="image-item"><img src="https://picsum.photos/200/150?random=4" alt="示例图片"></div>
<div class="image-item"><img src="https://picsum.photos/200/150?random=5" alt="示例图片"></div>
<div class="image-item"><img src="https://picsum.photos/200/150?random=6" alt="示例图片"></div>
<div class="image-item"><img src="https://picsum.photos/200/150?random=7" alt="示例图片"></div>
<div class="image-item"><img src="https://picsum.photos/200/150?random=8" alt="示例图片"></div>
</div>
</div>
</section>
<section class="section fade-in">
<h2>混合内容跑马灯</h2>
<div class="marquee-container">
<div class="marquee mixed-marquee">
<div class="mixed-item">
<div class="mixed-icon"><i class="fas fa-palette"></i></div>
<div class="mixed-content">
<div class="mixed-title">CSS动画</div>
<div class="mixed-desc">使用@keyframes创建平滑动画效果</div>
</div>
</div>
<div class="mixed-item">
<div class="mixed-icon"><i class="fas fa-magic"></i></div>
<div class="mixed-content">
<div class="mixed-title">悬停暂停</div>
<div class="mixed-desc">鼠标悬停时动画暂停,提升交互体验</div>
</div>
</div>
<div class="mixed-item">
<div class="mixed-icon"><i class="fas fa-tachometer-alt"></i></div>
<div class="mixed-content">
<div class="mixed-title">速度控制</div>
<div class="mixed-desc">可调节动画速度,适应不同场景</div>
</div>
</div>
<div class="mixed-item">
<div class="mixed-icon"><i class="fas fa-sync-alt"></i></div>
<div class="mixed-content">
<div class="mixed-title">无缝循环</div>
<div class="mixed-desc">通过内容复制实现无缝滚动效果</div>
</div>
</div>
<div class="mixed-item">
<div class="mixed-icon"><i class="fas fa-palette"></i></div>
<div class="mixed-content">
<div class="mixed-title">CSS动画</div>
<div class="mixed-desc">使用@keyframes创建平滑动画效果</div>
</div>
</div>
<div class="mixed-item">
<div class="mixed-icon"><i class="fas fa-magic"></i></div>
<div class="mixed-content">
<div class="mixed-title">悬停暂停</div>
<div class="mixed-desc">鼠标悬停时动画暂停,提升交互体验</div>
</div>
</div>
<div class="mixed-item">
<div class="mixed-icon"><i class="fas fa-tachometer-alt"></i></div>
<div class="mixed-content">
<div class="mixed-title">速度控制</div>
<div class="mixed-desc">可调节动画速度,适应不同场景</div>
</div>
</div>
<div class="mixed-item">
<div class="mixed-icon"><i class="fas fa-sync-alt"></i></div>
<div class="mixed-content">
<div class="mixed-title">无缝循环</div>
<div class="mixed-desc">通过内容复制实现无缝滚动效果</div>
</div>
</div>
</div>
</div>
</section>
<section class="section fade-in">
<h2>反向跑马灯</h2>
<div class="marquee-container">
<div class="marquee text-marquee marquee-reverse">
<span><i class="fas fa-arrow-left"></i> 这个跑马灯是反向滚动的!</span>
<span><i class="fas fa-rocket"></i> CSS动画强大!</span>
<span><i class="fas fa-mouse-pointer"></i> 尝试悬停暂停效果</span>
<span><i class="fas fa-arrow-left"></i> 这个跑马灯是反向滚动的!</span>
<span><i class="fas fa-rocket"></i> CSS动画强大!</span>
<span><i class="fas fa-mouse-pointer"></i> 尝试悬停暂停效果</span>
</div>
</div>
</section>
<section class="section fade-in">
<h2>速度控制</h2>
<div class="controls">
<button id="speed-fast" onclick="changeSpeed('fast')">
<i class="fas fa-tachometer-alt-fast"></i> 加速
</button>
<button id="speed-normal" onclick="changeSpeed('normal')" class="active">
<i class="fas fa-tachometer-alt"></i> 正常速度
</button>
<button id="speed-slow" onclick="changeSpeed('slow')">
<i class="fas fa-tachometer-alt-slow"></i> 减速
</button>
<button onclick="toggleAnimation()" id="toggle-btn">
<i class="fas fa-pause"></i> 暂停所有动画
</button>
</div>
</section>
<section class="section fade-in">
<h2>核心特性</h2>
<div class="features">
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-code"></i>
</div>
<div class="feature-title">纯CSS实现</div>
<p>无需JavaScript,仅使用CSS动画和转换属性</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-pause-circle"></i>
</div>
<div class="feature-title">悬停暂停</div>
<p>鼠标悬停时暂停动画,提供更好的交互体验</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-tachometer-alt"></i>
</div>
<div class="feature-title">速度控制</div>
<p>可动态调整动画速度,适应不同需求</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-mobile-alt"></i>
</div>
<div class="feature-title">响应式设计</div>
<p>适配各种屏幕尺寸,移动设备友好</p>
</div>
</div>
</section>
<section class="section fade-in">
<h2>实现原理</h2>
<div class="explanation">
<p>CSS跑马灯使用<code>@keyframes</code>动画和<code>transform: translateX()</code>实现水平移动效果。</p>
<div class="implementation-steps">
<h3>实现步骤</h3>
<div class="step">
<div class="step-number">1</div>
<h3>创建动画关键帧</h3>
<p>使用@keyframes定义从0%到100%的动画过程,通过transform: translateX(-50%)实现元素向左移动50%的宽度。</p>
<div class="code-example">
<div class="code-header">
<div class="code-title">CSS 动画关键帧</div>
<button class="copy-btn" onclick="copyCode(this)">复制代码</button>
</div>
<pre><code>@keyframes marquee {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}</code></pre>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<h3>应用动画到元素</h3>
<p>将动画应用到跑马灯元素,设置无限循环和线性时间函数。</p>
<div class="code-example">
<div class="code-header">
<div class="code-title">CSS 动画应用</div>
<button class="copy-btn" onclick="copyCode(this)">复制代码</button>
</div>
<pre><code>.marquee {
display: flex;
width: max-content;
animation: marquee 20s linear infinite;
}</code></pre>
</div>
</div>
<div class="step">
<div class="step-number">3</div>
<h3>实现悬停暂停</h3>
<p>通过:hover伪类和animation-play-state属性实现悬停时暂停动画。</p>
<div class="code-example">
<div class="code-header">
<div class="code-title">悬停暂停效果</div>
<button class="copy-btn" onclick="copyCode(this)">复制代码</button>
</div>
<pre><code>.marquee:hover {
animation-play-state: paused;
}</code></pre>
</div>
</div>
<div class="step">
<div class="step-number">4</div>
<h3>实现无缝滚动</h3>
<p>通过复制内容实现无缝滚动效果。当内容滚动到一半时,原始内容开始处刚好接上。</p>
<div class="code-example">
<div class="code-header">
<div class="code-title">HTML 结构示例</div>
<button class="copy-btn" onclick="copyCode(this)">复制代码</button>
</div>
<pre><code><div class="marquee">
<span>内容1</span>
<span>内容2</span>
<span>内容3</span>
<!-- 重复内容实现无缝滚动 -->
<span>内容1</span>
<span>内容2</span>
<span>内容3</span>
</div></code></pre>
</div>
</div>
</div>
<div class="note">
<p><strong>技术要点:</strong> 为了实现无缝滚动,需要复制一份内容(或使内容足够长),这样当内容滚动到一半时,原始内容开始处刚好接上,形成无缝循环效果。</p>
</div>
<h3>JavaScript 控制功能</h3>
<p>通过JavaScript动态修改CSS类来实现速度控制功能。</p>
<div class="code-example">
<div class="code-header">
<div class="code-title">JavaScript 速度控制</div>
<button class="copy-btn" onclick="copyCode(this)">复制代码</button>
</div>
<pre><code>function changeSpeed(speed) {
const marquees = document.querySelectorAll('.marquee');
marquees.forEach(marquee => {
marquee.classList.remove('marquee-fast', 'marquee-slow');
if (speed === 'fast') {
marquee.classList.add('marquee-fast');
} else if (speed === 'slow') {
marquees.classList.add('marquee-slow');
}
});
}</code></pre>
</div>
<div class="code-example">
<div class="code-header">
<div class="code-title">CSS 速度类定义</div>
<button class="copy-btn" onclick="copyCode(this)">复制代码</button>
</div>
<pre><code>.marquee-fast {
animation-duration: 15s;
}
.marquee-slow {
animation-duration: 30s;
}
.marquee-reverse {
animation-direction: reverse;
}</code></pre>
</div>
</div>
</section>
<section class="section fade-in">
<h2>应用场景</h2>
<div class="explanation">
<p>CSS跑马灯效果可以在多种场景下使用:</p>
<div class="features">
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-newspaper"></i>
</div>
<div class="feature-title">新闻头条</div>
<p>用于展示重要新闻或公告,吸引用户注意力</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-shopping-cart"></i>
</div>
<div class="feature-title">产品展示</div>
<p>在电商网站中展示热门产品或促销信息</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-award"></i>
</div>
<div class="feature-title">荣誉展示</div>
<p>展示企业荣誉、合作伙伴或客户评价</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-images"></i>
</div>
<div class="feature-title">图片画廊</div>
<p>创建动态图片展示效果,增强视觉吸引力</p>
</div>
</div>
<div class="note">
<p><strong>最佳实践:</strong> 在使用跑马灯效果时,确保内容简洁明了,滚动速度适中,避免给用户带来不适的体验。同时提供暂停功能,方便用户仔细阅读内容。</p>
</div>
</div>
</section>
</div>
<footer>
<p>CSS 跑马灯效果示例 © 2023 | 使用纯CSS实现,无需JavaScript</p>
</footer>
<script>
function changeSpeed(speed) {
const marquees = document.querySelectorAll('.marquee');
// 更新按钮状态
document.getElementById('speed-fast').classList.remove('active');
document.getElementById('speed-normal').classList.remove('active');
document.getElementById('speed-slow').classList.remove('active');
document.getElementById(`speed-${speed}`).classList.add('active');
marquees.forEach(marquee => {
marquee.classList.remove('marquee-fast', 'marquee-slow');
if (speed === 'fast') {
marquee.classList.add('marquee-fast');
} else if (speed === 'slow') {
marquee.classList.add('marquee-slow');
}
});
}
function toggleAnimation() {
const marquees = document.querySelectorAll('.marquee');
const button = document.getElementById('toggle-btn');
const icon = button.querySelector('i');
let isPaused = false;
marquees.forEach(marquee => {
if (marquee.style.animationPlayState === 'paused') {
marquee.style.animationPlayState = 'running';
icon.className = 'fas fa-pause';
button.innerHTML = '<i class="fas fa-pause"></i> 暂停所有动画';
} else {
marquee.style.animationPlayState = 'paused';
icon.className = 'fas fa-play';
button.innerHTML = '<i class="fas fa-play"></i> 播放所有动画';
isPaused = true;
}
});
if (isPaused) {
button.innerHTML = '<i class="fas fa-play"></i> 播放所有动画';
}
}
function copyCode(button) {
const codeElement = button.closest('.code-example').querySelector('code');
const textArea = document.createElement('textarea');
textArea.value = codeElement.textContent;
document.body.appendChild(textArea);
textArea.select();
document.execCommand('copy');
document.body.removeChild(textArea);
// 显示复制成功提示
const originalText = button.textContent;
button.textContent = '已复制!';
setTimeout(() => {
button.textContent = originalText;
}, 2000);
}
// 添加滚动动画
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in');
}
});
}, observerOptions);
document.querySelectorAll('.section').forEach(section => {
observer.observe(section);
});
</script>
</body>
</html>
实现原理
创建动画关键帧
使用@keyframes定义从0%到100%的动画过程,通过transform: translateX(-50%)实现元素向左移动50%的宽度。
css
CSS 动画关键帧
@keyframes marquee {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
应用动画到元素
将动画应用到跑马灯元素,设置无限循环和线性时间函数。
css
.marquee {
display: flex;
width: max-content;
animation: marquee 20s linear infinite;
}
实现悬停暂停
通过:hover伪类和animation-play-state属性实现悬停时暂停动画。
css
.marquee:hover {
animation-play-state: paused;
}
实现无缝滚动
通过复制内容实现无缝滚动效果。当内容滚动到一半时,原始内容开始处刚好接上。
css
<div class="marquee">
<span>内容1</span>
<span>内容2</span>
<span>内容3</span>
<!-- 重复内容实现无缝滚动 -->
<span>内容1</span>
<span>内容2</span>
<span>内容3</span>
</div>
技术要点: 为了实现无缝滚动,需要复制一份内容(或使内容足够长),这样当内容滚动到一半时,原始内容开始处刚好接上,形成无缝循环效果。
JavaScript 控制功能
通过JavaScript动态修改CSS类来实现速度控制功能。
javascript
function changeSpeed(speed) {
const marquees = document.querySelectorAll('.marquee');
marquees.forEach(marquee => {
marquee.classList.remove('marquee-fast', 'marquee-slow');
if (speed === 'fast') {
marquee.classList.add('marquee-fast');
} else if (speed === 'slow') {
marquees.classList.add('marquee-slow');
}
});
}
CSS 速度类定义
css
.marquee-fast {
animation-duration: 15s;
}
.marquee-slow {
animation-duration: 30s;
}
.marquee-reverse {
animation-direction: reverse;
}
使用到的技术点
1. CSS 动画技术
-
@keyframes 关键帧动画
-
定义动画从0%到100%的过程
-
使用transform: translateX(-50%)实现水平移动
-
-
animation 属性
-
animation: marquee 20s linear infinite- 设置动画名称、时长、时间函数和循环次数 -
animation-play-state: paused- 控制动画播放状态 -
animation-direction: reverse- 反向播放动画
-
2. CSS 布局技术
-
Flexbox 布局
-
display: flex- 创建弹性容器 -
width: max-content- 使容器宽度适应内容
-
-
CSS Grid 布局
- 用于特性展示区域的响应式网格布局
-
定位技术
-
position: relative/absolute- 相对和绝对定位 -
伪元素(::before, ::after)创建渐变遮罩
-
3. 现代 CSS 特性
-
CSS 自定义属性 (CSS Variables)
- 定义主题颜色、动画速度等可复用值
-
CSS 渐变
linear-gradient()- 创建背景渐变和文本渐变效果
-
backdrop-filter
- 创建毛玻璃效果
-
object-fit
- 控制图片在容器中的填充方式
4. 响应式设计
-
媒体查询 (@media)
- 针对不同屏幕尺寸调整样式
-
相对单位
- 使用rem、百分比等相对单位
-
flex-wrap
- 控制元素在空间不足时的换行行为
5. JavaScript 交互
-
DOM 操作
-
querySelectorAll()- 选择多个元素 -
classList.add/remove()- 动态添加/移除CSS类
-
-
事件处理
-
onclick- 按钮点击事件 -
onhover- 鼠标悬停事件(通过CSS实现)
-
-
Intersection Observer API
- 实现滚动时的淡入动画效果
6. 视觉效果技术
-
transform 变换
-
scale()- 缩放效果 -
translateX/Y()- 平移效果
-
-
transition 过渡
- 创建平滑的状态变化
-
box-shadow
- 添加阴影效果增强立体感
-
text-shadow
- 文字阴影效果
注意事项
1. 性能优化
-
使用 transform 和 opacity
-
这些属性不会触发重排,动画性能更好
-
避免使用会触发重排的属性(如width、height、left、top)
-
-
硬件加速
- transform和opacity属性可以利用GPU加速
-
动画复杂度
- 保持动画简单,避免过多复杂的动画同时运行
2. 可访问性
-
动画暂停功能
-
必须提供暂停动画的方法(如悬停暂停)
-
考虑前庭障碍用户,避免引起不适
-
-
键盘导航
- 确保所有功能可以通过键盘操作
-
屏幕阅读器
- 使用适当的ARIA标签和语义化HTML
3. 浏览器兼容性
-
前缀使用
- 某些CSS属性可能需要浏览器前缀(-webkit-, -moz-, -ms-)
-
渐进增强
- 在不支持某些特性的浏览器中提供降级方案
-
功能检测
- 使用@supports规则检测CSS特性支持
4. 用户体验
-
动画速度
-
速度不宜过快,确保内容可读
-
提供速度调节选项
-
-
暂停时机
-
用户与内容交互时应暂停动画
-
页面不可见时(如标签页切换)应暂停动画
-
-
内容重要性
- 跑马灯内容不应是关键信息,用户可能错过
5. 内容设计
-
无缝循环
-
确保内容复制足够形成无缝循环
-
计算合适的动画时长和内容长度比例
-
-
响应式内容
-
在不同屏幕尺寸下调整内容大小和布局
-
移动设备上可能需要简化内容
-
6. 代码维护
-
CSS 组织
-
使用CSS变量统一管理颜色、尺寸等
-
合理分组相关样式
-
-
命名规范
-
使用有意义的类名和ID
-
遵循BEM或其他命名约定
-
-
注释和文档
-
为复杂代码添加详细注释
-
提供使用说明和示例
-
7. 移动端优化
-
触摸交互
-
考虑触摸设备上的交互方式
-
确保按钮和交互元素有足够的触摸目标大小
-
-
性能考虑
-
移动设备性能有限,优化动画复杂度
-
考虑在低端设备上减少动画效果
-
8. 无障碍设计
-
减少运动
- 提供减少动画的选项(如通过 prefers-reduced-motion 媒体查询)
-
焦点管理
- 确保焦点在交互过程中正确移动
-
语义化结构
- 使用恰当的HTML标签传达内容结构
最佳实践总结
-
性能优先:优先使用transform和opacity实现动画
-
用户控制:始终提供暂停和速度控制选项
-
渐进增强:在不支持某些特性的浏览器中提供可用的降级方案
-
响应式设计:确保在所有设备上都有良好的体验
-
无障碍访问:考虑所有用户的需求,包括有特殊需求的用户
-
内容适宜:跑马灯适合展示非关键信息,重要内容应有静态展示方式
html
伪代码
页面结构
<div class="image-marquee__group image-marquee__hover">
{{#for _ in 1 | range(4)}}
<div
style="--animation-time: {{block.settings.select_speed}}s;"
class="image-marquee__item {{#if block.settings.select_direction == '1'}}image-marquee-reverse{{/if}}">
<div
style="
{{#if block.settings.switch_bgColor}}
border-radius:{{block.settings.range_radius}}px;
background-color:rgba({{block.settings.color_bgColor.red}},{{block.settings.color_bgColor.green}},{{block.settings.color_bgColor.blue}},{{block.settings.range_mask}}%);
{{/if}}
"
class="image-marquee__text" >
{{{block.settings.text}}}
</div>
<div
style="width: {{block.settings.range_height}}px;"
class="image-marquee__image">
{{#if block.settings.image}}
{{#component "image" data=block.settings.image /}}
{{#else/}}
{{#placeholder_svg "image" class="empty-image-class" /}}
{{/if}}
</div>
<div
style="
{{#if block.settings.switch_bgColor}}
border-radius:{{block.settings.range_radius}}px;
background-color:rgba({{block.settings.color_bgColor.red}},{{block.settings.color_bgColor.green}},{{block.settings.color_bgColor.blue}},{{block.settings.range_mask}}%);
{{/if}}
"
class="image-marquee__text">
{{{block.settings.text}}}
</div>
<div
style="width: {{block.settings.range_height}}px;"
class="image-marquee__image"
{{{block.shopline_attributes}}}>
{{#if block.settings.image}}
{{#component "image" data=block.settings.image /}}
{{#else/}}
{{#placeholder_svg "image" class="empty-image-class" /}}
{{/if}}
</div>
</div>
{{/for}}
</div>
样式动画
.image-marquee-wrap {
display: flex;
align-items: center;
overflow: hidden;
width: 100%;
}
.image-marquee__group{
font-size: 36px;
font-weight: bold;
color: #232833;
width: max-content;
display: flex;
align-items: center;
}
.image-marquee__item{
display: flex;
align-items: center;
white-space: nowrap;
width: fit-content;
// 动画
animation: move var(--animation-time) linear infinite;
}
.image-marquee__text{
color: rgb(var(--color-text));
}
// 反向
.image-marquee-reverse {
animation-direction: reverse;
}
// 暂停
.image-marquee__hover:hover .image-marquee__item{
animation-play-state: paused;
}
@keyframes move {
/* 0% {
transform: translateZ(0)
}
to {
transform: translate3d(-100%,0,0)
} */
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}