模态转场是新的界面覆盖在旧的界面上,旧的界面不消失的一种转场方式。
表1 模态转场接口
接口 | 说明 | 使用场景 |
---|---|---|
bindContentCover | 弹出全屏的模态组件。 | 用于自定义全屏的模态展示界面,结合转场动画和共享元素动画可实现复杂转场动画效果,如缩略图片点击后查看大图。 |
bindSheet | 弹出半模态组件。 | 用于半模态展示界面,如分享框。 |
bindMenu | 弹出菜单,点击组件后弹出。 | 需要Menu菜单的场景,如一般应用的“+”号键。 |
bindContextMenu | 弹出菜单,长按或者右键点击后弹出。 | 长按浮起效果,一般结合拖拽框架使用,如桌面图标长按浮起。 |
bindPopup | 弹出Popup弹框。 | Popup弹框场景,如点击后对某个组件进行临时说明。 |
if | 通过if新增或删除组件。 | 用来在某个状态下临时显示一个界面,这种方式的返回导航需要由开发者监听接口实现。 |
使用bindContentCover构建全屏模态转场效果
bindContentCover接口用于为组件绑定全屏模态页面,在组件出现和消失时可通过设置转场参数ModalTransition添加过渡动效。
-
定义全屏模态转场效果bindContentCover。
-
定义模态展示界面。
1. // 通过@Builder构建模态展示界面
2. @Builder MyBuilder() {
3. Column() {
4. Text('my model view')
5. }
6. // 通过转场动画实现出现消失转场动画效果,transition需要加在builder下的第一个组件
7. .transition(TransitionEffect.translate({ y: 1000 }).animation({ curve: curves.springMotion(0.6, 0.8) }))
8. }
- 通过模态接口调起模态展示界面,通过转场动画或者共享元素动画去实现对应的动画效果。
1. // 模态转场控制变量
2. @State isPresent: boolean = false;
4. Button('Click to present model view')
5. // 通过选定的模态接口,绑定模态展示界面,ModalTransition是内置的ContentCover转场动画类型,这里选择None代表系统不加默认动画,通过onDisappear控制状态变量变换
6. .bindContentCover(this.isPresent, this.MyBuilder(), {
7. modalTransition: ModalTransition.NONE,
8. onDisappear: () => {
9. this.isPresent = !this.isPresent;
10. }
11. })
12. .onClick(() => {
13. // 改变状态变量,显示模态界面
14. this.isPresent = !this.isPresent;
15. })
完整示例代码和效果如下。
1. import { curves } from '@kit.ArkUI';
3. interface PersonList {
4. name: string,
5. cardnum: string
6. }
8. @Entry
9. @Component
10. struct BindContentCoverDemo {
11. private personList: Array<PersonList> = [
12. { name: '王**', cardnum: '1234***********789' },
13. { name: '宋*', cardnum: '2345***********789' },
14. { name: '许**', cardnum: '3456***********789' },
15. { name: '唐*', cardnum: '4567***********789' }
16. ];
17. // 第一步:定义全屏模态转场效果bindContentCover
18. // 模态转场控制变量
19. @State isPresent: boolean = false;
21. // 第二步:定义模态展示界面
22. // 通过@Builder构建模态展示界面
23. @Builder
24. MyBuilder() {
25. Column() {
26. Row() {
27. Text('选择乘车人')
28. .fontSize(20)
29. .fontColor(Color.White)
30. .width('100%')
31. .textAlign(TextAlign.Center)
32. .padding({ top: 30, bottom: 15 })
33. }
34. .backgroundColor(0x007dfe)
36. Row() {
37. Text('+ 添加乘车人')
38. .fontSize(16)
39. .fontColor(0x333333)
40. .margin({ top: 10 })
41. .padding({ top: 20, bottom: 20 })
42. .width('92%')
43. .borderRadius(10)
44. .textAlign(TextAlign.Center)
45. .backgroundColor(Color.White)
46. }
48. Column() {
49. ForEach(this.personList, (item: PersonList, index: number) => {
50. Row() {
51. Column() {
52. if (index % 2 == 0) {
53. Column()
54. .width(20)
55. .height(20)
56. .border({ width: 1, color: 0x007dfe })
57. .backgroundColor(0x007dfe)
58. } else {
59. Column()
60. .width(20)
61. .height(20)
62. .border({ width: 1, color: 0x007dfe })
63. }
64. }
65. .width('20%')
67. Column() {
68. Text(item.name)
69. .fontColor(0x333333)
70. .fontSize(18)
71. Text(item.cardnum)
72. .fontColor(0x666666)
73. .fontSize(14)
74. }
75. .width('60%')
76. .alignItems(HorizontalAlign.Start)
78. Column() {
79. Text('编辑')
80. .fontColor(0x007dfe)
81. .fontSize(16)
82. }
83. .width('20%')
84. }
85. .padding({ top: 10, bottom: 10 })
86. .border({ width: { bottom: 1 }, color: 0xf1f1f1 })
87. .width('92%')
88. .backgroundColor(Color.White)
89. })
90. }
91. .padding({ top: 20, bottom: 20 })
93. Text('确认')
94. .width('90%')
95. .height(40)
96. .textAlign(TextAlign.Center)
97. .borderRadius(10)
98. .fontColor(Color.White)
99. .backgroundColor(0x007dfe)
100. .onClick(() => {
101. this.isPresent = !this.isPresent;
102. })
103. }
104. .size({ width: '100%', height: '100%' })
105. .backgroundColor(0xf5f5f5)
106. // 通过转场动画实现出现消失转场动画效果
107. .transition(TransitionEffect.translate({ y: 1000 }).animation({ curve: curves.springMotion(0.6, 0.8) }))
108. }
110. build() {
111. Column() {
112. Row() {
113. Text('确认订单')
114. .fontSize(20)
115. .fontColor(Color.White)
116. .width('100%')
117. .textAlign(TextAlign.Center)
118. .padding({ top: 30, bottom: 60 })
119. }
120. .backgroundColor(0x007dfe)
122. Column() {
123. Row() {
124. Column() {
125. Text('00:25')
126. Text('始发站')
127. }
128. .width('30%')
130. Column() {
131. Text('G1234')
132. Text('8时1分')
133. }
134. .width('30%')
136. Column() {
137. Text('08:26')
138. Text('终点站')
139. }
140. .width('30%')
141. }
142. }
143. .width('92%')
144. .padding(15)
145. .margin({ top: -30 })
146. .backgroundColor(Color.White)
147. .shadow({ radius: 30, color: '#aaaaaa' })
148. .borderRadius(10)
150. Column() {
151. Text('+ 选择乘车人')
152. .fontSize(18)
153. .fontColor(Color.Orange)
154. .fontWeight(FontWeight.Bold)
155. .padding({ top: 10, bottom: 10 })
156. .width('60%')
157. .textAlign(TextAlign.Center)
158. .borderRadius(15)// 通过选定的模态接口,绑定模态展示界面,ModalTransition是内置的ContentCover转场动画类型,这里选择DEFAULT代表设置上下切换动画效果,通过onDisappear控制状态变量变换。
159. .bindContentCover(this.isPresent, this.MyBuilder(), {
160. modalTransition: ModalTransition.DEFAULT,
161. onDisappear: () => {
162. this.isPresent = !this.isPresent;
163. }
164. })
165. .onClick(() => {
166. // 第三步:通过模态接口调起模态展示界面,通过转场动画或者共享元素动画去实现对应的动画效果
167. // 改变状态变量,显示模态界面
168. this.isPresent = !this.isPresent;
169. })
170. }
171. .padding({ top: 60 })
172. }
173. }
174. }
使用bindSheet构建半模态转场效果
bindSheet属性可为组件绑定半模态页面,在组件出现时可通过设置自定义或默认的内置高度确定半模态大小。构建半模态转场动效的步骤基本与使用bindContentCover构建全屏模态转场动效相同。
完整示例和效果如下。
1. @Entry
2. @Component
3. struct BindSheetDemo {
4. // 半模态转场显示隐藏控制
5. @State isShowSheet: boolean = false;
6. private menuList: string[] = ['不要辣', '少放辣', '多放辣', '不要香菜', '不要香葱', '不要一次性餐具', '需要一次性餐具'];
8. // 通过@Builder构建半模态展示界面
9. @Builder
10. mySheet() {
11. Column() {
12. Flex({ direction: FlexDirection.Row, wrap: FlexWrap.Wrap }) {
13. ForEach(this.menuList, (item: string) => {
14. Text(item)
15. .fontSize(16)
16. .fontColor(0x333333)
17. .backgroundColor(0xf1f1f1)
18. .borderRadius(8)
19. .margin(10)
20. .padding(10)
21. })
22. }
23. .padding({ top: 18 })
24. }
25. .width('100%')
26. .height('100%')
27. .backgroundColor(Color.White)
28. }
30. build() {
31. Column() {
32. Text('口味与餐具')
33. .fontSize(28)
34. .padding({ top: 30, bottom: 30 })
35. Column() {
36. Row() {
37. Row()
38. .width(10)
39. .height(10)
40. .backgroundColor('#a8a8a8')
41. .margin({ right: 12 })
42. .borderRadius(20)
44. Column() {
45. Text('选择点餐口味和餐具')
46. .fontSize(16)
47. .fontWeight(FontWeight.Medium)
48. }
49. .alignItems(HorizontalAlign.Start)
51. Blank()
53. Row()
54. .width(12)
55. .height(12)
56. .margin({ right: 15 })
57. .border({
58. width: { top: 2, right: 2 },
59. color: 0xcccccc
60. })
61. .rotate({ angle: 45 })
62. }
63. .borderRadius(15)
64. .shadow({ radius: 100, color: '#ededed' })
65. .width('90%')
66. .alignItems(VerticalAlign.Center)
67. .padding({ left: 15, top: 15, bottom: 15 })
68. .backgroundColor(Color.White)
69. // 通过选定的半模态接口,绑定模态展示界面,style中包含两个参数,一个是设置半模态的高度,不设置时默认高度是Large,一个是是否显示控制条DragBar,默认是true显示控制条,通过onDisappear控制状态变量变换。
70. .bindSheet(this.isShowSheet, this.mySheet(), {
71. height: 300,
72. dragBar: false,
73. onDisappear: () => {
74. this.isShowSheet = !this.isShowSheet;
75. }
76. })
77. .onClick(() => {
78. this.isShowSheet = !this.isShowSheet;
79. })
80. }
81. .width('100%')
82. }
83. .width('100%')
84. .height('100%')
85. .backgroundColor(0xf1f1f1)
86. }
87. }
使用bindMenu实现菜单弹出效果
bindMenu为组件绑定弹出式菜单,通过点击触发。完整示例和效果如下。
1. class BMD{
2. value:ResourceStr = ''
3. action:() => void = () => {}
4. }
5. @Entry
6. @Component
7. struct BindMenuDemo {
9. // 第一步: 定义一组数据用来表示菜单按钮项
10. @State items:BMD[] = [
11. {
12. value: '菜单项1',
13. action: () => {
14. console.info('handle Menu1 select')
15. }
16. },
17. {
18. value: '菜单项2',
19. action: () => {
20. console.info('handle Menu2 select')
21. }
22. },
23. ]
25. build() {
26. Column() {
27. Button('click')
28. .backgroundColor(0x409eff)
29. .borderRadius(5)
30. // 第二步: 通过bindMenu接口将菜单数据绑定给元素
31. .bindMenu(this.items)
32. }
33. .justifyContent(FlexAlign.Center)
34. .width('100%')
35. .height(437)
36. }
37. }
使用bindContextMenu实现菜单弹出效果
bindContextMenu为组件绑定弹出式菜单,通过长按或右键点击触发。完整示例和效果如下。
完整示例和效果如下。
1. @Entry
2. @Component
3. struct BindContextMenuDemo {
4. private menu: string[] = ['保存图片', '收藏', '搜一搜'];
5. private pics: Resource[] = [$r('app.media.icon_1'), $r('app.media.icon_2')];
7. // 通过@Builder构建自定义菜单项
8. @Builder myMenu() {
9. Column() {
10. ForEach(this.menu, (item: string) => {
11. Row() {
12. Text(item)
13. .fontSize(18)
14. .width('100%')
15. .textAlign(TextAlign.Center)
16. }
17. .padding(15)
18. .border({ width: { bottom: 1 }, color: 0xcccccc })
19. })
20. }
21. .width(140)
22. .borderRadius(15)
23. .shadow({ radius: 15, color: 0xf1f1f1 })
24. .backgroundColor(0xf1f1f1)
25. }
27. build() {
28. Column() {
29. Row() {
30. Text('查看图片')
31. .fontSize(20)
32. .fontColor(Color.White)
33. .width('100%')
34. .textAlign(TextAlign.Center)
35. .padding({ top: 20, bottom: 20 })
36. }
37. .backgroundColor(0x007dfe)
39. Column() {
40. ForEach(this.pics, (item: Resource) => {
41. Row(){
42. Image(item)
43. .width('100%')
44. .draggable(false)
45. }
46. .padding({ top: 20, bottom: 20, left: 10, right: 10 })
47. .bindContextMenu(this.myMenu, ResponseType.LongPress)
48. })
49. }
50. }
51. .width('100%')
52. .alignItems(HorizontalAlign.Center)
53. }
54. }
使用bindPopUp实现气泡弹窗效果
bindpopup属性可为组件绑定弹窗,并设置弹窗内容,交互逻辑和显示状态。
完整示例和代码如下。
1. @Entry
2. @Component
3. struct BindPopupDemo {
5. // 第一步:定义变量控制弹窗显示
6. @State customPopup: boolean = false;
8. // 第二步:popup构造器定义弹框内容
9. @Builder popupBuilder() {
10. Column({ space: 2 }) {
11. Row().width(64)
12. .height(64)
13. .backgroundColor(0x409eff)
14. Text('Popup')
15. .fontSize(10)
16. .fontColor(Color.White)
17. }
18. .justifyContent(FlexAlign.SpaceAround)
19. .width(100)
20. .height(100)
21. .padding(5)
22. }
24. build() {
25. Column() {
27. Button('click')
28. // 第四步:创建点击事件,控制弹窗显隐
29. .onClick(() => {
30. this.customPopup = !this.customPopup;
31. })
32. .backgroundColor(0xf56c6c)
33. // 第三步:使用bindPopup接口将弹窗内容绑定给元素
34. .bindPopup(this.customPopup, {
35. builder: this.popupBuilder,
36. placement: Placement.Top,
37. maskColor: 0x33000000,
38. popupColor: 0xf56c6c,
39. enableArrow: true,
40. onStateChange: (e) => {
41. if (!e.isVisible) {
42. this.customPopup = false;
43. }
44. }
45. })
46. }
47. .justifyContent(FlexAlign.Center)
48. .width('100%')
49. .height(437)
50. }
51. }
使用if实现模态转场
上述模态转场接口需要绑定到其他组件上,通过监听状态变量改变调起模态界面。同时,也可以通过if范式,通过新增/删除组件实现模态转场效果。
完整示例和代码如下。
1. @Entry
2. @Component
3. struct ModalTransitionWithIf {
4. private listArr: string[] = ['WLAN', '蓝牙', '个人热点', '连接与共享'];
5. private shareArr: string[] = ['投屏', '打印', 'VPN', '私人DNS', 'NFC'];
6. // 第一步:定义状态变量控制页面显示
7. @State isShowShare: boolean = false;
8. private shareFunc(): void {
9. animateTo({ duration: 500 }, () => {
10. this.isShowShare = !this.isShowShare;
11. })
12. }
14. build(){
15. // 第二步:定义Stack布局显示当前页面和模态页面
16. Stack() {
17. Column() {
18. Column() {
19. Text('设置')
20. .fontSize(28)
21. .fontColor(0x333333)
22. }
23. .width('90%')
24. .padding({ top: 30, bottom: 15 })
25. .alignItems(HorizontalAlign.Start)
27. TextInput({ placeholder: '输入关键字搜索' })
28. .width('90%')
29. .height(40)
30. .margin({ bottom: 10 })
31. .focusable(false)
33. List({ space: 12, initialIndex: 0 }) {
34. ForEach(this.listArr, (item: string, index: number) => {
35. ListItem() {
36. Row() {
37. Row() {
38. Text(`${item.slice(0, 1)}`)
39. .fontColor(Color.White)
40. .fontSize(14)
41. .fontWeight(FontWeight.Bold)
42. }
43. .width(30)
44. .height(30)
45. .backgroundColor('#a8a8a8')
46. .margin({ right: 12 })
47. .borderRadius(20)
48. .justifyContent(FlexAlign.Center)
50. Column() {
51. Text(item)
52. .fontSize(16)
53. .fontWeight(FontWeight.Medium)
54. }
55. .alignItems(HorizontalAlign.Start)
57. Blank()
59. Row()
60. .width(12)
61. .height(12)
62. .margin({ right: 15 })
63. .border({
64. width: { top: 2, right: 2 },
65. color: 0xcccccc
66. })
67. .rotate({ angle: 45 })
68. }
69. .borderRadius(15)
70. .shadow({ radius: 100, color: '#ededed' })
71. .width('90%')
72. .alignItems(VerticalAlign.Center)
73. .padding({ left: 15, top: 15, bottom: 15 })
74. .backgroundColor(Color.White)
75. }
76. .width('100%')
77. .onClick(() => {
78. // 第五步:改变状态变量,显示模态页面
79. if(item.slice(-2) === '共享'){
80. this.shareFunc();
81. }
82. })
83. }, (item: string): string => item)
84. }
85. .width('100%')
86. }
87. .width('100%')
88. .height('100%')
89. .backgroundColor(0xfefefe)
91. // 第三步:在if中定义模态页面,显示在最上层,通过if控制模态页面出现消失
92. if(this.isShowShare){
93. Column() {
94. Column() {
95. Row() {
96. Row() {
97. Row()
98. .width(16)
99. .height(16)
100. .border({
101. width: { left: 2, top: 2 },
102. color: 0x333333
103. })
104. .rotate({ angle: -45 })
105. }
106. .padding({ left: 15, right: 10 })
107. .onClick(() => {
108. this.shareFunc();
109. })
110. Text('连接与共享')
111. .fontSize(28)
112. .fontColor(0x333333)
113. }
114. .padding({ top: 30 })
115. }
116. .width('90%')
117. .padding({bottom: 15})
118. .alignItems(HorizontalAlign.Start)
120. List({ space: 12, initialIndex: 0 }) {
121. ForEach(this.shareArr, (item: string) => {
122. ListItem() {
123. Row() {
124. Row() {
125. Text(`${item.slice(0, 1)}`)
126. .fontColor(Color.White)
127. .fontSize(14)
128. .fontWeight(FontWeight.Bold)
129. }
130. .width(30)
131. .height(30)
132. .backgroundColor('#a8a8a8')
133. .margin({ right: 12 })
134. .borderRadius(20)
135. .justifyContent(FlexAlign.Center)
137. Column() {
138. Text(item)
139. .fontSize(16)
140. .fontWeight(FontWeight.Medium)
141. }
142. .alignItems(HorizontalAlign.Start)
144. Blank()
146. Row()
147. .width(12)
148. .height(12)
149. .margin({ right: 15 })
150. .border({
151. width: { top: 2, right: 2 },
152. color: 0xcccccc
153. })
154. .rotate({ angle: 45 })
155. }
156. .borderRadius(15)
157. .shadow({ radius: 100, color: '#ededed' })
158. .width('90%')
159. .alignItems(VerticalAlign.Center)
160. .padding({ left: 15, top: 15, bottom: 15 })
161. .backgroundColor(Color.White)
162. }
163. .width('100%')
164. }, (item: string): string => item)
165. }
166. .width('100%')
167. }
168. .width('100%')
169. .height('100%')
170. .backgroundColor(0xffffff)
171. // 第四步:定义模态页面出现消失转场方式
172. .transition(TransitionEffect.OPACITY
173. .combine(TransitionEffect.translate({ x: '100%' }))
174. .combine(TransitionEffect.scale({ x: 0.95, y: 0.95 })))
175. }
176. }
177. }
178. }
写在最后
●如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:
●点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
●关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
●更多鸿蒙最新技术知识点,请移步前往小编:https://gitee.com/