动画系统
介绍
RuoYi-Plus-UniApp 前端项目建立了完善的动画系统,通过 CSS 过渡(Transition)、关键帧动画(Keyframes)和 Vue 过渡组件的有机结合,为用户界面提供流畅自然的视觉反馈和交互体验。动画系统不仅注重视觉美感,更强调性能优化和用户体验,所有动画都经过精心调校,确保在各种设备上都能流畅运行。
项目采用标准化的动画时长变量体系,通过 CSS 自定义属性(CSS Variables)集中管理动画时长,便于全局统一调整。同时提供了丰富的预设动画效果,包括淡入淡出、缩放、旋转、位移等常见动画类型,开发者可以直接使用这些预设动画,也可以基于标准时长变量自定义动画效果。
核心特性:
- 统一时长体系 - 通过
--duration-normal和--duration-slow两个 CSS 变量统一管理动画时长,确保全局动画风格一致 - 丰富的预设动画 - 提供 10+ 种常用关键帧动画,包括淡入淡出、缩放、旋转、位移、呼吸等效果
- Vue 过渡集成 - 深度集成 Vue Transition 组件,提供 fade、fade-transform、breadcrumb 等过渡类
- 现代 API 支持 - 使用 View Transition API 实现主题切换的圆形扩散动画,提供惊艳的视觉效果
- 性能优化 - 优先使用 transform 和 opacity 属性实现动画,避免触发重排(reflow),确保 60fps 流畅运行
- 动画工具类 - 提供丰富的动画工具类,如
icon-hover-shake、icon-hover-rotate180等,即用即走
项目的动画系统遵循"自然流畅、性能优先"的设计原则,所有动画效果都经过真机测试,确保在低端设备上也能保持良好的性能。同时,动画系统支持暗黑模式,所有动画在亮色和暗色主题下都能完美呈现,提供一致的用户体验。
动画时长变量
项目使用 CSS 自定义属性定义了标准化的动画时长,确保全局动画风格统一:
:root {
// 动画时长统一
--duration-normal: 0.3s; // 标准动画时长,用于大多数过渡效果
--duration-slow: 0.6s; // 慢速动画时长,用于强调或复杂的动画
}时长使用场景
--duration-normal (0.3s) - 标准时长
适用于大多数交互反馈和快速过渡效果:
- 按钮悬停状态变化
- 输入框焦点效果
- 下拉菜单展开/收起
- 模态框的淡入淡出
- 工具提示(Tooltip)显示/隐藏
- 标签页切换
- 导航栏背景色变化
--duration-slow (0.6s) - 慢速时长
适用于需要强调或包含复杂变换的动画:
- 页面路由切换
- 侧边栏展开/收起
- 复杂的多属性过渡(位移+缩放+透明度)
- 面包屑导航的进入/离开动画
- 大型组件的显示/隐藏
- 主题切换动画
在样式中使用
// 在 SCSS 中使用
.my-element {
// 单个属性过渡
transition: opacity var(--duration-normal);
// 多个属性过渡
transition: all var(--duration-slow);
// 指定多个属性
transition:
opacity var(--duration-normal),
transform var(--duration-normal) cubic-bezier(0.4, 0, 0.6, 1);
}
// 在关键帧动画中使用
.animated-element {
animation: fadeIn var(--duration-normal) ease-out;
}自定义时长
如果需要特殊的动画时长,可以在组件内覆盖:
<template>
<div class="custom-animation">
自定义动画时长
</div>
</template>
<style scoped>
.custom-animation {
--duration-normal: 0.2s; // 局部覆盖,更快的动画
transition: all var(--duration-normal);
&:hover {
transform: scale(1.05);
}
}
</style>CSS 过渡动画
CSS 过渡(Transition)是最常用的动画实现方式,通过 transition 属性实现元素状态变化时的平滑过渡。
基础用法
// 单个属性过渡
.button {
background-color: #409eff;
transition: background-color var(--duration-normal);
&:hover {
background-color: #66b1ff;
}
}
// 多个属性过渡
.card {
opacity: 1;
transform: translateY(0);
transition:
opacity var(--duration-normal),
transform var(--duration-normal);
&:hover {
opacity: 0.8;
transform: translateY(-5px);
}
}
// 所有属性过渡(不推荐用于生产环境)
.element {
transition: all var(--duration-slow);
}过渡时间函数(Easing)
项目常用的缓动函数:
// 线性(匀速)
transition: opacity 0.3s linear;
// 缓入缓出(默认,最常用)
transition: transform 0.3s ease;
// 缓入
transition: opacity 0.3s ease-in;
// 缓出
transition: opacity 0.3s ease-out;
// 缓入缓出(更明显)
transition: transform 0.3s ease-in-out;
// 自定义贝塞尔曲线
transition: transform 0.3s cubic-bezier(0.4, 0, 0.6, 1);常用过渡效果示例
1. 按钮悬停效果
<template>
<button class="btn-primary">
主要按钮
</button>
</template>
<style scoped lang="scss">
.btn-primary {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 24px;
background: var(--el-color-primary);
color: white;
border: none;
border-radius: 8px;
cursor: pointer;
// 过渡效果
transition:
background-color var(--duration-normal),
transform var(--duration-normal),
box-shadow var(--duration-normal);
&:hover {
background: var(--el-color-primary-dark-2);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(64, 158, 255, 0.4);
}
&:active {
transform: translateY(0);
}
}
</style>2. 卡片悬停效果
<template>
<div class="card">
<img src="/image.jpg" alt="Card Image" class="card-image" />
<div class="card-content">
<h3>卡片标题</h3>
<p>卡片内容描述</p>
</div>
</div>
</template>
<style scoped lang="scss">
.card {
background: var(--bg-base);
border-radius: 12px;
overflow: hidden;
cursor: pointer;
// 过渡效果
transition:
transform var(--duration-normal) cubic-bezier(0.4, 0, 0.6, 1),
box-shadow var(--duration-normal);
&:hover {
transform: translateY(-8px);
box-shadow: 0 12px 24px rgba(0, 0, 0, 0.1);
.card-image {
transform: scale(1.1);
}
}
}
.card-image {
width: 100%;
height: 200px;
object-fit: cover;
transition: transform var(--duration-slow);
}
.card-content {
padding: 20px;
}
</style>3. 输入框焦点效果
<template>
<div class="input-wrapper">
<input
type="text"
class="input-field"
placeholder="请输入内容"
/>
<div class="input-border"></div>
</div>
</template>
<style scoped lang="scss">
.input-wrapper {
position: relative;
display: inline-block;
}
.input-field {
padding: 12px 16px;
border: 2px solid var(--border-color);
border-radius: 8px;
font-size: 14px;
outline: none;
// 过渡效果
transition:
border-color var(--duration-normal),
box-shadow var(--duration-normal);
&:focus {
border-color: var(--el-color-primary);
box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.1);
}
}
.input-border {
position: absolute;
bottom: 0;
left: 0;
width: 0;
height: 2px;
background: var(--el-color-primary);
// 过渡效果
transition: width var(--duration-normal) cubic-bezier(0.4, 0, 0.6, 1);
}
.input-field:focus ~ .input-border {
width: 100%;
}
</style>关键帧动画
关键帧动画(@keyframes)用于定义复杂的、多阶段的动画效果。项目预定义了多种常用的关键帧动画。
对话框动画
现代化的对话框打开/关闭动画,使用缩放效果:
// 对话框打开动画
@keyframes dialog-open {
0% {
opacity: 0;
transform: scale(0.2);
}
100% {
opacity: 1;
transform: scale(1);
}
}
// 对话框关闭动画
@keyframes dialog-close {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0.2);
}
}
// 遮罩层淡出动画
@keyframes fade-out {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}使用方式:
.dialog-fade-enter-active {
.el-dialog:not(.is-draggable) {
animation: dialog-open 0.2s cubic-bezier(0.32, 0.14, 0.15, 0.86);
}
}
.dialog-fade-leave-active {
animation: fade-out 0.2s linear;
.el-dialog:not(.is-draggable) {
animation: dialog-close 0.5s;
}
}图标动画
项目提供了 6 种常用的图标动画效果:
1. 抖动动画(Shake)
@keyframes shake {
0% {
transform: rotate(0);
}
25% {
transform: rotate(-5deg);
}
50% {
transform: rotate(5deg);
}
75% {
transform: rotate(-5deg);
}
100% {
transform: rotate(0);
}
}2. 旋转动画(Rotate 180°)
@keyframes rotate180 {
0% {
transform: rotate(0);
}
100% {
transform: rotate(180deg);
}
}3. 上下移动动画(Move Up)
@keyframes moveUp {
0% {
transform: translateY(0);
}
50% {
transform: translateY(-3px);
}
100% {
transform: translateY(0);
}
}4. 放大动画(Expand)
@keyframes expand {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}5. 缩小动画(Shrink)
@keyframes shrink {
0% {
transform: scale(1);
}
50% {
transform: scale(0.9);
}
100% {
transform: scale(1);
}
}6. 呼吸动画(Breathing)
@keyframes breathing {
0% {
opacity: 0.4;
transform: scale(0.9);
}
50% {
opacity: 1;
transform: scale(1.1);
}
100% {
opacity: 0.4;
transform: scale(0.9);
}
}徽章呼吸动画
用于通知徽章的呼吸效果:
@keyframes breathe {
0% {
opacity: 0.7;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.1);
}
100% {
opacity: 0.7;
transform: scale(1);
}
}使用示例:
<template>
<div class="notification-badge">
99+
</div>
</template>
<style scoped>
.notification-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 20px;
height: 20px;
padding: 0 6px;
background: #f56c6c;
color: white;
border-radius: 10px;
font-size: 12px;
// 应用呼吸动画
animation: breathe 2s ease-in-out infinite;
}
</style>Vue 过渡动画
项目集成了 Vue 的 Transition 组件,提供了多种预设的过渡类。
淡入淡出效果(Fade)
最基础的透明度过渡效果:
.fade-enter-active,
.fade-leave-active {
transition: opacity var(--duration-normal);
}
.fade-enter,
.fade-leave-active {
opacity: 0;
}使用示例:
<template>
<div>
<button @click="show = !show">切换显示</button>
<Transition name="fade">
<div v-if="show" class="content-box">
淡入淡出的内容
</div>
</Transition>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const show = ref(true)
</script>淡入淡出+位移效果(Fade Transform)
结合透明度和位移的复合过渡效果:
.fade-transform-enter-active,
.fade-transform-leave-active {
transition: all var(--duration-slow);
}
.fade-transform-enter {
opacity: 0;
transform: translateX(-30px);
}
.fade-transform-leave-to {
opacity: 0;
transform: translateX(30px);
}使用示例:
<template>
<div>
<button @click="currentTab = 'tab1'">Tab 1</button>
<button @click="currentTab = 'tab2'">Tab 2</button>
<Transition name="fade-transform" mode="out-in">
<div v-if="currentTab === 'tab1'" key="tab1" class="tab-content">
Tab 1 内容
</div>
<div v-else key="tab2" class="tab-content">
Tab 2 内容
</div>
</Transition>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const currentTab = ref('tab1')
</script>面包屑过渡效果(Breadcrumb)
专门为面包屑导航设计的过渡效果,包含列表项移动动画:
.breadcrumb-enter-active,
.breadcrumb-leave-active {
transition: all var(--duration-slow);
}
.breadcrumb-enter,
.breadcrumb-leave-active {
opacity: 0;
transform: translateX(20px);
}
.breadcrumb-move {
transition: all var(--duration-slow);
}
.breadcrumb-leave-active {
position: absolute;
}使用示例:
<template>
<nav class="breadcrumb">
<TransitionGroup name="breadcrumb" tag="ul">
<li v-for="item in breadcrumbs" :key="item.path" class="breadcrumb-item">
<a :href="item.path">{{ item.title }}</a>
</li>
</TransitionGroup>
</nav>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const breadcrumbs = ref([
{ path: '/', title: '首页' },
{ path: '/products', title: '产品' },
{ path: '/products/detail', title: '产品详情' }
])
</script>
<style scoped>
.breadcrumb {
display: flex;
align-items: center;
gap: 8px;
}
.breadcrumb-item {
position: relative;
display: inline-flex;
align-items: center;
&:not(:last-child)::after {
content: '/';
margin: 0 8px;
color: var(--text-color-secondary);
}
}
</style>主题切换动画
项目使用现代的 View Transition API 实现主题切换时的圆形扩散动画效果,提供惊艳的视觉体验。
View Transition API
// 定义动画时长
$theme-animation-duration: 0.5s;
html {
// View Transition 样式
&::view-transition-old(root),
&::view-transition-new(root) {
animation: none;
mix-blend-mode: normal;
}
// 亮色模式 -> 暗黑模式: 新层从圆心扩散
&::view-transition-new(root) {
animation: theme-clip-in $theme-animation-duration ease-in both;
z-index: 9999;
}
&::view-transition-old(root) {
z-index: 1;
}
// 暗黑模式 -> 亮色模式: 旧层从外向圆心收缩
&.dark {
&::view-transition-old(root) {
animation: theme-clip-out $theme-animation-duration ease-in both;
z-index: 9999;
}
&::view-transition-new(root) {
animation: none;
z-index: 1;
}
}
}
// 圆形扩散动画(从小到大)
@keyframes theme-clip-in {
from {
clip-path: circle(0% at var(--theme-x) var(--theme-y));
}
to {
clip-path: circle(var(--theme-r) at var(--theme-x) var(--theme-y));
}
}
// 圆形收缩动画(从大到小)
@keyframes theme-clip-out {
from {
clip-path: circle(var(--theme-r) at var(--theme-x) var(--theme-y));
}
to {
clip-path: circle(0% at var(--theme-x) var(--theme-y));
}
}在组件中使用
<template>
<button
class="theme-toggle-btn"
@click="toggleTheme"
>
<Icon :code="isDark ? 'sun' : 'moon'" />
</button>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
const isDark = computed(() => appStore.theme === 'dark')
const toggleTheme = async (event: MouseEvent) => {
const x = event.clientX
const y = event.clientY
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
)
// 设置圆心位置和半径
document.documentElement.style.setProperty('--theme-x', `${x}px`)
document.documentElement.style.setProperty('--theme-y', `${y}px`)
document.documentElement.style.setProperty('--theme-r', `${endRadius}px`)
// 检查浏览器是否支持 View Transition API
if (!document.startViewTransition) {
// 不支持则直接切换
appStore.toggleTheme()
return
}
// 使用 View Transition API
const transition = document.startViewTransition(() => {
appStore.toggleTheme()
})
await transition.ready
}
</script>
<style scoped>
.theme-toggle-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
border: none;
background: transparent;
cursor: pointer;
border-radius: 8px;
transition: background-color var(--duration-normal);
&:hover {
background: var(--bg-level-3);
}
}
</style>动画工具类
项目提供了一系列即用即走的动画工具类,特别是图标动画类:
图标悬停动画类
// 抖动效果
.icon-hover-shake {
&:hover {
animation: shake 0.5s ease-in-out;
}
}
// 180度旋转
.icon-hover-rotate180 {
transform-origin: 50% 50% !important;
&:hover {
animation: rotate180 0.4s cubic-bezier(0.4, 0, 0.6, 1);
}
}
// 上下移动
.icon-hover-moveUp {
&:hover {
animation: moveUp 0.4s ease-in-out;
}
}
// 放大
.icon-hover-expand {
&:hover {
animation: expand 0.6s ease-in-out;
}
}
// 缩小
.icon-hover-shrink {
&:hover {
animation: shrink 0.6s ease-in-out;
}
}
// 持续呼吸(无需悬停)
.icon-hover-breathing {
animation: breathing 1.5s ease-in-out infinite;
}使用示例
<template>
<div class="icon-showcase">
<div class="icon-item">
<Icon code="bell" class="icon-hover-shake" />
<span>抖动效果</span>
</div>
<div class="icon-item">
<Icon code="refresh" class="icon-hover-rotate180" />
<span>旋转效果</span>
</div>
<div class="icon-item">
<Icon code="arrow-up" class="icon-hover-moveUp" />
<span>上下移动</span>
</div>
<div class="icon-item">
<Icon code="heart" class="icon-hover-expand" />
<span>放大效果</span>
</div>
<div class="icon-item">
<Icon code="star" class="icon-hover-shrink" />
<span>缩小效果</span>
</div>
<div class="icon-item">
<Icon code="wifi" class="icon-hover-breathing" />
<span>呼吸效果</span>
</div>
</div>
</template>
<style scoped>
.icon-showcase {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 24px;
padding: 24px;
}
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
padding: 16px;
background: var(--bg-base);
border-radius: 8px;
cursor: pointer;
:deep(.icon) {
font-size: 32px;
color: var(--el-color-primary);
}
span {
font-size: 12px;
color: var(--text-color-secondary);
}
}
</style>自定义动画工具类
你也可以创建自己的动画工具类:
<template>
<div class="my-animated-element">
自定义动画元素
</div>
</template>
<style scoped>
// 定义关键帧
@keyframes slide-in-right {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
// 创建工具类
.slide-in-animation {
animation: slide-in-right 0.4s cubic-bezier(0.4, 0, 0.6, 1);
}
.my-animated-element {
// 应用动画
@extend .slide-in-animation;
// 或直接使用
animation: slide-in-right 0.4s cubic-bezier(0.4, 0, 0.6, 1);
}
</style>最佳实践
1. 优先使用 transform 和 opacity
这两个属性可以被 GPU 加速,不会触发重排(reflow):
// ✅ 推荐:使用 transform
.element {
transform: translateX(0);
transition: transform var(--duration-normal);
&:hover {
transform: translateX(10px);
}
}
// ❌ 不推荐:使用 left(会触发重排)
.element {
position: relative;
left: 0;
transition: left var(--duration-normal);
&:hover {
left: 10px;
}
}2. 合理使用 will-change
对于频繁动画的元素,可以使用 will-change 提示浏览器优化:
.frequently-animated {
// 提示浏览器优化这些属性
will-change: transform, opacity;
// 动画结束后移除 will-change
&:not(:hover) {
will-change: auto;
}
}注意: 不要滥用 will-change,它会消耗额外的内存。
3. 使用统一的时长变量
// ✅ 推荐:使用统一的时长变量
.button {
transition: background-color var(--duration-normal);
}
// ❌ 不推荐:硬编码时长
.button {
transition: background-color 0.3s;
}4. 避免动画过多属性
// ❌ 不推荐:过渡所有属性(性能差)
.element {
transition: all 0.3s;
}
// ✅ 推荐:只过渡需要的属性
.element {
transition:
transform var(--duration-normal),
opacity var(--duration-normal);
}5. 使用适当的缓动函数
// 入场动画:使用 ease-out(快进慢出)
.enter {
animation: slideIn 0.3s ease-out;
}
// 出场动画:使用 ease-in(慢进快出)
.leave {
animation: slideOut 0.3s ease-in;
}
// 循环动画:使用 ease-in-out
.loop {
animation: pulse 1s ease-in-out infinite;
}6. 为长动画提供中断机制
<template>
<div
class="animated-box"
:class="{ 'is-animating': isAnimating }"
@click="stopAnimation"
>
点击停止动画
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
const isAnimating = ref(true)
const stopAnimation = () => {
isAnimating.value = false
}
</script>
<style scoped>
.animated-box {
&.is-animating {
animation: rotate 2s linear infinite;
}
// 点击后停止动画
&:not(.is-animating) {
animation: none;
}
}
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
</style>7. 减少动画复杂度
在低端设备或移动设备上,简化动画效果:
.fancy-animation {
// 默认:复杂动画
animation:
fadeIn 0.3s ease-out,
slideUp 0.3s ease-out,
scaleIn 0.3s ease-out;
// 降低动画复杂度
@media (prefers-reduced-motion: reduce) {
animation: fadeIn 0.3s ease-out;
}
// 移动设备:简化动画
@media (max-width: 768px) {
animation: fadeIn 0.2s ease-out;
}
}性能优化
1. 使用 CSS Containment
.animated-container {
// 包含布局和绘制,防止影响外部元素
contain: layout paint;
}2. 使用 requestAnimationFrame
对于 JavaScript 驱动的动画:
function animate() {
// 动画逻辑
const element = document.querySelector('.animated')
let start: number
const duration = 300
function step(timestamp: number) {
if (!start) start = timestamp
const progress = (timestamp - start) / duration
if (progress < 1) {
element.style.transform = `translateX(${progress * 100}px)`
requestAnimationFrame(step)
}
}
requestAnimationFrame(step)
}3. 避免布局抖动
// ❌ 不推荐:读写交错导致布局抖动
elements.forEach(el => {
const height = el.offsetHeight // 读取
el.style.height = height + 10 + 'px' // 写入
})
// ✅ 推荐:批量读取,批量写入
const heights = elements.map(el => el.offsetHeight)
elements.forEach((el, i) => {
el.style.height = heights[i] + 10 + 'px'
})4. 使用 CSS 硬件加速
.hardware-accelerated {
// 强制 GPU 加速
transform: translateZ(0);
// 或
transform: translate3d(0, 0, 0);
}常见问题
1. 动画卡顿或不流畅
问题原因:
- 动画了非合成属性(如 width、height、left、top)
- 同时动画了过多元素
- 使用了
transition: all - 未启用硬件加速
解决方案:
// ❌ 问题代码
.element {
transition: all 0.3s;
&:hover {
width: 200px; // 触发重排
height: 200px; // 触发重排
}
}
// ✅ 优化后
.element {
transition: transform 0.3s, opacity 0.3s;
transform: translateZ(0); // 启用硬件加速
&:hover {
transform: scale(1.2); // 使用 transform
opacity: 0.8; // 使用 opacity
}
}2. Vue 过渡动画不生效
问题原因:
- 忘记添加
name属性 - CSS 类名不匹配
- 使用了
v-show但期望路由级过渡 - 没有定义过渡类
解决方案:
<!-- ❌ 问题代码 -->
<Transition>
<div v-if="show">内容</div>
</Transition>
<!-- ✅ 正确代码 -->
<Transition name="fade">
<div v-if="show">内容</div>
</Transition>
<style>
/* 定义对应的过渡类 */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>3. 主题切换动画在某些浏览器不生效
问题原因:
View Transition API 是较新的 API,部分浏览器不支持。
解决方案:
const toggleTheme = async (event: MouseEvent) => {
// 检查浏览器支持
if (!document.startViewTransition) {
// 降级方案:直接切换
appStore.toggleTheme()
return
}
// 设置动画参数
const x = event.clientX
const y = event.clientY
const endRadius = Math.hypot(
Math.max(x, innerWidth - x),
Math.max(y, innerHeight - y)
)
document.documentElement.style.setProperty('--theme-x', `${x}px`)
document.documentElement.style.setProperty('--theme-y', `${y}px`)
document.documentElement.style.setProperty('--theme-r', `${endRadius}px`)
// 使用 View Transition API
const transition = document.startViewTransition(() => {
appStore.toggleTheme()
})
await transition.ready
}4. 动画在移动设备上性能差
问题原因:
- 移动设备性能较弱
- 动画过于复杂
- 未考虑用户的动画偏好设置
解决方案:
.complex-animation {
// 默认:完整动画
animation:
fadeIn 0.3s ease-out,
slideUp 0.3s ease-out,
rotate 0.3s ease-out;
// 尊重用户的减少动画偏好
@media (prefers-reduced-motion: reduce) {
animation: fadeIn 0.2s ease-out;
}
// 移动设备:简化动画
@media (max-width: 768px) {
animation: fadeIn 0.2s ease-out;
}
}5. 动画结束后元素闪烁
问题原因:
- 动画结束后状态与最终状态不一致
- 缺少
animation-fill-mode
解决方案:
// ❌ 问题代码
.element {
animation: fadeIn 0.3s;
// 动画结束后恢复到初始状态,导致闪烁
}
// ✅ 正确代码
.element {
animation: fadeIn 0.3s ease-out forwards;
// forwards: 保持动画结束时的状态
// 或者确保初始状态与动画结束状态一致
opacity: 1; // 与动画结束时的 opacity 一致
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}通过合理使用动画系统,可以为用户界面增添生动的视觉效果,提升用户体验。记住始终将性能放在首位,优先使用高性能的动画属性(transform 和 opacity),并在必要时为用户提供禁用动画的选项。
