Appearance
过渡与动画
本章将介绍CSS过渡和动画,让网页元素能够平滑地改变样式,创建流畅的动画效果。
CSS过渡(Transition)
过渡允许CSS属性值在一定时间内平滑地变化。
基本语法
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>过渡基本语法</title>
<style>
.box {
width: 100px;
height: 100px;
background-color: #4CAF50;
margin: 20px;
/* transition: 属性名 持续时间 时间函数 延迟时间; */
transition: all 0.3s ease;
}
.box:hover {
width: 200px;
background-color: #2196F3;
}
/* 分解写法 */
.box-detailed {
width: 100px;
height: 100px;
background-color: #FF9800;
margin: 20px;
transition-property: width, background-color; /* 过渡属性 */
transition-duration: 0.5s; /* 持续时间 */
transition-timing-function: ease-in-out; /* 时间函数 */
transition-delay: 0.1s; /* 延迟时间 */
}
.box-detailed:hover {
width: 200px;
background-color: #E91E63;
}
</style>
</head>
<body>
<p>鼠标悬停查看过渡效果:</p>
<div class="box">简写形式</div>
<div class="box-detailed">分解写法</div>
</body>
</html>transition-property(过渡属性)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>transition-property示例</title>
<style>
.box {
width: 100px;
height: 100px;
background-color: #4CAF50;
margin: 20px;
display: inline-block;
vertical-align: top;
}
/* 过渡所有属性 */
.all {
transition: all 0.3s;
}
/* 只过渡宽度 */
.width-only {
transition: width 0.3s;
}
/* 过渡多个属性 */
.multiple {
transition: width 0.3s, background-color 0.3s, transform 0.3s;
}
/* 不过渡任何属性 */
.none {
transition: none;
}
.box:hover {
width: 150px;
height: 150px;
background-color: #2196F3;
transform: rotate(10deg);
}
</style>
</head>
<body>
<div class="box all">all:所有属性过渡</div>
<div class="box width-only">width:只有宽度过渡</div>
<div class="box multiple">多属性:指定属性过渡</div>
<div class="box none">none:无过渡</div>
</body>
</html>transition-duration(持续时间)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>transition-duration示例</title>
<style>
.box {
width: 100px;
height: 50px;
background-color: #4CAF50;
margin: 10px;
transition: width;
}
.box:hover {
width: 300px;
}
/* 不同持续时间 */
.fast { transition-duration: 0.1s; }
.normal { transition-duration: 0.3s; }
.slow { transition-duration: 1s; }
.slower { transition-duration: 2s; }
/* 不同单位 */
.ms { transition-duration: 500ms; } /* 毫秒 */
.s { transition-duration: 0.5s; } /* 秒 */
</style>
</head>
<body>
<div class="box fast">0.1秒</div>
<div class="box normal">0.3秒</div>
<div class="box slow">1秒</div>
<div class="box slower">2秒</div>
</body>
</html>transition-timing-function(时间函数)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>时间函数示例</title>
<style>
.container {
width: 400px;
height: 30px;
background-color: #f5f5f5;
margin: 10px;
position: relative;
}
.box {
width: 30px;
height: 30px;
background-color: #4CAF50;
position: absolute;
left: 0;
transition: left 2s;
}
.container:hover .box {
left: calc(100% - 30px);
}
/* 预设时间函数 */
.linear { transition-timing-function: linear; } /* 线性 */
.ease { transition-timing-function: ease; } /* 默认,快开始慢结束 */
.ease-in { transition-timing-function: ease-in; } /* 慢开始 */
.ease-out { transition-timing-function: ease-out; } /* 慢结束 */
.ease-in-out { transition-timing-function: ease-in-out; } /* 慢开始慢结束 */
/* 贝塞尔曲线 */
.cubic-bezier {
transition-timing-function: cubic-bezier(0.68, -0.55, 0.27, 1.55);
}
/* 步进函数 */
.steps-start { transition-timing-function: steps(5, start); }
.steps-end { transition-timing-function: steps(5, end); }
</style>
</head>
<body>
<p>鼠标悬停在容器上查看动画效果:</p>
<div class="container">
<div class="box linear"></div>
<span style="margin-left: 40px;">linear(线性)</span>
</div>
<div class="container">
<div class="box ease"></div>
<span style="margin-left: 40px;">ease(默认)</span>
</div>
<div class="container">
<div class="box ease-in"></div>
<span style="margin-left: 40px;">ease-in(慢开始)</span>
</div>
<div class="container">
<div class="box ease-out"></div>
<span style="margin-left: 40px;">ease-out(慢结束)</span>
</div>
<div class="container">
<div class="box ease-in-out"></div>
<span style="margin-left: 40px;">ease-in-out</span>
</div>
<div class="container">
<div class="box cubic-bezier"></div>
<span style="margin-left: 40px;">cubic-bezier(弹跳效果)</span>
</div>
</body>
</html>transition-delay(延迟时间)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>transition-delay示例</title>
<style>
.box {
width: 100px;
height: 100px;
background-color: #4CAF50;
margin: 10px;
display: inline-block;
transition: all 0.3s;
}
.box:hover {
background-color: #2196F3;
transform: scale(1.1);
}
/* 不同延迟时间 */
.delay-0 { transition-delay: 0s; }
.delay-1 { transition-delay: 0.1s; }
.delay-2 { transition-delay: 0.2s; }
.delay-3 { transition-delay: 0.3s; }
.delay-4 { transition-delay: 0.4s; }
</style>
</head>
<body>
<p>鼠标悬停查看延迟效果:</p>
<div class="box delay-0">延迟0s</div>
<div class="box delay-1">延迟0.1s</div>
<div class="box delay-2">延迟0.2s</div>
<div class="box delay-3">延迟0.3s</div>
<div class="box delay-4">延迟0.4s</div>
</body>
</html>过渡示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>过渡效果示例</title>
<style>
.container {
padding: 20px;
}
/* 按钮悬停效果 */
.btn {
padding: 12px 24px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
transition: all 0.3s ease;
}
.btn:hover {
background-color: #45a049;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}
/* 卡片悬停效果 */
.card {
width: 200px;
padding: 20px;
background-color: white;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
transition: all 0.3s ease;
display: inline-block;
margin: 10px;
}
.card:hover {
transform: translateY(-10px);
box-shadow: 0 10px 20px rgba(0,0,0,0.2);
}
/* 图片放大效果 */
.image-container {
width: 200px;
height: 150px;
overflow: hidden;
border-radius: 8px;
display: inline-block;
margin: 10px;
}
.image-container img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.image-container:hover img {
transform: scale(1.1);
}
/* 展开菜单 */
.menu {
width: 200px;
}
.menu-item {
padding: 10px 15px;
background-color: #f5f5f5;
border-bottom: 1px solid #ddd;
cursor: pointer;
transition: all 0.3s ease;
}
.menu-item:hover {
background-color: #4CAF50;
color: white;
padding-left: 25px;
}
/* 输入框聚焦效果 */
.input-group {
margin: 10px;
}
.input-group input {
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 16px;
transition: all 0.3s ease;
}
.input-group input:focus {
outline: none;
border-color: #4CAF50;
box-shadow: 0 0 10px rgba(76, 175, 80, 0.3);
}
</style>
</head>
<body>
<h3>按钮效果</h3>
<button class="btn">悬停查看效果</button>
<h3>卡片效果</h3>
<div class="card">
<h4>卡片标题</h4>
<p>悬停查看效果</p>
</div>
<h3>图片放大</h3>
<div class="image-container">
<img src="https://picsum.photos/200/150" alt="图片">
</div>
<h3>菜单效果</h3>
<div class="menu">
<div class="menu-item">首页</div>
<div class="menu-item">产品</div>
<div class="menu-item">关于</div>
<div class="menu-item">联系</div>
</div>
<h3>输入框效果</h3>
<div class="input-group">
<input type="text" placeholder="点击聚焦查看效果">
</div>
</body>
</html>CSS动画(Animation)
动画可以创建更复杂的序列动画效果。
@keyframes规则
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>keyframes规则示例</title>
<style>
/* 定义动画 */
@keyframes change-color {
/* 起始状态 */
from {
background-color: #4CAF50;
}
/* 结束状态 */
to {
background-color: #2196F3;
}
}
/* 使用百分比定义多个关键帧 */
@keyframes rainbow {
0% { background-color: red; }
25% { background-color: yellow; }
50% { background-color: green; }
75% { background-color: blue; }
100% { background-color: purple; }
}
/* 多属性动画 */
@keyframes move-and-scale {
0% {
transform: translateX(0) scale(1);
background-color: #4CAF50;
}
50% {
transform: translateX(100px) scale(1.2);
background-color: #2196F3;
}
100% {
transform: translateX(0) scale(1);
background-color: #4CAF50;
}
}
.box {
width: 100px;
height: 100px;
margin: 20px;
display: inline-block;
vertical-align: top;
}
.anim1 {
animation: change-color 2s infinite alternate;
}
.anim2 {
animation: rainbow 3s infinite;
}
.anim3 {
animation: move-and-scale 2s infinite;
}
</style>
</head>
<body>
<div class="box anim1">颜色变化</div>
<div class="box anim2">彩虹效果</div>
<div class="box anim3">移动缩放</div>
</body>
</html>animation属性
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>animation属性示例</title>
<style>
@keyframes slide {
from { transform: translateX(0); }
to { transform: translateX(200px); }
}
.box {
width: 50px;
height: 50px;
background-color: #4CAF50;
margin: 10px;
}
/* 简写形式 */
.shorthand {
/* animation: name duration timing-function delay iteration-count direction fill-mode; */
animation: slide 2s ease-in-out 0.5s infinite alternate;
}
/* 分解写法 */
.detailed {
animation-name: slide; /* 动画名称 */
animation-duration: 2s; /* 持续时间 */
animation-timing-function: ease; /* 时间函数 */
animation-delay: 0.5s; /* 延迟时间 */
animation-iteration-count: infinite; /* 播放次数 */
animation-direction: alternate; /* 播放方向 */
animation-fill-mode: forwards; /* 填充模式 */
animation-play-state: running; /* 播放状态 */
}
</style>
</head>
<body>
<div class="box shorthand">简写形式</div>
<div class="box detailed">分解写法</div>
</body>
</html>animation-iteration-count(播放次数)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>播放次数示例</title>
<style>
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-30px); }
}
.ball {
width: 50px;
height: 50px;
background-color: #4CAF50;
border-radius: 50%;
margin: 20px;
display: inline-block;
animation: bounce 0.5s ease-in-out;
}
/* 播放次数 */
.once { animation-iteration-count: 1; }
.twice { animation-iteration-count: 2; }
.three { animation-iteration-count: 3; }
.infinite { animation-iteration-count: infinite; }
</style>
</head>
<body>
<p>刷新页面查看效果:</p>
<div class="ball once">1次</div>
<div class="ball twice">2次</div>
<div class="ball three">3次</div>
<div class="ball infinite">无限</div>
</body>
</html>animation-direction(播放方向)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>播放方向示例</title>
<style>
@keyframes move {
from { transform: translateX(0); }
to { transform: translateX(200px); }
}
.box {
width: 50px;
height: 50px;
background-color: #4CAF50;
margin: 10px;
animation: move 1s infinite;
}
/* 正常播放 */
.normal { animation-direction: normal; }
/* 反向播放 */
.reverse { animation-direction: reverse; }
/* 先正向再反向 */
.alternate { animation-direction: alternate; }
/* 先反向再正向 */
.alternate-reverse { animation-direction: alternate-reverse; }
</style>
</head>
<body>
<div class="box normal">normal</div>
<div class="box reverse">reverse</div>
<div class="box alternate">alternate</div>
<div class="box alternate-reverse">alternate-reverse</div>
</body>
</html>animation-fill-mode(填充模式)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>填充模式示例</title>
<style>
@keyframes fade {
from {
opacity: 0;
transform: scale(0.5);
}
to {
opacity: 1;
transform: scale(1);
}
}
.box {
width: 100px;
height: 100px;
background-color: #4CAF50;
margin: 20px;
display: inline-block;
opacity: 0.5;
animation: fade 2s forwards;
}
/* 动画前后保持默认样式 */
.none { animation-fill-mode: none; }
/* 动画结束后保持最后一帧 */
.forwards { animation-fill-mode: forwards; }
/* 动画开始前应用第一帧 */
.backwards { animation-fill-mode: backwards; animation-delay: 1s; }
/* 同时应用forwards和backwards */
.both { animation-fill-mode: both; animation-delay: 1s; }
</style>
</head>
<body>
<p>刷新页面查看效果:</p>
<div class="box none">none</div>
<div class="box forwards">forwards</div>
<div class="box backwards">backwards</div>
<div class="box both">both</div>
</body>
</html>animation-play-state(播放状态)
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>播放状态示例</title>
<style>
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.spinner {
width: 50px;
height: 50px;
background-color: #4CAF50;
margin: 20px;
animation: spin 2s linear infinite;
animation-play-state: paused; /* 默认暂停 */
}
.spinner:hover {
animation-play-state: running; /* 悬停时播放 */
}
/* 控制按钮 */
.controls {
margin: 20px;
}
.btn {
padding: 10px 20px;
background-color: #2196F3;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
margin-right: 10px;
}
</style>
</head>
<body>
<p>悬停查看动画:</p>
<div class="spinner"></div>
<div class="controls">
<button class="btn" onclick="document.querySelector('.spinner').style.animationPlayState='running'">播放</button>
<button class="btn" onclick="document.querySelector('.spinner').style.animationPlayState='paused'">暂停</button>
</div>
</body>
</html>动画示例
加载动画
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>加载动画示例</title>
<style>
/* 旋转加载 */
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 20px;
display: inline-block;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 脉冲加载 */
.pulse {
width: 40px;
height: 40px;
background-color: #3498db;
border-radius: 50%;
animation: pulse 1.5s ease-in-out infinite;
margin: 20px;
display: inline-block;
}
@keyframes pulse {
0% { transform: scale(0.8); opacity: 1; }
50% { transform: scale(1.2); opacity: 0.5; }
100% { transform: scale(0.8); opacity: 1; }
}
/* 跳动点 */
.dots {
display: inline-flex;
gap: 8px;
margin: 20px;
}
.dot {
width: 12px;
height: 12px;
background-color: #3498db;
border-radius: 50%;
animation: bounce 1.4s ease-in-out infinite;
}
.dot:nth-child(1) { animation-delay: 0s; }
.dot:nth-child(2) { animation-delay: 0.2s; }
.dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
/* 进度条 */
.progress-bar {
width: 200px;
height: 4px;
background-color: #f3f3f3;
border-radius: 2px;
overflow: hidden;
margin: 20px;
}
.progress {
height: 100%;
background: linear-gradient(90deg, #3498db, #2ecc71);
animation: progress 2s ease-in-out infinite;
}
@keyframes progress {
0% { width: 0%; }
50% { width: 100%; }
100% { width: 0%; }
}
</style>
</head>
<body>
<h3>旋转加载</h3>
<div class="spinner"></div>
<h3>脉冲加载</h3>
<div class="pulse"></div>
<h3>跳动点</h3>
<div class="dots">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
<h3>进度条</h3>
<div class="progress-bar">
<div class="progress"></div>
</div>
</body>
</html>文字动画
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文字动画示例</title>
<style>
/* 打字机效果 */
.typewriter {
overflow: hidden;
border-right: 3px solid #333;
white-space: nowrap;
animation:
typing 3.5s steps(20) infinite,
blink-caret 0.75s step-end infinite;
}
@keyframes typing {
0%, 100% { width: 0; }
50% { width: 100%; }
}
@keyframes blink-caret {
from, to { border-color: transparent; }
50% { border-color: #333; }
}
/* 闪烁文字 */
.blink {
animation: blink 1s step-start infinite;
}
@keyframes blink {
50% { opacity: 0; }
}
/* 彩虹文字 */
.rainbow {
background: linear-gradient(90deg,
#ff0000, #ff7f00, #ffff00,
#00ff00, #0000ff, #4b0082, #8f00ff);
background-size: 400% 100%;
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
animation: rainbow 3s linear infinite;
font-size: 24px;
font-weight: bold;
}
@keyframes rainbow {
0% { background-position: 0% 50%; }
100% { background-position: 400% 50%; }
}
/* 弹跳文字 */
.bounce-text span {
display: inline-block;
animation: bounce 0.5s ease infinite;
}
.bounce-text span:nth-child(2) { animation-delay: 0.1s; }
.bounce-text span:nth-child(3) { animation-delay: 0.2s; }
.bounce-text span:nth-child(4) { animation-delay: 0.3s; }
.bounce-text span:nth-child(5) { animation-delay: 0.4s; }
@keyframes bounce {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-10px); }
}
/* 淡入文字 */
.fade-in {
animation: fadeIn 2s ease-in-out infinite;
}
@keyframes fadeIn {
0%, 100% { opacity: 0; }
50% { opacity: 1; }
}
</style>
</head>
<body>
<h3>打字机效果</h3>
<p class="typewriter">这是打字机效果示例文字</p>
<h3>闪烁文字</h3>
<p class="blink">这是闪烁的文字</p>
<h3>彩虹文字</h3>
<p class="rainbow">彩虹渐变文字效果</p>
<h3>弹跳文字</h3>
<p class="bounce-text">
<span>弹</span>
<span>跳</span>
<span>文</span>
<span>字</span>
<span>~</span>
</p>
<h3>淡入淡出</h3>
<p class="fade-in">淡入淡出效果</p>
</body>
</html>交互动画
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>交互动画示例</title>
<style>
.container {
padding: 20px;
}
/* 心跳动画 */
.heart {
width: 50px;
height: 50px;
background-color: #e74c3c;
transform: rotate(-45deg);
margin: 50px;
cursor: pointer;
animation: heartbeat 1s ease-in-out infinite;
}
.heart::before,
.heart::after {
content: "";
width: 50px;
height: 50px;
background-color: #e74c3c;
border-radius: 50%;
position: absolute;
}
.heart::before {
top: -25px;
left: 0;
}
.heart::after {
left: 25px;
top: 0;
}
@keyframes heartbeat {
0%, 100% { transform: rotate(-45deg) scale(1); }
50% { transform: rotate(-45deg) scale(1.1); }
}
.heart:hover {
animation: heartbeat 0.5s ease-in-out infinite;
}
/* 摇晃动画 */
.shake {
width: 100px;
height: 100px;
background-color: #3498db;
margin: 20px;
display: inline-block;
cursor: pointer;
}
.shake:hover {
animation: shake 0.5s ease-in-out;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
/* 翻转卡片 */
.flip-card {
width: 200px;
height: 150px;
perspective: 1000px;
margin: 20px;
display: inline-block;
}
.flip-card-inner {
width: 100%;
height: 100%;
transition: transform 0.6s;
transform-style: preserve-3d;
}
.flip-card:hover .flip-card-inner {
transform: rotateY(180deg);
}
.flip-card-front,
.flip-card-back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
border-radius: 8px;
}
.flip-card-front {
background-color: #3498db;
color: white;
}
.flip-card-back {
background-color: #e74c3c;
color: white;
transform: rotateY(180deg);
}
/* 波纹按钮 */
.ripple-btn {
position: relative;
padding: 15px 30px;
background-color: #3498db;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
overflow: hidden;
font-size: 16px;
}
.ripple-btn::after {
content: "";
position: absolute;
top: 50%;
left: 50%;
width: 0;
height: 0;
background: rgba(255, 255, 255, 0.3);
border-radius: 50%;
transform: translate(-50%, -50%);
transition: width 0.6s, height 0.6s;
}
.ripple-btn:active::after {
width: 300px;
height: 300px;
}
</style>
</head>
<body>
<h3>心跳动画(悬停加速)</h3>
<div class="heart"></div>
<h3>摇晃动画(悬停触发)</h3>
<div class="shake">悬停摇晃</div>
<h3>翻转卡片</h3>
<div class="flip-card">
<div class="flip-card-inner">
<div class="flip-card-front">正面</div>
<div class="flip-card-back">背面</div>
</div>
</div>
<h3>波纹按钮</h3>
<button class="ripple-btn">点击查看波纹</button>
</body>
</html>综合示例
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>过渡与动画综合示例</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 40px 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
}
/* 卡片样式 */
.card {
background: white;
border-radius: 16px;
padding: 30px;
margin-bottom: 30px;
box-shadow: 0 10px 40px rgba(0,0,0,0.2);
animation: slideUp 0.6s ease-out;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.card h2 {
color: #333;
margin-bottom: 20px;
}
/* 动画按钮组 */
.btn-group {
display: flex;
gap: 15px;
flex-wrap: wrap;
}
.btn {
padding: 12px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
cursor: pointer;
transition: all 0.3s ease;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(102, 126, 234, 0.4);
}
.btn-outline {
background: transparent;
border: 2px solid #667eea;
color: #667eea;
}
.btn-outline:hover {
background: #667eea;
color: white;
}
/* 加载动画展示 */
.loaders {
display: flex;
gap: 30px;
align-items: center;
flex-wrap: wrap;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #667eea;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.dots {
display: flex;
gap: 8px;
}
.dot {
width: 12px;
height: 12px;
background-color: #667eea;
border-radius: 50%;
animation: bounce 1.4s ease-in-out infinite;
}
.dot:nth-child(1) { animation-delay: 0s; }
.dot:nth-child(2) { animation-delay: 0.2s; }
.dot:nth-child(3) { animation-delay: 0.4s; }
@keyframes bounce {
0%, 80%, 100% { transform: scale(0); }
40% { transform: scale(1); }
}
/* 进度条 */
.progress-container {
width: 100%;
max-width: 300px;
}
.progress-bar {
height: 8px;
background-color: #f3f3f3;
border-radius: 4px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
animation: progress 2s ease-in-out infinite;
}
@keyframes progress {
0% { width: 0%; }
50% { width: 100%; }
100% { width: 0%; }
}
/* 图片卡片 */
.image-cards {
display: flex;
gap: 20px;
flex-wrap: wrap;
}
.image-card {
width: 200px;
border-radius: 12px;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0,0,0,0.1);
transition: all 0.3s ease;
}
.image-card:hover {
transform: translateY(-10px);
box-shadow: 0 10px 30px rgba(0,0,0,0.2);
}
.image-card img {
width: 100%;
height: 150px;
object-fit: cover;
transition: transform 0.3s ease;
}
.image-card:hover img {
transform: scale(1.1);
}
.image-card .content {
padding: 15px;
background: white;
}
/* 输入框动画 */
.input-group {
position: relative;
margin-bottom: 20px;
}
.input-group input {
width: 100%;
padding: 15px;
border: 2px solid #ddd;
border-radius: 8px;
font-size: 16px;
transition: all 0.3s ease;
}
.input-group input:focus {
outline: none;
border-color: #667eea;
box-shadow: 0 0 20px rgba(102, 126, 234, 0.3);
}
.input-group label {
position: absolute;
left: 15px;
top: 50%;
transform: translateY(-50%);
color: #999;
pointer-events: none;
transition: all 0.3s ease;
background: white;
padding: 0 5px;
}
.input-group input:focus + label,
.input-group input:not(:placeholder-shown) + label {
top: 0;
font-size: 12px;
color: #667eea;
}
</style>
</head>
<body>
<div class="container">
<div class="card">
<h2>按钮动画</h2>
<div class="btn-group">
<button class="btn btn-primary">主要按钮</button>
<button class="btn btn-outline">边框按钮</button>
</div>
</div>
<div class="card">
<h2>加载动画</h2>
<div class="loaders">
<div class="spinner"></div>
<div class="dots">
<div class="dot"></div>
<div class="dot"></div>
<div class="dot"></div>
</div>
<div class="progress-container">
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
</div>
</div>
</div>
<div class="card">
<h2>卡片悬停效果</h2>
<div class="image-cards">
<div class="image-card">
<img src="https://picsum.photos/200/150" alt="图片">
<div class="content">
<h4>卡片标题</h4>
<p>悬停查看效果</p>
</div>
</div>
<div class="image-card">
<img src="https://picsum.photos/200/150?random=2" alt="图片">
<div class="content">
<h4>另一个卡片</h4>
<p>悬停查看效果</p>
</div>
</div>
</div>
</div>
<div class="card">
<h2>输入框动画</h2>
<div class="input-group">
<input type="text" placeholder=" ">
<label>用户名</label>
</div>
<div class="input-group">
<input type="email" placeholder=" ">
<label>邮箱地址</label>
</div>
</div>
</div>
</body>
</html>本章小结
本章我们学习了:
- CSS过渡:transition-property、transition-duration、transition-timing-function、transition-delay
- 时间函数:linear、ease、ease-in、ease-out、ease-in-out、cubic-bezier
- CSS动画:@keyframes规则定义动画序列
- 动画属性:animation-name、animation-duration、animation-iteration-count、animation-direction、animation-fill-mode
- 动画状态:animation-play-state控制播放和暂停
- 实用动画:加载动画、文字动画、交互动画
下一章
下一章我们将学习 响应式设计,了解如何适配各种设备和屏幕尺寸。
