引用 MDN 对 CSS 动画的说明:
- CSS 动画模块(CSS Animation)可以让你通过使用关键帧对 CSS 属性的值进行动画处理,例如背景位置和变换。
- 每个关键帧都描述了动画元素在动画序列中的某个特定时间应该如何呈现。
- 你可以使用动画模块中的属性来控制动画的持续时间、重复次数、延迟启动等方面。
简单讲,CSS 动画是用于实现元素从一个 CSS 样式配置转换到另一个 CSS 样式配置的呈现效果。
CSS 动画使得您能够实现一些难以置信的效果点缀您的页面或者应用程序。(MDN)
动画语法包括两个部分:
示例代码:
/* 指定动画的样式规则 */
.div {
width: 200px;
height: 200px;
animation: change 3s;
}
/* 指定动画开始、结束点样式的关键帧。 */
@keyframes change {
0% {
background-color: #f00;
}
100% {
background-color: #fff;
}
}
结合上例,对一个元素创建动画,需要在元素的 CSS 选择器上使用animation
属性或其子属性来配置动画时长、动画呈现方式及其他动画运行时相关参数,而动画在不同时间点的关键帧的表现样式则需要通过@keyframes
来配置。
指定由 @keyframes 定义动画名称标识,多个使用逗号隔开。
设置动画一个周期的时长,值必须为正数或 0,单位:秒(s)或毫秒(ms)。
示例:说明:本文示例代码由 vue3 + element-plus 构建
<template>
<el-card header="animation-duration">
<div class="ani-box slow" :class="{ running: playState }">
<div>10sdiv>
div>
<div class="ani-box fast" :class="{ running: playState }">
<div>3sdiv>
div>
<el-button type="primary" @click="handleAnimationRunning"
>播放动画el-button
>
el-card>
template>
<style scoped>
.ani-box {
width: 80px;
height: 80px;
background-color: darkcyan;
border-radius: 40px;
animation-name: move;
animation-fill-mode: forwards;
animation-play-state: paused;
text-align: center;
line-height: 80px;
color: wheat;
font-size: 20px;
margin-bottom: 20px;
}
.ani-box.slow {
animation-duration: 10s;
}
.ani-box.fast {
animation-duration: 3s;
}
.ani-box.running {
animation-play-state: running;
}
@keyframes move {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(500px);
}
}
style>
<script setup>
import { ref } from "vue";
const playState = ref(false);
const handleAnimationRunning = () => {
playState.value = true;
};
script>
设置延时,指定从应用动画到元素开始执行之前等待的时间,值可以是负值,单位:秒(s)或毫秒(ms)。
示例代码:
<template>
<el-card header="animation-delay">
<div class="ani-box default" :class="{ running: playState }">
<div>0sdiv>
div>
<div class="ani-box positive" :class="{ running: playState }">
<div>5sdiv>
div>
<div class="ani-box negative" :class="{ running: playState }">
<div>-5sdiv>
div>
<el-button type="primary" @click="handleAnimationRunning"
>播放动画el-button
>
el-card>
template>
<style scoped>
.ani-box {
width: 80px;
height: 80px;
background-color: darkcyan;
border-radius: 40px;
text-align: center;
line-height: 80px;
color: wheat;
font-size: 20px;
margin-bottom: 20px;
animation-name: move;
animation-fill-mode: forwards;
animation-play-state: paused;
animation-duration: 10s;
animation-timing-function: linear;
}
.ani-box.default {
animation-delay: 0s;
}
.ani-box.positive {
animation-delay: 5s;
}
.ani-box.negative {
animation-delay: -5s;
}
.ani-box.running {
animation-play-state: running;
}
@keyframes move {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(500px);
}
}
style>
<script setup>
import { ref } from "vue";
const playState = ref(false);
const handleAnimationRunning = () => {
playState.value = true;
};
script>
设置动画是正向播放、反向播放、还是在正向和反向之间交替播放。可选值:
示例代码:
<template>
<el-card header="animation-direction">
<div class="ani-box normal" :class="{ running: playState }">
<div>normaldiv>
div>
<div class="ani-box reverse" :class="{ running: playState }">
<div>reversediv>
div>
<div class="ani-box alternate" :class="{ running: playState }">
<div>alternatediv>
div>
<div class="ani-box alternate-reverse" :class="{ running: playState }">
<div>alternate-reversediv>
div>
<el-button type="primary" @click="handleAnimationRunning"
>播放动画el-button
>
el-card>
template>
<style scoped>
.ani-box {
width: 80px;
height: 80px;
background-color: darkcyan;
border-radius: 40px;
text-align: center;
line-height: 1;
display: flex;
align-items: center;
justify-content: center;
color: wheat;
font-size: 16px;
margin-bottom: 20px;
animation-name: move;
animation-fill-mode: forwards;
animation-play-state: paused;
animation-duration: 2s;
animation-timing-function: linear;
animation-iteration-count: 4;
}
.ani-box.normal {
animation-direction: normal;
}
.ani-box.reverse {
animation-direction: reverse;
}
.ani-box.alternate {
animation-direction: alternate;
}
.ani-box.alternate-reverse {
animation-direction: alternate-reverse;
}
.ani-box.running {
animation-play-state: running;
}
@keyframes move {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(500px);
}
}
style>
<script setup>
import { ref } from "vue";
const playState = ref(false);
const handleAnimationRunning = () => {
playState.value = true;
};
script>
设置动画在停止前应播放的次数。可选值:
示例代码:
<template>
<el-card header="animation-iteration-count">
<div class="ani-box half" :class="{ running: playState }">
<div>0.5次div>
div>
<div class="ani-box once" :class="{ running: playState }">
<div>1次div>
div>
<div class="ani-box twice" :class="{ running: playState }">
<div>2次div>
div>
<el-button type="primary" @click="handleAnimationRunning"
>播放动画el-button
>
el-card>
template>
<style scoped>
.ani-box {
width: 80px;
height: 80px;
background-color: darkcyan;
border-radius: 40px;
text-align: center;
line-height: 80px;
color: wheat;
font-size: 20px;
margin-bottom: 20px;
animation-name: move;
animation-fill-mode: forwards;
animation-play-state: paused;
animation-duration: 6s;
animation-timing-function: linear;
}
.ani-box.half {
animation-iteration-count: 0.5;
}
.ani-box.once {
animation-iteration-count: 1;
}
.ani-box.twice {
animation-iteration-count: 2;
}
.ani-box.running {
animation-play-state: running;
}
@keyframes move {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(500px);
}
}
style>
<script setup>
import { ref } from "vue";
const playState = ref(false);
const handleAnimationRunning = () => {
playState.value = true;
};
script>
设置动画运行状态,运行或暂停。可选值:
示例代码:
<template>
<el-card header="animation-play-state">
<div class="ani-box running">51BLOGdiv>
<el-alert
title="鼠标悬浮时运行动画,离开时暂停动画。"
:closable="false"
show-icon
type="warning"
>el-alert>
el-card>
template>
<style lang="scss" scoped>
.ani-box {
width: 80px;
height: 80px;
background-color: darkcyan;
border-radius: 4px;
text-align: center;
line-height: 80px;
color: wheat;
font-size: 16px;
text-shadow: 1px 2px 1px red;
margin-bottom: 30px;
cursor: pointer;
animation-name: move;
animation-fill-mode: forwards;
animation-play-state: paused;
animation-duration: 5s;
animation-timing-function: linear;
}
.ani-box:hover {
animation-play-state: running;
}
@keyframes move {
0% {
transform: rotate(0);
}
100% {
transform: rotate(360deg);
}
}
style>
设置动画在每个周期的持续时间内如何进行。支持三种类型值:
cubic-bezier(0.25, 0.1, 0.25, 1.0)
cubic-bezier(0.42, 0, 1.0, 1.0)
cubic_bezier(0, 0, 0.58, 1.0)
cubic_bezier(0.42, 0, 0.58, 1.0)
cubic-bezier(0.0, 0.0, 1.0, 1.0)
steps(1, jump-start)
steps(1, jump-end)
ease、ease-in、ease-out、ease-in-out、linear 称之为非阶跃(non-step)关键字值,代表了固定的四点值的三次贝塞尔曲线
示例代码:
<template>
<el-card header="animation-timing-function: ease">
<div class="ani-box ease" :class="{ running: playState }">
<div>easediv>
div>
<div class="ani-box ease-in" :class="{ running: playState }">
<div>ease-indiv>
div>
<div class="ani-box ease-out" :class="{ running: playState }">
<div>ease-outdiv>
div>
<div class="ani-box ease-in-out" :class="{ running: playState }">
<div>ease-in-outdiv>
div>
<div class="ani-box linear" :class="{ running: playState }">
<div>lineardiv>
div>
<div class="ani-box cubic-bezier" :class="{ running: playState }">
<div>cubic-bezierdiv>
div>
<el-button type="primary" @click="handleAnimationRunning"
>播放动画el-button
>
el-card>
template>
<style scoped>
.ani-box {
width: 80px;
height: 80px;
background-color: darkcyan;
border-radius: 40px;
text-align: center;
line-height: 1;
display: flex;
justify-content: center;
align-items: center;
color: wheat;
font-size: 16px;
margin-bottom: 20px;
animation-name: move;
animation-fill-mode: forwards;
animation-play-state: paused;
animation-duration: 10s;
animation-timing-function: linear;
}
.ani-box.ease {
animation-timing-function: ease;
}
.ani-box.ease-in {
animation-timing-function: ease-in;
}
.ani-box.ease-out {
animation-timing-function: ease-out;
}
.ani-box.ease-in-out {
animation-timing-function: ease-in-out;
}
.ani-box.linear {
animation-timing-function: linear;
}
.ani-box.cubic-bezier {
animation-timing-function: cubic-bezier(0.19, 1, 0.86, 0.01);
}
.ani-box.running {
animation-play-state: running;
}
@keyframes move {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(800px);
}
}
style>
<script setup>
import { ref } from "vue";
const playState = ref(false);
const handleAnimationRunning = () => {
playState.value = true;
};
script>
steps(n, )
<template>
<el-card header="animation-timing-function: steps(n, )" >
<div class="ani-box jump-start" :class="{ running: playState }">
<div>jump-startdiv>
div>
<div class="ani-box jump-end" :class="{ running: playState }">
<div>jump-enddiv>
div>
<div class="ani-box jump-none" :class="{ running: playState }">
<div>jump-nonediv>
div>
<div class="ani-box jump-both" :class="{ running: playState }">
<div>jump-bothdiv>
div>
<el-button type="primary" @click="handleAnimationRunning"
>播放动画el-button
>
el-card>
template>
<style scoped>
.ani-box {
width: 80px;
height: 80px;
background-color: darkcyan;
border-radius: 40px;
text-align: center;
line-height: 1;
display: flex;
justify-content: center;
align-items: center;
color: wheat;
font-size: 16px;
margin-bottom: 20px;
animation-name: move;
animation-fill-mode: none;
animation-play-state: paused;
animation-duration: 10s;
}
.ani-box.jump-start {
animation-timing-function: steps(5, jump-start);
}
.ani-box.jump-end {
animation-timing-function: steps(5, jump-end);
}
.ani-box.jump-none {
animation-timing-function: steps(5, jump-none);
}
.ani-box.jump-both {
animation-timing-function: steps(5, jump-both);
}
.ani-box.running {
animation-play-state: running;
}
@keyframes move {
0% {
transform: translateX(0px);
}
100% {
transform: translateX(800px);
}
}
style>
<script setup>
import { ref } from "vue";
const playState = ref(false);
const handleAnimationRunning = () => {
playState.value = true;
};
script>
设置动画在执行之前和之后如何将样式应用于其目标。可选值:
示例代码:
<template>
<el-card header="animation-fill-mode">
<div class="ani-box none" :class="{ running: playState }">
<div>nonediv>
div>
<div class="ani-box forwards" :class="{ running: playState }">
<div>forwardsdiv>
div>
<div class="ani-box backwords" :class="{ running: playState }">
<div>backwardsdiv>
div>
<div class="ani-box both" :class="{ running: playState }">
<div>bothdiv>
div>
<el-button type="primary" @click="handleAnimationRunning"
>播放动画el-button
>
el-card>
template>
<style scoped>
.ani-box {
width: 80px;
height: 80px;
background-color: darkcyan;
border-radius: 40px;
text-align: center;
line-height: 80px;
color: wheat;
font-size: 16px;
margin-bottom: 20px;
animation-name: move;
animation-play-state: paused;
animation-duration: 5s;
animation-timing-function: linear;
animation-delay: 1s;
}
.ani-box.none {
animation-fill-mode: none;
}
.ani-box.forwards {
animation-fill-mode: forwards;
}
.ani-box.backwords {
animation-fill-mode: backwards;
}
.ani-box.both {
animation-fill-mode: both;
}
.ani-box.running {
animation-play-state: running;
}
@keyframes move {
0% {
transform: translateX(200px);
}
100% {
transform: translateX(500px);
}
}
style>
<script setup>
import { ref } from "vue";
const playState = ref(false);
const handleAnimationRunning = () => {
playState.value = true;
};
script>
animation 是上面这些属性的一个简写属性形式。后续会单独写篇文章来详细介绍。