未验证 提交 df14c4d2 编写于 作者: O openharmony_ci 提交者: Gitee

!22719 动效文档修改

Merge pull request !22719 from wangluyu/DocsModify0822
...@@ -4,8 +4,19 @@ ...@@ -4,8 +4,19 @@
UI(用户界面)中包含开发者与设备进行交互时所看到的各种组件(如时间、壁纸等)。属性作为接口,用于控制组件的行为。例如,开发者可通过位置属性调整组件在屏幕上的位置。 UI(用户界面)中包含开发者与设备进行交互时所看到的各种组件(如时间、壁纸等)。属性作为接口,用于控制组件的行为。例如,开发者可通过位置属性调整组件在屏幕上的位置。
属性值的变化,通常会引起UI的变化。动画可在UI发生改变时,添加流畅的过渡效果。以应用启动为例,当用户点击应用图标时,应用窗口应取代桌面,作为屏幕的主要显示内容。如果不添加动画,相关属性将在一瞬间完成变化,应用窗口直接替换桌面,出现不连贯的视觉感受。动画可以解决UI变化时在视觉上不连续的问题 属性值的变化,通常会引起UI的变化。动画可在UI发生改变时,添加流畅的过渡效果。如果不加入动画,属性将在一瞬间完成变化。造成突兀感的同时,容易导致用户失去视觉焦点
![zh-cn_image_20230822](figures/zh-cn_image_20230822.gif)
动画的目的包括:
- 使界面的过渡自然流畅。
- 增强用户从界面获得的反馈感和互动感。
- 在内容加载等场景中,增加用户的耐心,缓解等待带来的不适感。
- 引导用户了解和操作设备。
在需要为UI变化添加过渡的场景,都可以使用动画,如开机、应用启动退出、下拉进入控制中心等。这些动画可向用户提供关于其操作的反馈,并有助于让用户始终关注界面。
ArkUI中提供多种动画接口(属性动画、转场动画等),用于驱动属性值按照设定的动画参数,从起始值逐渐变化到终点值。尽管变化过程中参数值并非绝对的连续,而是具有一定的离散性。但由于人眼会产生视觉暂留,所以最终看到的就是一个“连续“的动画。UI的一次改变称为一个动画帧,对应一次屏幕刷新。决定动画流畅度的一个重要指标就是帧率FPS(Frame Per Second),即每秒的动画帧数,帧率越高则动画就会越流畅。ArkUI中,动画参数包含了如动画时长、动画曲线等参数。动画曲线作为主要因素,决定了属性值变化的规律。以线性动画曲线为例,在动画时长内,属性值将从起点值匀速变化到终点值。属性过快或过慢的变化,都可能带来不好的视觉感受,影响用户体验。因此动画参数特别是动画曲线,需要结合场景和曲线特点进行设计和调整。 ArkUI中提供多种动画接口(属性动画、转场动画等),用于驱动属性值按照设定的动画参数,从起始值逐渐变化到终点值。尽管变化过程中参数值并非绝对的连续,而是具有一定的离散性。但由于人眼会产生视觉暂留,所以最终看到的就是一个“连续“的动画。UI的一次改变称为一个动画帧,对应一次屏幕刷新。决定动画流畅度的一个重要指标就是帧率FPS(Frame Per Second),即每秒的动画帧数,帧率越高则动画就会越流畅。ArkUI中,动画参数包含了如动画时长、动画曲线等参数。动画曲线作为主要因素,决定了属性值变化的规律。以线性动画曲线为例,在动画时长内,属性值将从起点值匀速变化到终点值。属性过快或过慢的变化,都可能带来不好的视觉感受,影响用户体验。因此动画参数特别是动画曲线,需要结合场景和曲线特点进行设计和调整。
...@@ -20,8 +31,8 @@ ArkUI中提供多种动画接口(属性动画、转场动画等),用于驱 ...@@ -20,8 +31,8 @@ ArkUI中提供多种动画接口(属性动画、转场动画等),用于驱
- 属性动画:最基础的动画类型,按照动画参数逐帧驱动属性的变化,产生一帧帧的动画效果。 - 属性动画:最基础的动画类型,按照动画参数逐帧驱动属性的变化,产生一帧帧的动画效果。
- 转场动画:为组件在出现和消失时添加过渡动画。为了保证动画一致性,部分接口动画曲线已内置,不支持开发者自定义。 - 转场动画:为组件在出现和消失时添加过渡动画。为了保证动画一致性,部分接口动画曲线已内置,不支持开发者自定义。
- 不推荐使用Ability去组合界面:Ability是一个任务,会在多任务界面独立显示一个卡片,Ability之间的跳转是任务之间的跳转。以应用内查看大图的典型场景为例,不建议应用内调用图库Ability去打开图片查看大图,会导致任务的跳转,图库Ability也会加入多任务界面中。正确的方式是应用内构建大图组件,通过模态转场去调起大图组件,所有的界面都在一个Ability内闭环。 - 不推荐在应用内使用UIAbility组合所有的界面:UIAbility是一个任务,会在多任务界面独立显示一个卡片,UIAbility之间的跳转是任务之间的跳转。以应用内查看大图的典型场景为例,不建议应用内调用图库的UIAbility去打开图片查看大图,会导致任务的跳转,图库的UIAbility也会加入多任务界面中。正确的方式是应用内构建大图组件,通过模态转场去调起大图组件,一个任务内的所有的界面都在一个UIAbility内闭环。
- 使用Navigation组件实现导航,不要使用page导航方式。page+router方式会导致页面之间的割裂,不利于实现联动的转场效果,并且不支持一次开发多端部署。 - 导航转场中,应使用Navigation组件实现转场动画。过去的page+router方式在实现导航转场过程中,因为page和page之间相互独立,其联动动画效果受限。不仅容易导致页面之间的割裂,并且不支持一次开发多端部署。
- 组件动画:组件提供默认动效(如List的滑动动效)便于开发者使用,同时部分组件还支持定制化动效。 - 组件动画:组件提供默认动效(如List的滑动动效)便于开发者使用,同时部分组件还支持定制化动效。
......
...@@ -224,7 +224,6 @@ export const taskDataArr: Array<TaskData> = ...@@ -224,7 +224,6 @@ export const taskDataArr: Array<TaskData> =
export struct TaskSwitchMainPage { export struct TaskSwitchMainPage {
displayWidth: number = WindowManager.getInstance().getDisplayWidth(); displayWidth: number = WindowManager.getInstance().getDisplayWidth();
scroller: Scroller = new Scroller(); scroller: Scroller = new Scroller();
bgImage: Resource = $r('app.media.share');
cardSpace: number = 0; // 卡片间距 cardSpace: number = 0; // 卡片间距
cardWidth: number = this.displayWidth / 2 - this.cardSpace / 2; // 卡片宽度 cardWidth: number = this.displayWidth / 2 - this.cardSpace / 2; // 卡片宽度
cardHeight: number = 400; // 卡片高度 cardHeight: number = 400; // 卡片高度
......
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
``` ```
完整的示例代码和效果如下 完整的示例代码和效果如下
```ts ```ts
import curves from '@ohos.curves'; import curves from '@ohos.curves';
......
...@@ -12,13 +12,81 @@ ...@@ -12,13 +12,81 @@
```ts ```ts
@Component
export struct MyFirstIndex {
@Consume('pathInfos') pathInfos: NavPathStack
name: string = ''
@State value: string = ''
build() {
NavDestination() {
Column() {
Blank()
Text('通过点击NavRouter区域进入导航目标页面' + this.value)
.fontStyle(FontStyle.Italic)
.lineHeight(35)
.fontSize(25)
.fontColor(Color.Black)
.textAlign(TextAlign.Center)
.letterSpacing(5)
.textShadow({ radius: 2, offsetX: 4, offsetY: 4, color: 0x909399 })
.padding({ left: 30, right: 30 })
Blank()
Button('返回上级页面')
.backgroundColor(Color.Black)
.onClick(() => {
this.pathInfos.pop()
})
Blank()
}
.size({ width: '100%', height: '100%' })
}.title(this.name + '二级页面')
}
}
@Component
export struct MySecondIndex {
@Consume('pathInfos') pathInfos: NavPathStack;
name: String = '';
@State value: String = ''
build() {
NavDestination() {
Column() {
Blank()
Text('通过更新路由栈数据对象进入导航目标界面' + this.value)
.fontStyle(FontStyle.Italic)
.lineHeight(35)
.fontSize(25)
.fontColor(Color.Black)
.textAlign(TextAlign.Center)
.letterSpacing(5)
.textShadow({ radius: 2, offsetX: 4, offsetY: 4, color: 0x909399 })
.padding({ left: 30, right: 30 })
Blank()
Button('返回上级页面')
.backgroundColor(Color.Black)
.onClick(() => {
this.pathInfos.pop()
})
Blank()
}
.size({ width: '100%', height: '100%' })
}.title(this.name + '二级页面')
}
}
@Entry @Entry
@Component @Component
struct NavigationDemo { struct NavigationDemo {
private listArray: Array<number> = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; @Provide('pathInfos') pathInfos: NavPathStack = new NavPathStack()
private listArray: Array<Number> = [0, 1, 2]
// 设置标题栏菜单组件,如果不需要标题栏组件,可以不设置 @Builder NavPathStack() {
@Builder NavigationMenus() {
Column() { Column() {
Text('menu') Text('menu')
.fontColor('#182431') .fontColor('#182431')
...@@ -30,102 +98,103 @@ struct NavigationDemo { ...@@ -30,102 +98,103 @@ struct NavigationDemo {
.alignItems(HorizontalAlign.Start) .alignItems(HorizontalAlign.Start)
} }
// Navigation的navDestination属性方法设置的构造函数,在路由栈变化时触发该构建数创建新的路由页面
@Builder myRouter(name: string, param: string) {
if (name == '方式一进入') {
MyFirstIndex({ name: name, value: param })
}
if (name == '方式二进入') {
MySecondIndex({ name: name, value: param })
}
}
build() { build() {
Stack() { Column() {
Column() { Navigation(this.pathInfos) {
// 定义Navigation组件,设置显示模式,设置标题 TextInput({ placeholder: 'search...' })
Navigation() { .width('90%')
// 这里定义了一个输入法框组件 .height(40)
TextInput({ placeholder: 'search...' }) .margin({ bottom: 10 })
.width('90%')
.height(40) // 通过List定义导航的一级界面
.backgroundColor('#ededed') List({ space: 12, initialIndex: 0 }) {
.margin({ bottom: 10 }) ForEach(this.listArray, (item) => {
ListItem() {
// 通过List定义导航的一级界面 // 通过NavDestination定义导航目标界面,界面之间同故宫组件间的状态变量或者普通变量传递参数
List({ space: 12, initialIndex: 0 }) { // NavRouter点击之后会传递name和param参数给Navigation的navDestination书香方法设置的builder函数(myRouter)
ForEach(this.listArray, (item) => { NavRouter({ name: '方式一进入', param: '' + item }) {
ListItem() { Row() {
// 通过NavRouter定义导航转场,通过NavDestination定义导航目标界面,界面之间通过组件间的状态变量或者普通变量传递参数
// NavRouter必须包含两个子组件,第一个组件是导航一级界面,第二个子组件必须为NavDestination为导航目标界面
NavRouter() {
// 第一个组件:导航的一级界面显示的组件
Row() { Row() {
Row() Text('' + item)
.width(40) .fontColor(Color.White)
.height(40) .fontSize(15)
.backgroundColor('#a8a8a8') .fontWeight(FontWeight.Bold)
.margin({ right: 12 })
.borderRadius(20)
Column() {
Text('导航一级页面')
.fontSize(16)
.lineHeight(21)
.fontWeight(FontWeight.Medium)
Text('点击跳转目标子页面')
.fontSize(13)
.lineHeight(21)
.fontColor('#a8a8a8')
}
.alignItems(HorizontalAlign.Start)
Blank()
Row()
.width(15)
.height(15)
.margin({ right: 12 })
.border({
width: { top: 2, right: 2 },
color: 0xcccccc
})
.rotate({ angle: 45 })
} }
.borderRadius(15) .width(40)
.shadow({ radius: 100, color: '#ededed' }) .height(40)
.width('90%') .backgroundColor('#a8a8a8')
.alignItems(VerticalAlign.Center) .margin({ right: 12 })
.padding({ left: 16, top: 12, bottom: 12 }) .borderRadius(20)
.height(80) .justifyContent(FlexAlign.Center)
// 第二个组件:导航的目的界面 Column() {
NavDestination() { Text('导航一级页面')
// 目的界面的内容,这里一般为自定义的Component .fontSize(16)
Column() { .lineHeight(21)
Text("导航目标页面" + item + "内容") .fontWeight(FontWeight.Medium)
.fontSize(20) Text('点击跳转目标子页面' + item)
.fontColor(Color.Black) .fontSize(13)
.textAlign(TextAlign.Center) .lineHeight(21)
.width('100%') .fontColor('#a8a8a8')
.height('100%')
}
.width('100%')
.height('100%')
.backgroundColor(0xf5f5f5)
} }
.title('导航目标页面') // 这里定义二级界面的标题 .alignItems(HorizontalAlign.Start)
Blank()
Row()
.width(15)
.height(15)
.margin({ right: 12 })
.border({
width: { top: 2, right: 2 },
color: 0xcccccc
})
.rotate({ angle: 45 })
} }
.borderRadius(15)
.shadow({ radius: 100, color: '#ededed' })
.width('90%')
.alignItems(VerticalAlign.Center)
.padding({ left: 16, top: 12, bottom: 12 })
.height(80)
.backgroundColor(Color.White)
} }
.width('100%') }
}, item => item) .width('100%')
} }, item => item)
.listDirection(Axis.Vertical)
.edgeEffect(EdgeEffect.Spring)
.sticky(StickyStyle.Header)
.chainAnimation(false)
.borderRadius(15)
.width('100%')
.height('100%')
} }
.listDirection(Axis.Vertical)
.edgeEffect(EdgeEffect.Spring)
.sticky(StickyStyle.Header)
.chainAnimation(false)
.width('100%') .width('100%')
.mode(NavigationMode.Auto) // 设置显示模式为Auto
.title('导航转场') // 设置标题文字 Column()
.titleMode(NavigationTitleMode.Full) // 设置标题栏模式 .height('20%')
.menus(this.NavigationMenus) // 设置标题栏菜单
Button('点击进入下一页')
.onClick(() => {
// 通过操作绑定的路由栈数据对象触发Navigation更新,基于pathInfos数据变化场景触发navDestination属性方法构建新页面
this.pathInfos.pushPathByName('方式二进入', 4)
})
} }
.navDestination(this.myRouter)
.width('100%') .width('100%')
.mode(NavigationMode.Auto)
.title('导航转场') // 设置标题文字
} }
.size({ width: '100%', height: '100%' })
.backgroundColor(0xf4f4f5)
} }
} }
``` ```
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册