一、概述
页面间转场是用户从一个页面切换到另一个页面时的过程,一个无缝流畅的转场动效可以提升用户的交互体验。从主页到详情页、从列表页到结果页都需要去设置一些转场动效使得用户体验更加流畅。基于用户行为和应用设计模式,我们总结出了一些常见的转场场景,包括层级转场、搜索转场、新建转场、编辑转场、通用转场、跨应用转场。
二、视角转换步骤
- 了解系统能力:首先,开发者需要深入了解HarmonyOS系统提供的转场能力和动画功能。这包括了解页面转场的机制、转场动画的类型和特性,以及如何在HarmonyOS应用中使用相关API。
- 分析UX设计视角:仔细分析UX设计所提供的转场场景和动效。理解设计师的意图,包括页面之间的过渡效果、元素的动画行为,以及何时触发和结束转场动效。
- 设计转场方案:基于分析的结果,设计出合理的转场方案。确定页面切换的触发时机、转场动画的类型和参数等。
- 使用转场能力:利用HarmonyOS提供的转场能力,如UIAbility转场、页面路由转场、组件转场,来实现页面之间的转场效果。
- 使用动画能力:利用HarmonyOS提供的动画能力,如属性动画、出现/消失转场动画等来实现元素的平移、缩放、旋转、透明度等动画行为。
- (可选)使用高级模板化转场:HarmonyOS已经为我们提供了一些基于场景化封装的相关高级模板化转场,如导航转场、模态转场、共享元素转场。
- 调试和优化:在实施转场和动效的过程中,进行调试和优化。确保转场效果流畅,动效符合预期,且满足性能要求。
三、转场场景
1、 层级转场
层级转场是指在用户界面中,从一个层级结构的界面状态转换到另一个层级结构的界面状态的过程,它通常用于在应用中进行页面间的导航和视图层级的变化。层级转场的场景可以划分为卡片、图标展开和列表展开:
- 列表展开:通常是完整的页面替换,开发者可以使用左右位移遮罩动效完成这类变化
- 卡片/图表展开:单体独立卡片展开推荐使用一镜到底动效;相对复杂的组合卡片样式则需要由开发者以更为符合用户视觉流畅感为标准,根据实际情况选择左右位移遮罩动效或一镜到底动效。
2、搜索转场
搜索转场是指在用户执行搜索操作,如在搜索栏中输入关键词并按下搜索按钮、或者直接触摸搜索图标时,应用改变应用页面以显示搜索结果的过程。它包含了固定搜索区域和非固定搜索区域两种情况:
- 固定搜索区域:在固定搜索区域中,大部分空间是不需要变化的,只是在上面增加了一层蒙版。主要变化区域集中在页眉,即搜索框和返回按键。当用户触发搜索操作时,页面可以使用淡入淡出动效来优化搜索体验,搜索框和返回按键通过渐变的方式进入视图,从而吸引用户的注意力。
- 非固定搜索区域:在非固定搜索区域中,页面的变化更加复杂。为了保持用户的注意力和流畅的体验,可以使用一镜到底的动效,让搜索框始终保持在用户视线焦点中,相对忽视页面中其余变动较大的部分。
3、新建转场
新建转场是指用户创建新内容或实体时,应用页面发生的过渡效果,它可以让用户感知到新的事物的添加或创建,并提供一种连贯和引人注目的视觉切换。由于新建页面中需要完成整个页面的替换,推荐开发者使用左右位移遮罩作为转场动效。
对于新建转场,推荐使用系统转场,页面转场采用左右位移的运动方式,不应单帧直接切换或上下位移切换,曲线优先使用弹簧曲线。
4、编辑转场
用户对现有内容或实体进行编辑时,例如点击“编辑”按钮、选择要编辑的项目或内容,或者执行其他与编辑相关的动作,应用应提供动效引导用户进入一个用于编辑现有内容的页面,修改所需的信息。在这个场景下,开发者需要达成的视觉效果是从编辑按键处弹出编辑页面,类似于单体卡片展开的效果。但由于一般的编辑按键并没有分明的外框,并不适用一镜到底的动效,此时淡入淡出能够提供类似于一镜到底的效果。
5、通用转场
通用转场是一种广泛适用于不同情境和应用类型的页面过渡效果,目的是提供一种通用的、可重复使用的方式,以改善用户页面之间的切换,增强用户体验。其关键点在于要适用各种应用情境,包括不同类型的应用(例如社交媒体、电子商务、新闻等)和不同操作(例如导航、搜索、编辑等)。这就需要一种通用的、不需要复杂操作的动效来完成跳转任务,而缩放能够满足绝大多数用户的需求和视觉体验感受。
6、跨应用转场
跨应用转场是指用户从一个应用程序切换到另一个应用程序,用户能够无缝地从一个应用切换到另一个应用,而不会感到中断或不适。和以上几类转场都不同的是,用户点击应用内的链接、按钮或执行其他与外部应用交互的动作后,页面的跳转已经不仅仅存在于页面与页面之间,而是应用与应用之间,为此,推荐开发者使用专为此设计的左右间隔位移动效。
7、场景解构
转场是由交互行为引起的界面变化,分析界面元素在过程中的意义,定义其在转场中所在的类型,并将它们进行分类,元素所属的类别会影响它们使用怎样的转场能力,同时也将决定用什么类型的曲线和时长。
- 进场元素:转场中新出现的元素,一般是结果界面上的构成元素。
- 出场元素:转场中消失的元素,一般是上一界面中的构成元素。
- 持续元素:转场中持续存在的元素,可以是元素在布局上的变化,也可以是某种连续性的动画效果,整个过程无中断。
- 静止元素:转场中无任何变化的元素。
四、转场场景开发
1、转场能力
HarmonyOS为开发者提供了UIAbility转场、页面路由和组件转场三种方式,在选择转场方式时,开发者需要考虑用户体验、界面一致性和业务需求,确保所选导航组件能够提供直观、易用的导航方式,帮助应用实现更好的转场效果。
- UIAbility组件间交互:UIAbility是系统调度的最小单元。通过调用startAbility()方法启动UIAbility实现在设备内的功能模块之间的跳转。该UIAbility可以是应用内的其他UIAbility,也可以是其他应用的UIAbility(例如启动三方支付UIAbility)。
- 页面路由(@ohos.router):页面路由指在应用程序中实现不同页面之间的跳转和数据传递。HarmonyOS提供了Router模块,通过不同的url地址,可以方便地进行页面路由,轻松地访问不同的页面。
- 组件转场:组件转场是指通过HarmonyOS提供的各类组件来实现转场效果,以便更加便捷地展示不同的内容或功能模块。在组件转场中,可以使用诸如页面切换动画、过渡效果、布局变化等方式来实现页面之间的平滑切换
2、动画能力
转场动画是指对将要出现或消失的组件做动画,对始终出现的组件做动画应使用属性动画。转场动画主要为了让开发者从繁重的消失节点管理中解放出来,如果用属性动画做组件转场,开发者需要在动画结束回调中删除组件节点。同时,由于动画结束前已经删除的组件节点可能会重新出现,还需要在结束回调中增加对节点状态的判断。
转场动画分为基础转场和高级模板化转场,出现/消失转场是一种基础转场,是对新增、消失的控件实现动画效果的能力。为了简化开发者工作,HarmonyOS提供了以下高级模板,将属性动画和出现消失动画封装,开发者只需调用接口,可以轻松的完成页面转场:
- 导航转场:页面的路由转场方式,对应一个界面消失,另外一个界面出现的动画效果,如设置应用一级菜单切换到二级界面。
- 模态转场:新的界面覆盖在旧的界面之上的动画,旧的界面不消失,新的界面出现,如弹框就是典型的模态转场动画。
- 共享元素转场:共享元素转场是一种界面切换时对相同或者相似的元素做的一种位置和大小匹配的过渡动画效果。
五、案例实践
转场场景 | 动效 | 推荐使用的转场能力 | ||
---|---|---|---|---|
层级转场 | 卡片/图标展开 | 组合卡片样式 | 左右遮罩位移(可考虑一镜到底) | 页面路由转场、导航转场模板 |
单体卡片样式 | 一镜到底 | 模态转场模板、共享元素转场模板 | ||
列表展开 | 列表样式(非卡片) | 左右遮罩位移 | 页面路由转场、导航转场模板 | |
混合样式(卡片+列表+图标) | 左右遮罩位移 | 页面路由转场、导航转场模板 | ||
搜索转场 | 固定搜索区域 | 淡入淡出 | 组件转场、模态转场模板 | |
非固定搜索区域 | 一镜到底 | 共享元素转场模板、模态转场模板 | ||
新建转场 | 左右遮罩位移 | 组件转场、模态转场模板 | ||
编辑转场 | 淡入淡出 | 组件转场 | ||
通用转场 | 缩放 | 组件转场、模态转场模板 | ||
跨应用转场 | 左右间隔位移 | 组件转场 |
1、导航转场模板实现层级转场
实现效果:在本场景中,需要在地图上显示6个具体的坐标位置,点击对应的坐标icon后,能跳转到对应的详情介绍页面中去
build() {
Stack() {
// Navigation组件一般可以用来实现标题栏、工具栏、导航栏等。
Navigation() {
//分别对每一个地图上的导航mapElements进行渲染和链接
ForEach(this.mapElements,
(item: MapElement) => {
Column() {
// NavRouter导航组件,默认提供点击响应处理,不需要开发者自定义点击事件逻辑。NavRouter必须包含两个子组件,其中第二个子组件必须为NavDestination。
NavRouter() {
//地图上对应导航位置图标资源的渲染
AnimIcon({ item: item })
......
// 作为NavRouter组件的子组件,用于显示导航内容区。其中IntroductionPage即为即将跳转的对应页面
NavDestination() {
IntroductionPage({currentZoneId: item.id})
}
}
// 指定点击NavRouter跳转到NavDestination页面时使用的路由模式。此处不需要保存原页面,选择replace方式,跳转后页面销毁,且该页面信息从路由栈中清除。
.mode(NavRouteMode.REPLACE)
}
......
},
(item: MapElement) => JSON.stringify(item)
)
}
// 将mode属性设置为NavigationMode.Stack,Navigation组件即可设置为单页面显示模式。
.mode(NavigationMode.Stack)
.hideNavBar(true)
.hideTitleBar(true)
.hideBackButton(true)
.hideToolBar(true)
}
}
2、Tabs组件实现层级转场
实现效果:在本场景中,需要完成是效果是在用户主页切换显示收藏、浏览、成就三个页面
build() {
......
ListItem() {
// Tabs组件的页面组成包含两个部分,分别是TabContent和TabBar。
Tabs() {
// TabContent仅在Tabs中使用,对应一个切换页签的内容视图
TabContent() {
// 收藏列表页面
CollectedResourceView({ innerScroller: this.innerScroller, outerScroller: this.outScroller })
}
// tabBar设置TabBar上显示内容。每一个TabContent对应的内容需要有一个页签,可以通过TabContent的tabBar属性进行配置。
.tabBar(this.MineTabs(TabInfo.COLLECTED, $r('app.string.my_collected')))
.backgroundColor($r('app.color.hmos_background_color_white'))
TabContent() {
// 浏览列表页面
ViewedResourceView({ innerScroller: this.innerScroller, outerScroller: this.outScroller })
}
.tabBar(this.MineTabs(TabInfo.VIEWED, $r('app.string.my_viewed')))
.backgroundColor($r('app.color.hmos_background_color_white'))
TabContent() {
// 成就展示页面
AchievesView()
}
.tabBar(this.MineTabs(TabInfo.ACHIEVEMENT, $r('app.string.my_achieve')))
.backgroundColor($r('app.color.hmos_background_color_white'))
}
.onChange((index: number) => {
this.currentIndex = index;
})
......
}
......
}
3、模态转场模板实现通用转场
实现效果:在本场景中,当用户点击介绍详情页中的图片时,图片会放大展示,覆盖在原有界面上,并且点击空白处完成返回操作
build() {
Stack() {
ForEach(this.introductionData.imageList, (item: ResourceStr, index?: number) => {
if (index !== undefined) {
Row() {
Image(item)
.objectFit(ImageFit.ScaleDown)
// geometryTransition绑定id
.geometryTransition(index === this.currentIndex ? 'share_' + this.currentIndex : '')
......
}
.onClick(() => {
// 设置动画参数,curves.springMotion构造弹性动画对象
animateTo({ curve: curves.springMotion(Const.SPRING_RESPONSE, Const.DAMPING_FRACTION) }, () => {
this.isPresent = !this.isPresent;
});
})
}
}, (item: ResourceStr) => JSON.stringify(item))
}
.width(Const.FULL_PERCENT)
.height($r('app.float.swiper_height'))
.alignContent(Alignment.Center)
// 通过bindContentCover属性为组件绑定全屏模态页面,在组件插入和删除时,可通过设置转场参数ModalTransition显示过渡动效。
.bindContentCover($$this.isPresent, this.PhotoBuilder, {modalTransition: ModalTransition.ALPHA, onAppear: () => {}, onDisappear: () => {}})
}
@Builder
PhotoBuilder() {
ImageViewDialog({ isPresent: $isPresent, currentIndex: $currentIndex })
.width(Const.FULL_PERCENT)
.height(Const.FULL_PERCENT)
.transition(TransitionEffect.opacity(1))
.backgroundBlurStyle(this.isPresent ? BlurStyle.Regular : BlurStyle.NONE, { colorMode: ThemeColorMode.DARK })
}
build() {
Stack() {
Swiper() {
ForEach(this.introductionData.imageList, (item: Resource, index?: number) => {
Column() {
......
Image(item)
.width(Const.FULL_PERCENT)
.syncLoad(true)
.objectFit(ImageFit.Contain)
.gesture(
PinchGesture()
.onActionStart(() => {
this.isGesture = true;
})
.onActionUpdate((event?: GestureEvent) => {
if (event) {
this.imgScale = this.curScale * event.scale;
}
})
.onActionEnd(() => {
this.limitScale(false);
})
)
// geometryTransition绑定id
.geometryTransition(index === this.currentIndex && !this.isGesture ? 'share_' + index : '')
......
}
.width(Const.FULL_PERCENT)
.height(Const.FULL_PERCENT)
.justifyContent(FlexAlign.Center)
})
}
......
}
}
六、总结
- 符合界面设计需求:动效应该与应用的整体设计风格和用户期望相匹配,不应过于炫目或过于简单。动效的设计应该与应用的功能和交互逻辑相符,能够提供有意义的反馈和引导。
- 提高用户体验:动效可以增强用户对界面操作的反馈感知,使用户能够更直观地理解应用的状态和变化。例如,在页面切换时使用渐变效果或位移动画,可以使用户感到界面的流畅性和连贯性,提高用户的满意度和使用体验。
- 控制动效的频率和时长:动效的过度使用可能会让用户感到疲劳或干扰用户的操作。因此,在设计动效时需要注意控制动效的频率和时长,避免过多的动效干扰用户的操作和阅读。
- 考虑性能和流畅度:在使用动效时,需要考虑应用的性能和流畅度。过多或复杂的动效可能会导致应用的性能下降,影响用户的体验。因此,在设计动效时需要合理权衡动效的效果和应用的性能,确保动效的运行流畅和稳定。