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<void>](../reference/apis-form-kit/js-apis-application-LiveFormExtensionContext.md#startabilitybyliveform)| 拉起互动卡片提供方(应用)的页面。 | 21| [formProvider.requestOverflow(formId: string, overflowInfo: formInfo.OverflowInfo): Promise<void>](../reference/apis-form-kit/js-apis-app-form-formProvider.md#formproviderrequestoverflow20) | 卡片提供方发起互动卡片动效请求。 | 22| [formProvider.cancelOverflow(formId: string): Promise<void>](../reference/apis-form-kit/js-apis-app-form-formProvider.md#formprovidercanceloverflow20) | 卡片提供方发起取消互动卡片动效请求。 | 23| [formProvider.getFormRect(formId: string): Promise<formInfo.Rect>](../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 202 在module.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 218 在main_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 268 在form_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