文章目录
- [CSS 动画体系(二)------ Animation关键帧动画](#CSS 动画体系(二)—— Animation关键帧动画)
-
- [1. Animation 动画基础](#1. Animation 动画基础)
-
- [1.1 定义关键帧(@keyframes)](#1.1 定义关键帧(@keyframes))
- [1.2 使用 animation 属性](#1.2 使用 animation 属性)
- [2. 实战案例:跑马灯动画(Marquee)](#2. 实战案例:跑马灯动画(Marquee))
-
- [2.1 跑马灯原理](#2.1 跑马灯原理)
- [2.2 实现跑马灯](#2.2 实现跑马灯)
CSS 动画体系(二)------ Animation关键帧动画
基于关键帧(@keyframes)的时间轴动画系统。
它不是"属性变化动画"。而是自己就能跑的时间驱动动画。
1. Animation 动画基础
1.1 定义关键帧(@keyframes)
css
@keyframes marquee-left {
from {
transform: translateX(0);
}
to {
transform: translateX(-50%);
}
}
含义:从原位置 → 向左移动 50%。
还有百分比写法:
css
@keyframes move {
0% { transform: translateX(0); }
50% { transform: translateX(100px); }
100% { transform: translateX(0); }
}
1.2 使用 animation 属性
css
.marquee-left {
animation: marquee-left 30s linear infinite;
}
| 参数 | 含义 |
|---|---|
| marquee-left | 动画名字 |
| 30s | 持续时间 |
| linear | 匀速 |
| infinite | 无限循环 |
2. 实战案例:跑马灯动画(Marquee)
2.1 跑马灯原理
跑马灯核心原理:
css
/* 向左滚动动画:从 0 到 -50% */
@keyframes marquee-left {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
/* 向右滚动动画:从 -50% 到 0 */
@keyframes marquee-right {
0% {
transform: translateX(-50%);
}
100% {
transform: translateX(0);
}
}
跑马灯不是"滚完消失"。而是:滚动一段距离后,画面能无缝衔接。
结构一般是这样的:
javascript
[A B C D][A B C D]
两份完全一样的内容
动画从 0%-50% 的意思
假设:
- 整个容器宽度是 200%
- 原始内容宽度是 100%
当你:
css
transform: translateX(-50%);
意味着:
- 向左移动"原始内容的一半宽度"
- 刚好移动一份内容的长度
2.2 实现跑马灯
实现跑马灯的步骤:
(一)复制数组
虽然在上面例子的展示中,是复制一个数组一次,也就是一共有两个数组宽度,但是,真实的数据可能没有那么多,会造成跑马灯中会出现空白,所以,需要动态计算要复制的次数
typescript
// 复制 items 数组以实现无缝循环,确保有足够的内容填满容器
const duplicatedItems = computed(() => {
if (props.items.length === 0) return []
// 每个项目的宽度(380px 卡片 + 16px gap)
const itemWidth = 396
// 估算需要的最小项目数(假设容器宽度至少 1200px,需要 2400px 内容)
const minItemsNeeded = Math.ceil(2400 / itemWidth) // 约 6-7 个
// 计算需要复制的次数,确保至少复制 2 次用于无缝循环
const copiesNeeded = Math.max(2, Math.ceil(minItemsNeeded / props.items.length))
// 一次性生成所有副本,避免数组指数膨胀
return Array.from({ length: copiesNeeded }, () => props.items).flat()
})
1.空数组保护
javascript
if (props.items.length === 0) return []
- 没有数据就直接返回空数组,下面的计算都不用做。
2.估算一个"合理的内容总宽度"
javascript
const itemWidth = 396
const minItemsNeeded = Math.ceil(2400 / itemWidth) // 约 6-7 个
-
假设每个卡片占宽度 396px(380px 卡片 + 16px 间距)。
-
假设想让滚动内容至少有 2400px 的长度(约等于一个宽 1200px 容器的 2 倍),滚起来才自然。
-
minItemsNeeded 就是:要达到 2400px 总长度,至少需要多少个卡片。
比如 2400 / 396 ≈ 6.06,向上取整成 7 个。
3.算出要复制多少"轮"
javascript
const copiesNeeded = Math.max(2, Math.ceil(minItemsNeeded / props.items.length))
- Math.max 取两者最大值,最少是复制 2 次,Math.ceil 上取整,minItemsNeeded 最小宽度,除于实际的一个数组的长度
4.return Array.from({ length: copiesNeeded }, () => props.items).flat()
其中,Array.from() 是 JavaScript 的一个静态方法。
作用是,把"类数组"或"可迭代对象"转换成真正的数组。
javascript
Array.from(arrayLike, mapFn)
一共有两个参数,第一个参数是类数组,第二个参数是映射函数(每一项生成的时候,都执行这个函数)
先说类数组,
数组的本质是"有 length 属性的对象"
数组底层其实长这样(简化理解):
javascript
{
0: 'a',
1: 'b',
2: 'c',
length: 3
}
所以 { length: 3 }就满足一个类数组最基本的要求
javascript
Array.from({ length: 3 })
//结果是
[undefined, undefined, undefined]
再说 mapFn
mapFn 接收两个参数
javascript
(value, index)
举例写法
javascript
Array.from({ length: 5 }, (_, i) => i)
[0, 1, 2, 3, 4]
- _就是一个占位的,箭头函数中 value 没有用,用的是 index,index 的递增的
.flat() 是数组方法,作用是把二维数组"拍平"成一维数组。
javascript
[
[A, B],
[A, B],
[A, B]
].flat()
变成
[A, B, A, B, A, B]
(二)跑马灯动画
css
.horizontal-image-list.marquee-left .horizontal-image-list-container {
animation: marquee-left 30s linear infinite;
}
.horizontal-image-list.marquee-right .horizontal-image-list-container {
animation: marquee-right 30s linear infinite;
}
/* 向左滚动动画:从 0 到 -50% */
@keyframes marquee-left {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-50%);
}
}
/* 向右滚动动画:从 -50% 到 0 */
@keyframes marquee-right {
0% {
transform: translateX(-50%);
}
100% {
transform: translateX(0);
}
}
(三)鼠标悬浮时,暂停跑动
css
/* Hover 时暂停动画(仅在支持 hover 的设备上生效,如 desktop) */
@media (hover: hover) {
.horizontal-image-list:hover .horizontal-image-list-container {
animation-play-state: paused;
}
}
@media 是 CSS 的一个 At-Rule(规则指令),当然了,@keyframes 也是一个规则指令
在满足某个条件时,才应用里面的样式。
@media (hover: hover)
- 设备支持鼠标 hover 行为
- 因为手机是没有 hover 的,如果不加入这个判断,手机可能会卡住,动画异常,交互行为怪异
javascript
animation-play-state: paused;
animation-play-state 就是 CSS animation 的一个属性,专门用来控制动画的"播放状态"
animation-play-state 用来控制动画是"播放"还是"暂停"。
它有两个值
animation-play-state: running; /* 默认,播放 */
animation-play-state: paused; /* 暂停 */
animation 体系
animation 实际上是一个"简写属性",包含很多细分属性,比如:
| 属性 | 作用 |
|---|---|
| animation-name | 动画名称 |
| animation-duration | 持续时间 |
| animation-timing-function | 速度曲线 |
| animation-iteration-count | 循环次数 |
| animation-delay | 延迟 |
| animation-direction | 播放方向 |
| animation-fill-mode | 结束状态 |
| animation-play-state | 播放状态 |