css 动效分析
淡入/淡出
普通淡入: 淡入就是 DOM 元素透明度从 0-1
淡入
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示淡入
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
淡入
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_fadeIn {
animation: fadein 0.5s ease-in;
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
</style>
从上方淡入: translateY 从-100%到 0 (-100%可以根据实际需要指定,下面的例子设置的是-30%)
从上方淡入
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示从上方淡入
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
从上方淡入
</div>
</div>
</template>
<style lang="scss" scoped>
.customBtn {
margin-bottom: 30px;
}
.rect {
width: 200px;
height: 100px;
background-color: lightcoral;
}
._animate_fadeIn {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-30%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
从下方淡入: translateY 从 100%到 0 (100%可以根据实际需要指定,下面的例子设置的是 30%)
从下方淡入
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示从下方淡入
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
从下方淡入
</div>
</div>
</template>
<style lang="scss" scoped>
.customBtn {
margin-bottom: 30px;
}
.rect {
width: 200px;
height: 100px;
background-color: lightcoral;
}
._animate_fadeIn {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(30%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
从左边淡入: translateX 从 -100%到 0 (-100%可以根据实际需要指定,下面的例子设置的是 -30%)
从左侧淡入
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示从左侧淡入
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
从左侧淡入
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_fadeIn {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(-30%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
从右边淡入: translateX 从 100%到 0 (100%可以根据实际需要指定,下面的例子设置的是 30%)
从右侧淡入
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示从右侧淡入
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
从右侧淡入
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_fadeIn {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateX(30%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
从右下边淡入: translate(100%,100%) 从 translate(0,0) (100%可以根据实际需要指定,下面的例子设置的是 30%)
从右下角淡入
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示从右下角淡入
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
从右下角淡入
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_fadeIn {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translate(30%, 30%);
}
to {
opacity: 1;
transform: translate(0, 0);
}
}
</style>
幻灯片切换
原理: DOM 元素位置变化
原理: translateX从-100%到0
幻灯片效果
<script setup lang="ts">
import { ref } from 'vue'
const slideInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(slideInElRef, '_animate_slideIn')">
演示幻灯片效果
</el-button>
<div ref="slideInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
幻灯片效果
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_slideIn {
animation: slideIn 0.5s ease-in;
}
@keyframes slideIn {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
</style>
原理: translateX从100%到0
幻灯片效果
<script setup lang="ts">
import { ref } from 'vue'
const slideInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(slideInElRef, '_animate_slideIn')">
演示幻灯片效果
</el-button>
<div ref="slideInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
幻灯片效果
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_slideIn {
animation: slideIn 0.5s ease-in;
}
@keyframes slideIn {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
</style>
幻灯片A
幻灯片B
<script setup lang="ts">
import { onBeforeUpdate, ref } from 'vue'
const slideInElRef = ref<HTMLDivElement[]>([])
function setSlideInElRef(dom: unknown) {
slideInElRef.value?.push(dom as HTMLDivElement)
}
onBeforeUpdate(() => slideInElRef.value = [])
function onShowSlideIn() {
// 给第一个元素加入幻灯片进入效果
let animateClassName = '_animate_slideIn'
slideInElRef.value![0].classList.contains(animateClassName) &&
slideInElRef.value![0].classList.remove(animateClassName)
// 给第二个元素加入幻灯片离开效果
animateClassName = '_animate_slideOut'
slideInElRef.value![1].classList.contains(animateClassName) &&
slideInElRef.value![1].classList.remove(animateClassName)
requestAnimationFrame(() => {
slideInElRef.value![0].classList.add('_animate_slideIn')
slideInElRef.value![1].classList.add('_animate_slideOut')
})
}
</script>
<template>
<div class="py-8 px-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="onShowSlideIn">
演示幻灯片效果
</el-button>
<div class="flex w-full h-100px">
<div :ref="setSlideInElRef" class="w-200px h-100px bgLightcoral flex_ccc">
幻灯片A
</div>
<div :ref="setSlideInElRef" class="w-200px h-100px bgLightseagreen flex_ccc">
幻灯片B
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.bgLightcoral {
background-color: lightcoral;
border-radius: 2px;
transform: translateX(-100%);
visibility: hidden;
}
.bgLightseagreen {
background-color: #fff;
border-radius: 2px;
transform: translateX(-100%);
visibility: visible;
}
._animate_slideIn {
animation: slideIn 0.5s ease-in;
animation-fill-mode: forwards;
}
._animate_slideOut {
animation: slideOut 0.5s ease-in;
animation-fill-mode: forwards;
}
@keyframes slideIn {
from {
transform: translateX(-100%);
visibility: visible;
}
to {
transform: translateX(0);
visibility: visible;
}
}
@keyframes slideOut {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
visibility: hidden;
}
}
</style>
放大/缩小
原理: 通过设置 scale 完成
放大
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示放大
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
放大
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_fadeIn {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
0% {
transform: scale(1);
}
60% {
transform: scale(1.5);
}
100% {
transform: scale(1);
}
}
</style>
缩小
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示缩小
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
缩小
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_fadeIn {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
0% {
transform: scale(1);
}
60% {
transform: scale(0.8);
}
80% {
transform: scale(0.5);
}
100% {
transform: scale(1);
}
}
</style>
保持最后的状态
但上面两个实例,在缩放完成之后,又变回了原来的大小(初始状态),那么怎么保持最后的状态呢?
可以通过animation-fill-mode:forwards
属性控制
animation-fill-mode : none | forwards | backwards | both;
值 描述
none 不改变默认行为。
forwards 当动画完成后,保持最后一个属性值(在最后一个关键帧中定义)。
backwards 在 animation-delay 所指定的一段时间内,在动画显示之前,应用开始属性值(在第一个关键帧中定义)。
both 向前和向后填充模式都被应用。
缩小
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示缩小
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
缩小
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_fadeIn {
animation: fadeIn 0.5s ease-in;
animation-fill-mode: forwards;
}
@keyframes fadeIn {
0% {
transform: scale(1);
}
100% {
transform: scale(0.5);
}
}
</style>
通过设置:animation: fadeIn 1.5s ease-out infinite; 的infinite达到无限循环的目的
缩小
<script setup lang="ts">
import { ref } from 'vue'
const fadeInElRef = ref<HTMLDivElement>()
const showAnimation = (el: HTMLDivElement | undefined, className: any) => {
if (!el) return
el.classList.contains(className) && el.classList.remove(className)
requestAnimationFrame(() => el!.classList.add(className))
}
</script>
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col items-center">
<el-button class="mb-8" @click="showAnimation(fadeInElRef, '_animate_fadeIn')">
演示缩小
</el-button>
<div ref="fadeInElRef" class="w-50 h-25 bg-white rounded-lg flex_ccc">
缩小
</div>
</div>
</template>
<style lang="scss" scoped>
._animate_fadeIn {
animation: fadeIn 1.5s ease-out infinite;
animation-fill-mode: forwards;
}
@keyframes fadeIn {
0% {
transform: scale(1);
background-color: lightcoral;
}
40% {
transform: scale(0.9);
background-color: lightgreen;
}
100% {
transform: scale(1);
background-color: lightcoral;
}
}
</style>
动画速度控制
animation-timing-function, transition-timing-function 两者的属性取值一样,共有 5 种
属性/方法 | 是否默认值 | 说明 |
---|---|---|
ease | 是 | 速度从快到逐渐变慢 |
linear | 否 | 速度保持匀速 |
ease-in | 否 | 速度越来越快(称之为渐显效果) |
ease-out | 否 | 速度越来越慢(称之为渐隐效果) |
ease-in-out | 否 | 速度先加速再减速(称之为渐显渐隐效果) |
ease(速度从快逐渐变慢)
linear(匀速)
ease-in(速度越来越快)
ease-out(速度越来越慢)
ease-in-out(速度先加速再减速)
<template>
<div class="py-8 rounded-lg linear-gradient-blue flex flex-col px-8">
<div class="rect bg-light-500 ease">
ease(速度从快逐渐变慢)
</div>
<div class="rect bg-orange-500 linear">
linear(匀速)
</div>
<div class="rect bg-blue-500 ease-in">
ease-in(速度越来越快)
</div>
<div class="rect bg-purple-500 ease-out">
ease-out(速度越来越慢)
</div>
<div class="rect bg-pink-500 ease-in-out">
ease-in-out(速度先加速再减速)
</div>
</div>
</template>
<style lang="scss" scoped>
.rect {
width: 190px;
height: 80px;
border-radius: 8px;
margin-bottom: 10px;
}
.ease {
animation: test 10s ease infinite;
}
.linear {
animation: test 10s linear infinite;
}
.ease-in {
animation: test 10s ease-in infinite;
}
.ease-out {
animation: test 10s ease-out infinite;
}
.ease-in-out {
animation: test 10s ease-in-out infinite;
}
@keyframes test {
0% {
transform: translateX(0);
}
90% {
transform: translateX(200%);
}
100% {
transform: translateX(0);
}
}
</style>