• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 模态转场
2
3
4模态转场是新的界面覆盖在旧的界面上,旧的界面不消失的一种转场方式。
5
6
7**表1** 模态转场接口
8| 接口                                       | 说明                | 使用场景                                     |
9| ---------------------------------------- | ----------------- | ---------------------------------------- |
10| [bindContentCover](../reference/arkui-ts/ts-universal-attributes-modal-transition.md) | 弹出全屏的模态组件。        | 用于自定义全屏的模态展示界面,结合转场动画和共享元素动画可实现复杂转场动画效果,如缩略图片点击后查看大图。 |
11| [bindSheet](../reference/arkui-ts/ts-universal-attributes-sheet-transition.md) | 弹出半模态组件。          | 用于半模态展示界面,如分享框。                          |
12| [bindMenu](../reference/arkui-ts/ts-universal-attributes-menu.md) | 弹出菜单,点击组件后弹出。     | 需要Menu菜单的场景,如一般应用的“+”号键。                 |
13| [bindContextMenu](../reference/arkui-ts/ts-universal-attributes-menu.md) | 弹出菜单,长按或者右键点击后弹出。 | 长按浮起效果,一般结合拖拽框架使用,如桌面图标长按浮起。             |
14| [bindPopup](../reference/arkui-ts/ts-universal-attributes-popup.md) | 弹出Popup弹框。        | Popup弹框场景,如点击后对某个组件进行临时说明。               |
15| if                                       | 通过if新增或删除组件。      | 用来在某个状态下临时显示一个界面,这种方式的返回导航需要由开发者监听接口实现。  |
16
17
18## 使用bindContentCover构建全屏模态转场效果
19
20[bindContentCover](../reference/arkui-ts/ts-universal-attributes-modal-transition.md)接口用于为组件绑定全屏模态页面,在组件插入和删除时可通过设置转场参数ModalTransition显示过渡动效。
21
221. 定义全屏模态转场效果[bindContentCover](../reference/arkui-ts/ts-universal-attributes-modal-transition.md)。
23
242. 定义模态展示界面。
25
26   ```ts
27   // 通过@Builder构建模态展示界面
28   @Builder MyBuilder() {
29     Column() {
30       Text('my model view')
31     }
32     // 通过转场动画实现出现消失转场动画效果,transition需要加在builder下的第一个组件
33     .transition(TransitionEffect.translate(y:300).animation({ curve: curves.springMotion(0.6, 0.8) }))
34   }
35   ```
36
373. 通过模态接口调起模态展示界面,通过转场动画或者共享元素动画去实现对应的动画效果。
38
39   ```ts
40    class PresentTmp{
41      isPresent: boolean = false;
42      set(){
43        this.isPresent = !this.isPresent;
44      }
45    }
46   // 模态转场控制变量
47   @State isPresent: boolean = false;
48
49   Button('Click to present model view')
50     // 通过选定的模态接口,绑定模态展示界面,ModalTransition是内置的ContentCover转场动画类型,这里选择None代表系统不加默认动画
51     .bindContentCover(this.isPresent, this.MyBuilder, ModalTransition.NONE)
52     .onClick(() => {
53       // 改变状态变量,显示模态界面
54       let setPre:PresentTmp = new PresentTmp()
55       setPre.set()
56     })
57   ```
58
59
60完整示例代码和效果如下。
61
62
63
64```ts
65import curves from '@ohos.curves';
66
67@Entry
68@Component
69struct BindContentCoverDemo {
70  // 第一步:定义全屏模态转场效果bindContentCover
71  // 模态转场控制变量
72  @State isPresent: boolean = false;
73
74  // 第二步:定义模态展示界面
75  // 通过@Builder构建模态展示界面
76  @Builder MyBuilder() {
77    Column() {
78      Column() {
79        Column() {
80          Text('back')
81            .fontSize(24)
82            .fontColor(Color.White)
83        }
84        .justifyContent(FlexAlign.Center)
85        .width(100)
86        .height(100)
87        .borderRadius(5)
88        .backgroundColor(0xf56c6c)
89        .onClick(() => {
90          this.isPresent = false;
91        })
92      }
93      .height('100%')
94      .width('100%')
95      .backgroundColor(0x909399)
96      .justifyContent(FlexAlign.Center)
97      .border({
98        radius: {
99          topLeft: 15,
100          topRight: 15,
101        }
102      })
103    }
104    .height('100%')
105    .justifyContent(FlexAlign.End)
106    // 通过转场动画实现出现消失转场动画效果
107    .transition(TransitionEffect.translate({ y: 1000 }).animation({ curve: curves.springMotion(0.6, 0.8) }))
108  }
109
110  build() {
111    Column() {
112      Column() {
113        Text('Click Me')
114          .fontSize(24)
115          .fontColor(Color.White)
116      }
117      // 第三步:通过模态接口调起模态展示界面,通过转场动画或者共享元素动画去实现对应的动画效果
118      .onClick(() => {
119        // 改变状态变量,显示模态界面
120        this.isPresent = !this.isPresent;
121      })
122      // 通过选定的模态接口,绑定模态展示界面,ModalTransition是内置的ContentCover转场动画类型,这里选择DEFAULT代表设置上下切换动画效果。
123      .bindContentCover(this.isPresent, this.MyBuilder(), ModalTransition.DEFAULT)
124      .justifyContent(FlexAlign.Center)
125      .backgroundColor(0XF56C6C)
126      .width(100)
127      .height(100)
128      .borderRadius(5)
129    }
130    .justifyContent(FlexAlign.Center)
131    .width('100%')
132    .height('100%')
133  }
134}
135```
136
137
138
139![zh-cn_image_0000001646921957](figures/zh-cn_image_0000001646921957.gif)
140
141
142
143## 使用bindSheet构建半模态转场效果
144
145[bindSheet](../reference/arkui-ts/ts-universal-attributes-sheet-transition.md)属性可为组件绑定半模态页面,在组件插入时可通过设置自定义或默认的内置高度确定半模态大小。构建半模态转场动效的步骤基本与使用bindContentCover构建全屏模态转场动效相同。
146
147完整示例和效果如下。
148
149
150```ts
151@Entry
152@Component
153struct BindSheetDemo {
154
155  // 半模态转场高度控制变量
156  @State sheetHeight: number|SheetSize|null|undefined = 300;
157  // 半模态转场控制条控制变量
158  @State showDragBar: boolean = true;
159
160  // 通过@Builder构建半模态展示界面
161  @Builder myBuilder() {
162    Column() {
163      Button("change height")
164        .margin(10)
165        .fontSize(20)
166        .onClick(() => {
167          this.sheetHeight = 500;
168        })
169
170      Button("Set Illegal height")
171        .margin(10)
172        .fontSize(20)
173        .onClick(() => {
174          this.sheetHeight = null;
175        })
176
177      Button("close dragbar")
178        .margin(10)
179        .fontSize(20)
180        .onClick(() => {
181          this.showDragBar = !this.showDragBar;
182        })
183      Button("close modal 1")
184        .margin(10)
185        .fontSize(20)
186        .onClick(() => {
187          this.isPresent = false;
188        })
189    }
190    .width('100%')
191    .height('100%')
192  }
193
194  // 半模态转场控制变量
195  @State isPresent: boolean = false;
196
197  build() {
198    Column() {
199      if(this.sheetHeight){
200        Button("Click to present sheet view")
201        .onClick(() => {
202          // 改变状态变量,让模态界面显示
203          this.isPresent = !this.isPresent;
204        })
205        .fontSize(20)
206        .margin(10)
207          // 通过选定的半模态接口,绑定模态展示界面,style中包含两个参数,一个是设置半模态的高度,不设置时默认高度是Large,一个是是否显示控制条DragBar,默认是true显示控制条
208        .bindSheet(this.isPresent, this.myBuilder(), { height: this.sheetHeight, dragBar: this.showDragBar })
209      }
210    }
211    .justifyContent(FlexAlign.Center)
212    .width('100%')
213    .height('100%')
214  }
215}
216```
217
218![zh-cn_image_0000001599977924](figures/zh-cn_image_0000001599977924.gif)
219
220
221## 使用bindMenu实现菜单弹出效果
222
223[bindMenu](../reference/arkui-ts/ts-universal-attributes-menu.md)为组件绑定弹出式菜单,通过点击触发。完整示例和效果如下。
224
225
226```ts
227class BMD{
228  value:ResourceStr = ''
229  action:() => void = () => {}
230}
231@Entry
232@Component
233struct BindMenuDemo {
234
235  // 第一步: 定义一组数据用来表示菜单按钮项
236  @State items:BMD[] = [
237    {
238      value: '菜单项1',
239      action: () => {
240        console.info('handle Menu1 select')
241      }
242    },
243    {
244      value: '菜单项2',
245      action: () => {
246        console.info('handle Menu2 select')
247      }
248    },
249  ]
250
251  build() {
252    Column() {
253      Button('click')
254        .backgroundColor(0x409eff)
255        .borderRadius(5)
256          // 第二步: 通过bindMenu接口将菜单数据绑定给元素
257        .bindMenu(this.items)
258    }
259    .justifyContent(FlexAlign.Center)
260    .width('100%')
261    .height(437)
262  }
263}
264```
265
266![zh-cn_image_0000001599643478](figures/zh-cn_image_0000001599643478.gif)
267
268
269## 使用bindContextMenu实现菜单弹出效果
270
271[bindContextMenu](../reference/arkui-ts/ts-universal-attributes-menu.md)为组件绑定弹出式菜单,通过长按或右键点击触发。完整示例和效果如下。
272
273完整示例和效果如下。
274
275
276```ts
277@Entry
278@Component
279struct BindContextMenuDemo {
280  private num: number[] = [1, 2, 3, 4];
281  private colors: Color[] = [0x67C23A, 0xE6A23C, 0xf56c6c, 0x909399];
282  // 通过@Builder构建自定义菜单项
283  @Builder MyMenu() {
284    Row() {
285      Column() {
286        ForEach(this.num, (item: number, index: number = 0) => {
287          Row() {
288              Text(item.toString())
289                .fontSize(20)
290                .fontColor(Color.White)
291            }
292            .backgroundColor(this.colors[index])
293            .width('100%')
294            .aspectRatio(2)
295            .justifyContent(FlexAlign.Center)
296        })
297      }
298      .width('100%')
299    }
300    .width(150)
301    .justifyContent(FlexAlign.Center)
302    .padding(5)
303  }
304
305  build() {
306    Column() {
307      Column() {
308        Text('longPress')
309          .fontSize(20)
310          .fontColor(Color.White)
311      }
312      .justifyContent(FlexAlign.Center)
313      .width(170)
314      .height(50)
315      .bindContextMenu(this.MyMenu, ResponseType.LongPress)
316      .backgroundColor(0xf56c6c)
317      .borderRadius(5)
318    }
319    .justifyContent(FlexAlign.Center)
320    .width('100%')
321    .height(437)
322  }
323}
324```
325
326![zh-cn_image_0000001600137920](figures/zh-cn_image_0000001600137920.gif)
327
328
329## 使用bindPopUp实现气泡弹窗效果
330
331[bindpopup](../reference/arkui-ts/ts-universal-attributes-popup.md)属性可为组件绑定弹窗,并设置弹窗内容,交互逻辑和显示状态。
332
333完整示例和代码如下。
334
335
336```ts
337@Entry
338@Component
339struct BindPopupDemo {
340
341  // 第一步:定义变量控制弹窗显示
342  @State customPopup: boolean = false;
343
344  // 第二步:popup构造器定义弹框内容
345  @Builder popupBuilder() {
346    Column({ space: 2 }) {
347      Row().width(64)
348        .height(64)
349        .backgroundColor(0x409eff)
350      Text('Popup')
351        .fontSize(10)
352        .fontColor(Color.White)
353    }
354    .justifyContent(FlexAlign.SpaceAround)
355    .width(100)
356    .height(100)
357    .padding(5)
358  }
359
360  build() {
361    Column() {
362
363      Button('click')
364        // 第四步:创建点击事件,控制弹窗显隐
365        .onClick(() => {
366          this.customPopup = !this.customPopup;
367        })
368        .backgroundColor(0xf56c6c)
369          // 第三步:使用bindPopup接口将弹窗内容绑定给元素
370        .bindPopup(this.customPopup, {
371          builder: this.popupBuilder,
372          placement: Placement.Top,
373          maskColor: 0x33000000,
374          popupColor: 0xf56c6c,
375          enableArrow: true,
376          onStateChange: (e) => {
377            if (!e.isVisible) {
378              this.customPopup = false;
379            }
380          }
381        })
382    }
383    .justifyContent(FlexAlign.Center)
384    .width('100%')
385    .height(437)
386  }
387}
388```
389
390
391
392![zh-cn_image_0000001649282285](figures/zh-cn_image_0000001649282285.gif)
393
394
395## 使用if实现模态转场
396
397上述模态转场接口需要绑定到其他组件上,通过监听状态变量改变调起模态界面。同时,也可以通过if范式,通过新增/删除组件实现模态转场效果。
398
399完整示例和代码如下。
400
401
402```ts
403@Entry
404@Component
405struct ModalTransition1 {
406
407  // 第一步:定义状态变量控制页面显示
408  @State isShow: boolean = false;
409
410  build() {
411    // 第二步:定义Stack布局显示当前页面和模态页面
412    Stack() {
413      Column() {
414        Text('Page1')
415          .fontSize(40)
416          .fontColor(Color.White)
417          .fontWeight(FontWeight.Bolder)
418
419        Text('Click to transition')
420          .fontSize(15)
421          .fontColor(Color.White)
422      }
423      .justifyContent(FlexAlign.Center)
424      .width('100%')
425      .height('100%')
426      .linearGradient({
427        colors: [
428          [0xf56c6c, 0.0],
429          [0xffffff, 1.0]
430        ]
431      })
432      // 第五步:改变状态变量,显示模态页面
433      .onClick(() => {
434        animateTo({ duration: 500 }, () => {
435          this.isShow = !this.isShow;
436        })
437      })
438
439      // 第三步:在if中定义模态页面,显示在最上层,通过if控制模态页面出现消失
440      if (this.isShow) {
441        Column() {
442          Text('Page2')
443            .fontSize(40)
444            .fontColor(Color.Gray)
445            .fontWeight(FontWeight.Bolder)
446
447          Text('Click to transition')
448            .fontSize(15)
449            .fontColor(Color.Gray)
450        }
451        .justifyContent(FlexAlign.Start)
452        .width('100%')
453        .height('100%')
454        .linearGradient({
455          colors: [
456            [0xffffff, 0.0],
457            [0x409eff, 1.0]
458          ]
459        })
460        // 第四步:定义模态页面出现消失转场方式
461        .transition(TransitionEffect.OPACITY.combine(TransitionEffect.rotate({ angle: 90, y: 1 })))
462        .onClick(() => {
463          animateTo({ duration: 500 }, () => {
464            this.isShow = !this.isShow;
465          })
466        })
467      }
468
469    }
470    .width('100%')
471    .height('100%')
472  }
473}
474```
475
476![zh-cn_image_0000001597792146](figures/zh-cn_image_0000001597792146.gif)
477