Skip to content
本页目录

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>