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>