• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 场景动效类型互动卡片开发指导
2<!--Kit: Form Kit-->
3<!--Subsystem: Ability-->
4<!--Owner: @cx983299475-->
5<!--Designer: @xueyulong-->
6<!--Tester: @chenmingze-->
7<!--Adviser: @Brilliantry_Rui-->
8本文档提供了场景动效类型互动卡片的开发指导,包括卡片非激活态和激活态UI界面开发、卡片配置文件开发。
9
10## 接口说明
11
12场景动效类型互动卡片关键接口如下表所示。
13
14**表1** 主要接口
15
16| 接口名                                                                                                                                                                                                 | 描述                 |
17|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------|
18| [onLiveFormCreate(liveFormInfo: LiveFormInfo, session: UIExtensionContentSession): void](../reference/apis-form-kit/js-apis-app-form-LiveFormExtensionAbility.md#onliveformcreate)                  | 互动卡片界面对象创建的回调函数。   |
19| [onLiveFormDestroy(liveFormInfo: LiveFormInfo): void](../reference/apis-form-kit/js-apis-app-form-LiveFormExtensionAbility.md#onliveformdestroy)                                                    | 互动卡片界面对象销毁、资源清理的回调函数。  |
20| [startAbilityByLiveForm(want: Want): Promise&lt;void&gt;](../reference/apis-form-kit/js-apis-application-LiveFormExtensionContext.md#startabilitybyliveform)| 拉起互动卡片提供方(应用)的页面。 |
21| [formProvider.requestOverflow(formId: string, overflowInfo: formInfo.OverflowInfo): Promise&lt;void&gt;](../reference/apis-form-kit/js-apis-app-form-formProvider.md#formproviderrequestoverflow20) | 卡片提供方发起互动卡片动效请求。   |
22| [formProvider.cancelOverflow(formId: string): Promise&lt;void&gt;](../reference/apis-form-kit/js-apis-app-form-formProvider.md#formprovidercanceloverflow20)                                        | 卡片提供方发起取消互动卡片动效请求。 |
23| [formProvider.getFormRect(formId: string): Promise&lt;formInfo.Rect&gt;](../reference/apis-form-kit/js-apis-app-form-formProvider.md#formprovidergetformrect20)                                        | 卡片提供方查询卡片位置、尺寸。 |
24
25## 开发流程
26
27### 卡片激活态UI开发
28
291. 创建互动卡片
30
31    通过[LiveFormExtensionAbility](../reference/apis-form-kit/js-apis-app-form-LiveFormExtensionAbility.md)创建互动卡片,创建时加载互动卡片页面。
32
33    ```ts
34    // entry/src/main/ets/myliveformextensionability/MyLiveFormExtensionAbility.ets
35    import { formInfo, LiveFormInfo, LiveFormExtensionAbility } from '@kit.FormKit';
36    import { UIExtensionContentSession } from '@kit.AbilityKit';
37
38    export default class MyLiveFormExtensionAbility extends LiveFormExtensionAbility {
39      onLiveFormCreate(liveFormInfo: LiveFormInfo, session: UIExtensionContentSession) {
40        let storage: LocalStorage = new LocalStorage();
41        storage.setOrCreate('context', this.context);
42        storage.setOrCreate('session', session);
43        let formId: string = liveFormInfo.formId;
44        storage.setOrCreate('formId', formId);
45
46        // 获取卡片圆角信息
47        let borderRadius: number = liveFormInfo.borderRadius;
48        storage.setOrCreate('borderRadius', borderRadius);
49
50        // liveFormInfo.rect字段表示非激活态卡片组件相对激活态UI的位置和尺寸信息
51        let formRect: formInfo.Rect = liveFormInfo.rect;
52        storage.setOrCreate('formRect', formRect);
53        console.log(`MyLiveFormExtensionAbility onSessionCreate formId: ${formId}` +
54          `, borderRadius: ${borderRadius}, formRectInfo: ${JSON.stringify(formRect)}`);
55
56        // 加载互动页面
57        session.loadContent('myliveformextensionability/pages/MyLiveFormPage', storage);
58      }
59
60      onLiveFormDestroy(liveFormInfo: LiveFormInfo) {
61        console.log(`MyLiveFormExtensionAbility onDestroy`);
62      }
63    };
64    ```
65
662. 实现互动卡片页面
67
68    ```ts
69    // entry/src/main/ets/myliveformextensionability/pages/MyLiveFormPage.ets
70    import { formInfo, formProvider } from '@kit.FormKit';
71    import { BusinessError } from '@kit.BasicServicesKit';
72    import LiveFormExtensionContext from 'application/LiveFormExtensionContext';
73    import { Constants } from '../../common/Constants';
74
75    const ANIMATION_RECT_SIZE: number = 100;
76    const END_SCALE: number = 1.5;
77    const END_TRANSLATE: number = -300;
78
79    @Entry
80    @Component
81    struct MyLiveFormPage {
82      @State columnScale: number = 1.0;
83      @State columnTranslate: number = 0.0;
84
85      private uiContext: UIContext | undefined = undefined;
86      private storageForMyLiveFormPage: LocalStorage | undefined = undefined;
87      private formId: string | undefined = undefined;
88      private formRect: formInfo.Rect | undefined = undefined;
89      private formBorderRadius: number | undefined = undefined;
90      private liveFormContext: LiveFormExtensionContext | undefined = undefined;
91
92      aboutToAppear(): void {
93        this.uiContext = this.getUIContext();
94        if (!this.uiContext) {
95          console.warn("no uiContext");
96          return;
97        }
98        this.initParams();
99      }
100
101      private initParams(): void {
102        this.storageForMyLiveFormPage = this.uiContext?.getSharedLocalStorage();
103        this.formId = this.storageForMyLiveFormPage?.get<string>('formId');
104        this.formRect = this.storageForMyLiveFormPage?.get<formInfo.Rect>('formRect');
105        this.formBorderRadius = this.storageForMyLiveFormPage?.get<number>('borderRadius');
106        this.liveFormContext = this.storageForMyLiveFormPage?.get<LiveFormExtensionContext>('context');
107      }
108
109      // 执行动效
110      private runAnimation(): void {
111        this.uiContext?.animateTo({
112          duration: Constants.OVERFLOW_DURATION,
113          curve: Curve.Ease
114        }, () => {
115          this.columnScale = END_SCALE;
116          this.columnTranslate = END_TRANSLATE;
117        });
118      }
119
120       private startAbilityByLiveForm(): void {
121        try {
122          // 请开发者替换为实际的want信息
123          this.liveFormContext?.startAbilityByLiveForm({
124            bundleName: 'com.example.liveformdemo',
125            abilityName: 'EntryAbility',
126          })
127            .then(() => {
128              console.info('startAbilityByLiveForm succeed');
129            })
130            .catch((err: BusinessError) => {
131              console.error(`startAbilityByLiveForm failed, code is ${err?.code}, message is ${err?.message}`);
132            });
133        } catch (e) {
134          console.error(`startAbilityByLiveForm failed, code is ${e?.code}, message is ${e?.message}`);
135        }
136      }
137
138      build() {
139        Stack({alignContent: Alignment.TopStart}) {
140          // 背景组件,和普通卡片等大
141          Column()
142            .width(this.formRect? this.formRect.width : 0)
143            .height(this.formRect? this.formRect.height : 0)
144            .offset({
145              x: this.formRect? this.formRect.left : 0,
146              y: this.formRect? this.formRect.top : 0,
147            })
148            .borderRadius(this.formBorderRadius ? this.formBorderRadius : 0)
149            .backgroundColor('#2875F5')
150          Stack() {
151            this.buildContent();
152          }
153          .width('100%')
154          .height('100%')
155        }
156        .width('100%')
157        .height('100%')
158        .onClick(() => {
159          console.info('MyLiveFormPage click to start ability');
160          if (!this.liveFormContext) {
161            console.info('MyLiveFormPage liveFormContext is empty');
162            return;
163          }
164          this.startAbilityByLiveForm();
165        })
166      }
167
168      @Builder
169      buildContent() {
170        Stack()
171          .width(ANIMATION_RECT_SIZE)
172          .height(ANIMATION_RECT_SIZE)
173          .backgroundColor(Color.White)
174          .scale({
175            x: this.columnScale,
176            y: this.columnScale,
177          })
178          .translate({
179            y: this.columnTranslate
180          })
181          .onAppear(() => {
182            // 在页面出现时执行动效
183            this.runAnimation();
184          })
185
186        Button('强制取消动效')
187          .backgroundColor(Color.Grey)
188          .onClick(() => {
189            if (!this.formId) {
190              console.info('MyLiveFormPage formId is empty, cancel overflow failed');
191              return;
192            }
193            console.info('MyLiveFormPage cancel overflow animation');
194            formProvider.cancelOverflow(this.formId);
195          })
196      }
197    }
198    ```
199
2003. 互动卡片LiveFormExtensionAbility配置
201
202module.json5配置文件中[extensionAbilities标签](../quick-start/module-configuration-file.md#extensionabilities标签)下配置LiveFormExtensionAbility。
203
204    ```ts
205    // entry/src/main/module.json5
206        ...
207        "extensionAbilities": [
208          {
209            "name": "MyLiveFormExtensionAbility",
210            "srcEntry": "./ets/myliveformextensionability/MyLiveFormExtensionAbility.ets",
211            "description": "MyLiveFormExtensionAbility",
212            "type": "liveForm"
213          }
214        ]
215        ...
216    ```
217
218main_pages.json文件中声明互动卡片页面。
219
220    ```ts
221    // entry/src/main/resources/base/profile/main_pages.json
222    {
223      "src": [
224        "pages/Index",
225        "myliveformextensionability/pages/MyLiveFormPage"
226      ]
227    }
228    ```
229
230### 卡片非激活态UI开发
231
2321. 非激活态卡片页面实现
233
234    非激活态卡片页面开发同普通卡片开发流程完全一致,在widgetCard.ets中完成。widgetCard.ets文件在卡片创建时自动生成,卡片创建流程可以参考[创建ArkTS卡片](arkts-ui-widget-creation.md)。在非激活态卡片页面实现点击卡片时,发起卡片动效请求。
235
236    ```ts
237    // entry/src/main/ets/widget/pages/WidgetCard.ets
238    @Entry
239    @Component
240    struct WidgetCard {
241      build() {
242        Row() {
243          Column() {
244            Text('点击触发互动卡片动效')
245              .fontSize($r('app.float.font_size'))
246              .fontWeight(FontWeight.Medium)
247              .fontColor($r('sys.color.font_primary'))
248          }
249          .width('100%')
250        }
251        .height('100%')
252        .onClick(() => {
253          // 点击卡片时,选择向EntryFormAbility发送消息,并在其onFormEvent回调中调用formProvider.requestOverflow,请求卡片动效
254          postCardAction(this, {
255            action: 'message',
256            abilityName: 'EntryFormAbility',
257            params: {
258              message: 'requestOverflow'
259            }
260          });
261        })
262      }
263    }
264    ```
265
2662. form_config.json配置
267
268form_config.json配置文件中新增sceneAnimationParams配置项。
269
270    ```ts
271    // entry/src/main/resources/base/profile/form_config.json
272    {
273      "forms": [
274        {
275          "name": "widget",
276          "displayName": "$string:widget_display_name",
277          "description": "$string:widget_desc",
278          "src": "./ets/widget/pages/WidgetCard.ets",
279          "uiSyntax": "arkts",
280          "window": {
281            "designWidth": 720,
282            "autoDesignWidth": true
283          },
284          "colorMode": "auto",
285          "isDefault": true,
286          "updateEnabled": true,
287          "scheduledUpdateTime": "10:30",
288          "updateDuration": 1,
289          "defaultDimension": "2*2",
290          "supportDimensions": [
291            "2*2"
292          ],
293          "formConfigAbility": "ability://EntryAbility",
294          "dataProxyEnabled": false,
295          "isDynamic": true,
296          "transparencyEnabled": false,
297          "metadata": [],
298          "sceneAnimationParams": {
299            "abilityName": "MyLiveFormExtensionAbility"
300          }
301        }
302      ]
303    }
304    ```
305
306### 互动卡片动效实现
307
3081. 触发互动卡片动效
309
310    互动卡片通过调用[formProvider.requestOverflow](../reference/apis-form-kit/js-apis-app-form-formProvider.md#formproviderrequestoverflow20)接口触发动效,调用时需要明确:(1)动效申请范围。(2)动效持续时间。(3)是否使用系统提供的默认切换动效。具体可参考[formInfo.OverflowInfo](../reference/apis-form-kit/js-apis-app-form-formInfo.md#overflowinfo20)。其中,互动卡片可以通过调用[formProvider.getFormRect](../reference/apis-form-kit/js-apis-app-form-formProvider.md#formprovidergetformrect20)接口获取卡片尺寸和在窗口内的位置信息。卡片提供方以此计算动效申请范围,单位为vp。计算规则具体请参考[互动卡片请求参数约束](arkts-ui-liveform-sceneanimation-overview.md#请求参数约束)。
311
312    ```ts
313    // entry/src/main/ets/entryformability/EntryFormAbility.ets
314    import {
315      formInfo,
316      formProvider,
317      FormExtensionAbility,
318    } from '@kit.FormKit';
319    import { BusinessError } from '@kit.BasicServicesKit';
320    import { Constants } from '../common/Constants';
321
322    export default class EntryFormAbility extends FormExtensionAbility {
323      async onFormEvent(formId: string, message: string) {
324        let shortMessage: string = JSON.parse(message)['message'];
325
326        // 当接收的 message 为 requestOverflow,触发互动卡片动效
327        if (shortMessage === 'requestOverflow') {
328          let formRect: formInfo.Rect = await formProvider.getFormRect(formId);
329          this.requestOverflow(formId, formRect.width, formRect.height);
330          return;
331        }
332      }
333
334      private requestOverflow(formId: string, formWidth: number, formHeight: number): void {
335        if (formWidth <= 0 || formHeight <= 0) {
336          console.log('requestOverflow failed, form size is not correct.');
337          return;
338        }
339
340        // 基于卡片自身尺寸信息,计算卡片动效渲染区域
341        let left: number = -Constants.OVERFLOW_LEFT_RATIO * formWidth;
342        let top: number = -Constants.OVERFLOW_TOP_RATIO * formHeight;
343        let width: number = Constants.OVERFLOW_WIDTH_RATIO * formWidth;
344        let height: number = Constants.OVERFLOW_HEIGHT_RATIO * formHeight;
345        let duration: number = Constants.OVERFLOW_DURATION;
346
347        // 发起互动卡片动效申请
348        try {
349          formProvider.requestOverflow(formId, {
350            // 动效申请范围
351            area: { left: left, top: top, width: width, height: height },
352            // 动效持续时间
353            duration: duration,
354            // 指定是否使用系统提供的默认切换动效
355            useDefaultAnimation: true,
356          }).then(() => {
357            console.log('requestOverflow requestOverflow succeed');
358          }).catch((error: BusinessError) => {
359            console.log(`requestOverflow requestOverflow catch error` + `, code: ${error.code}, message: ${error.message}`);
360          })
361        } catch (e) {
362          console.log(`requestOverflow call requestOverflow catch error` + `, code: ${e.code}, message: ${e.message}`);
363        }
364      }
365    }
366    ```
367
3682. 互动卡片动效工具函数实现
369    ```ts
370    // entry/src/main/ets/common/Constants.ets
371    // 动效相关常量的开发
372    export class Constants {
373      // 互动卡片动效超范围,左侧偏移百分比 = 偏移值/卡片宽度
374      public static readonly OVERFLOW_LEFT_RATIO: number = 0.1;
375
376      // 互动卡片动效超范围,上侧偏移百分比 = 偏移值/卡片高度
377      public static readonly OVERFLOW_TOP_RATIO: number = 0.15;
378
379      // 互动卡片动效超范围,宽度放大百分比
380      public static readonly OVERFLOW_WIDTH_RATIO: number = 1.2;
381
382      // 互动卡片动效超范围,高度放大百分比
383      public static readonly OVERFLOW_HEIGHT_RATIO: number = 1.3;
384
385      // 互动卡片动效超范围,动效时长
386      public static readonly OVERFLOW_DURATION: number = 3500;
387    }
388    ```
389
390## 实现效果
391以下是按照本文档代码示例开发而成的效果demo,demo执行动效时,点击按钮,将调用 [formProvider.cancelOverflow](../reference/apis-form-kit/js-apis-app-form-formProvider.md#formprovidercanceloverflow20) 接口,打断当前破框动效,卡片切换为非激活态。
392
393![live-form-base-demo.gif](figures/live-form-base-demo.gif)