Ionic动画效果 #
一、动画概述 #
1.1 Ionic动画系统 #
Ionic提供两种动画方式:
text
动画系统
│
├── Ionic Animations API
│ ├── 编程式动画
│ ├── 性能优化
│ └── 跨平台一致
│
└── CSS动画
├── 简单易用
├── 声明式
└── 原生性能
1.2 动画类型 #
| 类型 | 说明 | 使用场景 |
|---|---|---|
| 入场动画 | 元素出现 | 页面进入、模态框打开 |
| 出场动画 | 元素消失 | 页面离开、模态框关闭 |
| 交互动画 | 用户反馈 | 点击、滑动、拖拽 |
| 过渡动画 | 状态切换 | 展开/收起、切换 |
二、Ionic Animations API #
2.1 基本用法 #
typescript
import { Component, ElementRef } from '@angular/core';
import { Animation, AnimationController } from '@ionic/angular';
@Component({
selector: 'app-home',
template: `
<div #myElement class="box">动画元素</div>
<ion-button (click)="playAnimation()">播放动画</ion-button>
`
})
export class HomePage {
private animation: Animation;
constructor(
private animationCtrl: AnimationController,
private element: ElementRef
) {}
ngAfterViewInit() {
this.animation = this.animationCtrl
.create()
.addElement(this.element.nativeElement.querySelector('.box'))
.duration(1000)
.fromTo('opacity', '0', '1')
.fromTo('transform', 'translateX(-100px)', 'translateX(0)');
}
playAnimation() {
this.animation.play();
}
}
2.2 动画属性 #
typescript
const animation = this.animationCtrl
.create()
.addElement(element)
// 持续时间
.duration(1000)
// 延迟
.delay(500)
// 缓动函数
.easing('ease-out')
// 迭代次数
.iterations(Infinity)
// 方向
.direction('alternate')
// 填充模式
.fill('forwards');
2.3 关键帧动画 #
typescript
const animation = this.animationCtrl
.create()
.addElement(element)
.duration(2000)
.keyframes([
{ offset: 0, transform: 'scale(1)', opacity: '1' },
{ offset: 0.5, transform: 'scale(1.2)', opacity: '0.8' },
{ offset: 1, transform: 'scale(1)', opacity: '1' }
]);
2.4 组合动画 #
typescript
const animationA = this.animationCtrl
.create()
.addElement(elementA)
.fromTo('opacity', '0', '1');
const animationB = this.animationCtrl
.create()
.addElement(elementB)
.fromTo('transform', 'translateY(100px)', 'translateY(0)');
// 并行执行
const parallel = this.animationCtrl
.create()
.addAnimation([animationA, animationB]);
parallel.play();
// 顺序执行
const sequence = this.animationCtrl
.create()
.addAnimation([animationA])
.addAnimation([animationB]);
sequence.play();
2.5 动画事件 #
typescript
const animation = this.animationCtrl
.create()
.addElement(element)
.duration(1000)
.fromTo('opacity', '0', '1');
// 动画开始
animation.onStart(() => {
console.log('动画开始');
});
// 动画结束
animation.onEnd(() => {
console.log('动画结束');
});
// 动画迭代
animation.onIteration(() => {
console.log('动画迭代');
});
animation.play();
2.6 动画控制 #
typescript
// 播放
animation.play();
// 暂停
animation.pause();
// 停止
animation.stop();
// 销毁
animation.destroy();
// 反向播放
animation.reverse();
// 跳转到特定时间点
animation.progressStart();
animation.progressStep(0.5);
animation.progressEnd(1);
三、CSS动画 #
3.1 过渡动画 #
scss
.box {
transition: all 0.3s ease;
transform: scale(1);
}
.box:hover {
transform: scale(1.1);
}
.box:active {
transform: scale(0.95);
}
3.2 关键帧动画 #
scss
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.fade-in {
animation: fadeIn 0.5s ease forwards;
}
3.3 常用动画 #
scss
// 淡入
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
// 淡出
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
// 滑入
@keyframes slideInUp {
from {
transform: translateY(100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
// 滑出
@keyframes slideOutDown {
from {
transform: translateY(0);
opacity: 1;
}
to {
transform: translateY(100%);
opacity: 0;
}
}
// 缩放
@keyframes scaleIn {
from {
transform: scale(0);
opacity: 0;
}
to {
transform: scale(1);
opacity: 1;
}
}
// 旋转
@keyframes rotate {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
// 弹跳
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {
transform: translateY(0);
}
40% {
transform: translateY(-20px);
}
60% {
transform: translateY(-10px);
}
}
// 脉冲
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
// 摇晃
@keyframes shake {
0%, 100% {
transform: translateX(0);
}
10%, 30%, 50%, 70%, 90% {
transform: translateX(-5px);
}
20%, 40%, 60%, 80% {
transform: translateX(5px);
}
}
3.4 动画类 #
scss
// 动画类
.animate-fade-in {
animation: fadeIn 0.5s ease forwards;
}
.animate-slide-up {
animation: slideInUp 0.5s ease forwards;
}
.animate-scale {
animation: scaleIn 0.3s ease forwards;
}
.animate-bounce {
animation: bounce 1s ease infinite;
}
.animate-pulse {
animation: pulse 1s ease infinite;
}
.animate-shake {
animation: shake 0.5s ease;
}
.animate-rotate {
animation: rotate 1s linear infinite;
}
// 延迟类
.delay-100 { animation-delay: 100ms; }
.delay-200 { animation-delay: 200ms; }
.delay-300 { animation-delay: 300ms; }
.delay-500 { animation-delay: 500ms; }
.delay-1000 { animation-delay: 1000ms; }
// 持续时间类
.duration-100 { animation-duration: 100ms; }
.duration-200 { animation-duration: 200ms; }
.duration-300 { animation-duration: 300ms; }
.duration-500 { animation-duration: 500ms; }
.duration-1000 { animation-duration: 1000ms; }
四、页面过渡动画 #
4.1 自定义页面过渡 #
typescript
import { Component } from '@angular/core';
import { Animation, AnimationController } from '@ionic/angular';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html'
})
export class HomePage {
constructor(private animationCtrl: AnimationController) {}
// 自定义进入动画
enterAnimation = (baseEl: HTMLElement) => {
const animation = this.animationCtrl
.create()
.addElement(baseEl)
.duration(500)
.fromTo('opacity', '0', '1')
.fromTo('transform', 'translateX(100%)', 'translateX(0)');
return animation;
}
// 自定义离开动画
leaveAnimation = (baseEl: HTMLElement) => {
const animation = this.animationCtrl
.create()
.addElement(baseEl)
.duration(500)
.fromTo('opacity', '1', '0')
.fromTo('transform', 'translateX(0)', 'translateX(-100%)');
return animation;
}
}
4.2 模态框动画 #
typescript
import { Component } from '@angular/core';
import { ModalController } from '@ionic/angular';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html'
})
export class HomePage {
constructor(private modalCtrl: ModalController) {}
async openModal() {
const modal = await this.modalCtrl.create({
component: DetailModalComponent,
enterAnimation: this.enterAnimation,
leaveAnimation: this.leaveAnimation
});
await modal.present();
}
enterAnimation = (baseEl: HTMLElement) => {
const backdropAnimation = this.animationCtrl
.create()
.addElement(baseEl.querySelector('ion-backdrop')!)
.fromTo('opacity', '0.01', '0.4');
const wrapperAnimation = this.animationCtrl
.create()
.addElement(baseEl.querySelector('.modal-wrapper')!)
.fromTo('transform', 'translateY(100%)', 'translateY(0)')
.fromTo('opacity', '0', '1');
return this.animationCtrl
.create()
.addElement(baseEl)
.easing('ease-out')
.duration(300)
.addAnimation([backdropAnimation, wrapperAnimation]);
}
leaveAnimation = (baseEl: HTMLElement) => {
return this.enterAnimation(baseEl).direction('reverse');
}
}
五、交互动画 #
5.1 点击反馈 #
scss
.button-press {
transition: transform 0.1s ease;
&:active {
transform: scale(0.95);
}
}
5.2 滑动动画 #
typescript
import { Component } from '@angular/core';
import { Gesture, GestureController } from '@ionic/angular';
@Component({
selector: 'app-home',
template: `
<div #card class="swipe-card">滑动卡片</div>
`
})
export class HomePage {
private gesture: Gesture;
constructor(private gestureCtrl: GestureController) {}
ngAfterViewInit() {
this.gesture = this.gestureCtrl.create({
el: this.card.nativeElement,
gestureName: 'swipe',
onMove: (detail) => {
this.card.nativeElement.style.transform =
`translateX(${detail.deltaX}px)`;
},
onEnd: (detail) => {
if (Math.abs(detail.deltaX) > 100) {
// 滑出屏幕
this.card.nativeElement.style.transform =
`translateX(${detail.deltaX > 0 ? 500 : -500}px)`;
} else {
// 回弹
this.card.nativeElement.style.transform = 'translateX(0)';
}
}
});
this.gesture.enable();
}
}
5.3 拖拽动画 #
typescript
import { Component, ElementRef } from '@angular/core';
import { Gesture, GestureController } from '@ionic/angular';
@Component({
selector: 'app-home',
template: `
<div #draggable class="draggable">拖拽我</div>
`
})
export class HomePage {
private gesture: Gesture;
private startX = 0;
private startY = 0;
constructor(
private gestureCtrl: GestureController,
private element: ElementRef
) {}
ngAfterViewInit() {
const draggable = this.element.nativeElement.querySelector('.draggable');
this.gesture = this.gestureCtrl.create({
el: draggable,
gestureName: 'drag',
onStart: () => {
const rect = draggable.getBoundingClientRect();
this.startX = rect.left;
this.startY = rect.top;
},
onMove: (detail) => {
draggable.style.transform =
`translate(${detail.deltaX}px, ${detail.deltaY}px)`;
}
});
this.gesture.enable();
}
}
六、加载动画 #
6.1 加载指示器 #
typescript
import { Component } from '@angular/core';
import { LoadingController } from '@ionic/angular';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html'
})
export class HomePage {
constructor(private loadingCtrl: LoadingController) {}
async showLoading() {
const loading = await this.loadingCtrl.create({
message: '加载中...',
spinner: 'crescent',
duration: 2000
});
await loading.present();
}
}
6.2 骨架屏动画 #
scss
.skeleton {
background: linear-gradient(
90deg,
#f0f0f0 25%,
#e0e0e0 50%,
#f0f0f0 75%
);
background-size: 200% 100%;
animation: skeleton-loading 1.5s infinite;
}
@keyframes skeleton-loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
七、动画性能优化 #
7.1 使用transform #
scss
// 推荐:使用transform
.element {
transition: transform 0.3s ease;
}
.element:hover {
transform: translateX(100px);
}
// 不推荐:改变位置属性
.element {
transition: left 0.3s ease;
}
.element:hover {
left: 100px;
}
7.2 使用will-change #
scss
.animated-element {
will-change: transform, opacity;
}
7.3 减少重绘 #
scss
// 使用GPU加速
.gpu-accelerated {
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000px;
}
八、最佳实践 #
8.1 动画时长 #
scss
// 推荐的动画时长
$duration-fast: 150ms; // 微交互
$duration-normal: 300ms; // 常规动画
$duration-slow: 500ms; // 复杂动画
8.2 缓动函数 #
scss
// 常用缓动函数
$ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
$ease-out: cubic-bezier(0, 0, 0.2, 1);
$ease-in: cubic-bezier(0.4, 0, 1, 1);
$bounce: cubic-bezier(0.68, -0.55, 0.265, 1.55);
8.3 无障碍考虑 #
scss
// 尊重用户偏好
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
}
}
九、总结 #
9.1 动画要点 #
| 要点 | 说明 |
|---|---|
| Ionic Animations | 编程式动画,性能优化 |
| CSS动画 | 简单易用,声明式 |
| 页面过渡 | 自定义进入/离开动画 |
| 交互动画 | 点击、滑动、拖拽 |
| 性能优化 | transform、will-change |
9.2 下一步 #
掌握了动画效果后,接下来让我们学习 路由基础,了解Ionic的导航系统!
最后更新:2026-03-28