一、转场动画概述
转场动画是指对将要出现或消失的组件做动画,对始终出现的组件做动画应使用属性动画。转场动画主要为了让开发者从繁重的消失节点管理中解放出来,如果用属性动画做组件转场,开发者需要在动画结束回调中删除组件节点。同时,由于动画结束前已经删除的组件节点可能会重新出现,还需要在结束回调中增加对节点状态的判断。
转场动画分为基础转场和高级模板化转场,有如下几类:
- 出现/消失转场:对新增、消失的控件实现动画效果,是通用的基础转场效果。
- 导航转场:页面的路由转场方式,对应一个界面消失,另外一个界面出现的动画效果,如设置应用一级菜单切换到二级界面。
- 模态转场:新的界面覆盖在旧的界面之上的动画,旧的界面不消失,新的界面出现,如弹框就是典型的模态转场动画。
- 共享元素转场 (一镜到底):共享元素转场是一种界面切换时对相同或者相似的元素做的一种位置和大小匹配的过渡动画效果。
- 页面转场动画(不推荐):页面的路由转场方式,可以通过在pageTransition函数中自定义页面入场和页面退场的转场动效。为了实现更好的转场效果,推荐使用导航转场和模态转场。
- 旋转屏动画:旋转屏动画主要分为两类:布局切换的旋转屏动画和透明度变化的旋转屏动画,旨在实现屏幕显示方向变化时的自然过渡。
二、出现/消失转场
transition 是基础的组件转场接口,用于实现一个组件出现或者消失时的动画效果。可以通过TransitionEffect对象的组合使用,定义出各式效果。
| 转场效果 | 说明 | 动画 |
|---|---|---|
| IDENTITY | 禁用转场效果。 | 无。 |
| OPACITY | 默认的转场效果,透明度转场。 | 出现时透明度从0到1,消失时透明度从1到0。 |
| SLIDE | 滑动转场效果。 | 出现时从窗口左侧滑入,消失时从窗口右侧滑出。 |
| translate | 通过设置组件平移创建转场效果。 | 出现时为translate接口设置的值到默认值0,消失时为默认值0到translate接口设置的值。 |
| rotate | 通过设置组件旋转创建转场效果。 | 出现时为rotate接口设置的值到默认值0,消失时为默认值0到rotate接口设置的值。 |
| opacity | 通过设置透明度参数创建转场效果。 | 出现时为opacity设置的值到默认透明度1,消失时为默认透明度1到opacity设置的值。 |
| move | 通过TransitionEdge创建从窗口哪条边缘出来的效果。 | 出现时从TransitionEdge方向滑入,消失时滑出到TransitionEdge方向。 |
| asymmetric | 通过此方法组合非对称的出现消失转场效果。 - appear:出现转场的效果。 - disappear:消失转场的效果。 | 出现时采用appear设置的TransitionEffect出现效果,消失时采用disappear设置的TransitionEffect消失效果。 |
| combine | 组合其他TransitionEffect。 | 组合其他TransitionEffect,一起生效。 |
| animation | 定义转场效果的动画参数: - 如果不定义会跟随animateTo的动画参数。 - 不支持通过控件的animation接口配置动画参数。 - TransitionEffect中animation的onFinish不生效。 | 调用顺序时从上往下,上面TransitionEffect的animation也会作用到下面TransitionEffect。 |
- 创建TransitionEffect。
c
// 出现时会是所有转场效果的出现效果叠加,消失时会是所有消失转场效果的叠加
// 说明各个effect跟随的动画参数
private effect: object =
TransitionEffect.OPACITY // 创建了透明度转场效果,这里没有调用animation接口,会跟随animateTo的动画参数
// 通过combine方法,添加缩放转场效果,并指定了springMotion(0.6, 1.2)曲线
.combine(TransitionEffect.scale({ x: 0, y: 0 }).animation({ curve: curves.springMotion(0.6, 1.2) }))
// 添加旋转转场效果,这里的动画参数会跟随上面的TransitionEffect,也就是springMotion(0.6, 1.2)
.combine(TransitionEffect.rotate({ angle: 90 }))
// 添加平移转场效果,动画参数会跟随其之上带animation的TransitionEffect,也就是springMotion(0.6, 1.2)
.combine(TransitionEffect.translate({ x: 150, y: 150 }))
// 添加move转场效果,并指定了springMotion曲线
.combine(TransitionEffect.move(TransitionEdge.END)).animation({curve: curves.springMotion()})
// 添加非对称的转场效果,由于这里没有设置animation,会跟随上面的TransitionEffect的animation效果,也就是springMotion
.combine(TransitionEffect.asymmetric(TransitionEffect.scale({ x: 0, y: 0 }), TransitionEffect.rotate({ angle: 90 })));
- 将转场效果通过transition接口设置到组件。
c
Text('test')
.transition(this.effect)
- 新增或者删除组件触发转场。
c
@State isPresent: boolean = true;
// ...
if (this.isPresent) {
Text('test')
.transition(this.effect)
}
// ...
// 控制新增或者删除组件
// 方式一:将控制变量放到animateTo闭包内,未通过animation接口定义动画参数的TransitionEffect将跟随animateTo的动画参数
this.getUIContext()?.animateTo({ curve: curves.springMotion() }, () => {
this.isPresent = false;
})
// 方式二:直接控制删除或者新增组件,动画参数由TransitionEffect的animation接口配置
this.isPresent = false;
完整的示例代码和效果如下,示例中采用直接删除或新增组件的方式触发转场,也可以替换为在animateTo闭包内改变控制变量触发转场。
效果图
示例代码
c
import { curves } from '@kit.ArkUI';
@Entry
@Component
struct TransitionEffectDemo {
@State isPresent: boolean = false;
// 第一步,创建TransitionEffect
private effect: TransitionEffect =
// 创建默认透明度转场效果,并指定了springMotion(0.6, 0.8)曲线
TransitionEffect.OPACITY.animation({
curve: curves.springMotion(0.6, 0.8)
})// 通过combine方法,这里的动画参数会跟随上面的TransitionEffect,也就是springMotion(0.6, 0.8)
.combine(TransitionEffect.scale({
x: 0,
y: 0
}))// 添加旋转转场效果,这里的动画参数会跟随上面带animation的TransitionEffect,也就是springMotion(0.6, 0.8)
.combine(TransitionEffect.rotate({ angle: 90 }))// 添加平移转场效果,这里的动画参数使用指定的springMotion()
.combine(TransitionEffect.translate({ y: 150 })
.animation({ curve: curves.springMotion() }))// 添加move转场效果,这里的动画参数会跟随上面的TransitionEffect,也就是springMotion()
.combine(TransitionEffect.move(TransitionEdge.END));
build() {
Stack() {
if (this.isPresent) {
Column() {
Text('ArkUI')
.fontWeight(FontWeight.Bold)
.fontSize(20)
.fontColor(Color.White)
}
.justifyContent(FlexAlign.Center)
.width(150)
.height(150)
.borderRadius(10)
.backgroundColor(0xf56c6c)
// 第二步:将转场效果通过transition接口设置到组件
.transition(this.effect)
}
// 边框
Column()
.width(155)
.height(155)
.border({
width: 5,
radius: 10,
color: Color.Black
})
// 第三步:新增或者删除组件触发转场,控制新增或者删除组件
Button('Click')
.margin({ top: 320 })
.onClick(() => {
this.isPresent = !this.isPresent;
})
}
.width('100%')
.height('60%')
}
}
对多个组件添加转场效果时,可以在animation动画参数中配置不同的delay值,实现组件渐次出现消失的效果:
效果图
示例代码
c
const ITEM_COUNTS = 9;
const ITEM_COLOR = '#ED6F21';
const INTERVAL = 30;
const DURATION = 300;
@Entry
@Component
struct Index1 {
@State isGridShow: boolean = false;
private dataArray: number[] = new Array(ITEM_COUNTS);
aboutToAppear(): void {
for (let i = 0; i < ITEM_COUNTS; i++) {
this.dataArray[i] = i;
}
}
build() {
Stack() {
if (this.isGridShow) {
Grid() {
ForEach(this.dataArray, (item: number, index: number) => {
GridItem() {
Stack() {
Text((item + 1).toString())
}
.size({ width: 50, height: 50 })
.backgroundColor(ITEM_COLOR)
.transition(TransitionEffect.OPACITY
.combine(TransitionEffect.scale({ x: 0.5, y: 0.5 }))// 对每个方格的转场添加delay,实现组件的渐次出现消失效果
.animation({ duration: DURATION, curve: Curve.Friction, delay: INTERVAL * index }))
.borderRadius(10)
}
// 消失时,如果不对方格的所有父控件添加转场效果,则方格的消失转场不会生效
// 此处让方格的父控件在出现消失转场时一直以0.99的透明度显示,使得方格的转场效果不受影响
.transition(TransitionEffect.opacity(0.99))
}, (item: number) => item.toString())
}
.columnsTemplate('1fr 1fr 1fr')
.rowsGap(15)
.columnsGap(15)
.size({ width: 180, height: 180 })
// 消失时,如果不对方格的所有父控件添加转场效果,则方格的消失转场不会生效
// 此处让父控件在出现消失转场时一直以0.99的透明度显示,使得方格的转场效果不受影响
.transition(TransitionEffect.opacity(0.99))
}
}
.size({ width: '100%', height: '100%' })
.onClick(() => {
this.getUIContext()?.animateTo({
duration: DURATION + INTERVAL * (ITEM_COUNTS - 1),
curve: Curve.Friction
}, () => {
this.isGridShow = !this.isGridShow;
})
})
}
}
三、旋转屏动画
旋转屏动画主要分为两类:布局切换的旋转屏动画 和透明度变化的旋转屏动画,旨在实现屏幕显示方向变化时的自然过渡。布局切换的旋转屏动画实现较为简便,例如在module.json5中配置自动旋转(或设置窗口显示方向)即可实现。而透明度变化的旋转屏动画则需在module.json5配置的基础上,预备两套视图,在屏幕旋转时,通过视图切换,使消失的视图呈现渐隐效果,新出现的视图则渐显,从而营造流畅的视觉体验。
3.1 布局切换的旋转屏动画
布局切换时的旋转屏动画,是在屏幕显示方向改变时,为窗口与应用视图同步旋转而设计的大小和位置过渡动画。这种布局切换的旋转屏动画是系统默认的,便于开发者实现。当屏幕显示方向变化时,系统会生成窗口旋转动画,并自动调整窗口大小以匹配旋转后的尺寸。在此过程中,窗口会通知对应的应用,要求其根据新的窗口大小重新布局,产生与窗口旋转动画参数相同的布局动画。
- 切换屏幕方向即可实现布局切换的旋转屏动画效果。
效果图
c
@Entry
@Component
struct Rotation1 {
build() {
Stack() {
Image($r('app.media.mount'))
.position({ x: 100, y: 100 })
.size({ width: 150, height: 100 })
.id('image1')
}
.backgroundColor(Color.White)
.size({ width: '100%', height: '100%' })
}
}
需要在项目的module.json5文件中的abilities列表里添加"orientation ",指定为"auto_rotation "。
布局切换的旋转屏动画,会对同步旋转的窗口与应用视图做大小和位置的过渡。
c
"orientation": "auto_rotation",
3.2 透明度变化的旋转屏动画
透明度变化的旋转屏动画在屏幕显示方向变化时启用,当窗口进行旋转动画时,为旋转过程中新增或删除的组件添加默认透明度转场,以实现组件的优雅出现和消失。此功能通过监听窗口旋转事件,在事件中切换组件的视图效果,如果消失视图的根节点和新出现视图的根节点未设置转场效果,会为其自动添加默认透明度转场(即TransitionEffect.OPACITY),展现出透明度的渐隐和渐显效果。
效果图竖屏
旋转为横屏
示例代码
c
import { display } from '@kit.ArkUI';
@Entry
@Component
struct Rotation2 {
// 获取通过监听窗口的windowsSizeChange事件得到的屏幕显示方向
@StorageLink('orientation') myOrientation: display.Orientation = display.Orientation.PORTRAIT;
build() {
Stack() {
// 当屏幕显示方向变化时,切换组件的视图效果
if (this.myOrientation == display.Orientation.PORTRAIT ||
this.myOrientation == display.Orientation.PORTRAIT_INVERTED) {
Image($r('app.media.Equipment0'))
.size({ width: 158, height: 73 })
.id('image1')
// 开发者也可以通过自行设置transition的TransitionEffect.OPACITY转场效果来实现旋转屏动画的透明度变化
// .transition(TransitionEffect.OPACITY)
} else {
Image($r('app.media.Equipment1'))
.position({ x: 200, y: 200 })
.size({ width: 158, height: 73 })
.id('image2')
// 开发者也可以通过自行设置transition的TransitionEffect.OPACITY来实现旋转屏动画的透明度变化
// .transition(TransitionEffect.OPACITY)
}
}
.backgroundColor(Color.White)
.size({ width: '100%', height: '100%' })
}
}
监听窗口旋转的同步事件windowsSizeChange 来实现视图的切换。例如可在EntryAbility.ets文件的onWindowStageCreate方法中添加处理逻辑以获取屏幕的显示方向。
c
onWindowStageCreate(windowStage: window.WindowStage): void {
hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate');
let mainWindow: window.Window;
try {
mainWindow = windowStage.getMainWindowSync();
let displayClass: display.Display = display.getDefaultDisplaySync();
AppStorage.setOrCreate('orientation', displayClass.orientation);
// 监听窗口的windowsSizeChange事件,旋转屏时会触发该事件
mainWindow.on('windowSizeChange', (data) => {
console.info('Succeeded in enabling the listener for window size changes. Data: ' + JSON.stringify(data));
let displayClass: display.Display | null = null;
try {
displayClass = display.getDefaultDisplaySync();
console.info('display orientation is ' + JSON.stringify(displayClass.orientation));
// 获取屏幕的显示方向
AppStorage.set('orientation', displayClass.orientation);
} catch {
return;
}
})
} catch {
hilog.error(0x0000, 'testTag', '%{public}s', 'error');
return;
}
windowStage.loadContent('pages/Rotation2', (err) => {
if (err.code) {
hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
return;
}
hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.');
});
}
需要在项目的 module.json5 文件中的 abilities 列表里添加 "orientation",指定为 "auto_rotation"。
c
"orientation": "auto_rotation",
透明度变化的旋转屏动画,会对窗口做大小和位置的过渡,并同时对应用视图做切换过渡,且为消失隐藏的应用视图做渐隐效果,对新出现的视图做渐显的效果。




