1# @ohos.multimedia.movingphotoview (MovingPhotoView) 2 3The **MovingPhotoView** component is used to play moving photos and control the playback status. 4 5> **NOTE** 6> 7> - This component is supported since API version 12. Updates will be marked with a superscript to indicate their earliest API version. 8> - Currently, the **MovingPhotoView** component cannot be used in Previewer. 9 10## Modules to Import 11 12``` 13import { MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit'; 14``` 15 16## MovingPhotoView 17 18> **NOTE** 19> 20> - Currently, live attributes cannot be set. 21> - Currently, **expandSafeArea** in the ArkUI common attribute **ComponentOptions** cannot be set. 22> - When this component is long pressed to trigger playback, the component area is zoomed in to 1.1 times. 23> - This component uses [AVPlayer](../apis-media-kit/_a_v_player.md#avplayer) to play moving photos. A maximum of three [AVPlayers](../apis-media-kit/_a_v_player.md#avplayer) can be used at the same time. Otherwise, frame freezing may occur during the playback. 24 25MovingPhotoView(options: MovingPhotoViewOptions) 26 27**Parameters** 28 29 30| Name | Type | Mandatory| Description | 31| ------- | --------------------------------------------------------- | ---- | -------------- | 32| options | [MovingPhotoViewOptions](#movingphotoviewoptions) | Yes | Moving photo information.| 33 34## MovingPhotoViewOptions 35 36 37| Name | Type | Mandatory| Description | 38| ----------- | ------------------------------------------------------------------------------------------------ | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------- | 39| movingPhoto | [MovingPhoto](js-apis-photoAccessHelper.md#movingphoto12) | Yes | **MovingPhoto** instance. For details, see [MovingPhoto](js-apis-photoAccessHelper.md#movingphoto12).<br>**Atomic service API**: This API can be used in atomic services since API version 12.| 40| controller | [MovingPhotoViewController](#movingphotoviewcontroller) | No | Controller used to control the playback status of the moving photo.<br>**Atomic service API**: This API can be used in atomic services since API version 12. | 41| imageAIOptions<sup>14+</sup> | [ImageAIOptions](../apis-arkui/arkui-ts/ts-image-common.md#imageaioptions) | No | AI options. You can set the image analyzer type or bind an image analyzer controller.<br>**Atomic service API**: This API can be used in atomic services since API version 14.| 42 43## Properties 44 45In addition to the [universal properties](../apis-arkui/arkui-ts/ts-universal-attributes-size.md), the following properties are supported. 46 47### muted 48 49muted(isMuted: boolean) 50 51Sets whether to mute the player. 52 53**Atomic service API**: This API can be used in atomic services since API version 12. 54 55**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 56 57**Parameters** 58 59 60| Name | Type | Mandatory| Description | 61| ------- | ------- | ---- | ---------------------------- | 62| isMuted | boolean | Yes | Whether to mute the player.<br>Default value: **false**<br>The value **true** means to mute the player; the value **false** means the opposite.| 63 64### objectFit 65 66objectFit(value: ImageFit) 67 68Sets the display mode of the moving photo. 69 70**Atomic service API**: This API can be used in atomic services since API version 12. 71 72**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 73 74**Parameters** 75 76 77| Name| Type | Mandatory| Description | 78| ------ | ----------------------------------------------------------------------------- | ---- | -------------------------------- | 79| value | [ImageFit](../apis-arkui/arkui-ts/ts-appendix-enums.md#imagefit) | Yes | Image scale type.<br>Default value: **Cover**| 80 81### autoPlayPeriod<sup>13+</sup> 82 83autoPlayPeriod(startTime: number, endTime: number) 84 85Sets the autoplay period, which is a configuration item of **autoPlay**. 86 87Before this API is called, [autoPlay](#autoplay13) must be set to **true**. Otherwise, the specified video play period (**startTime**, **endTime**) does not take effect. 88 89**Atomic service API**: This API can be used in atomic services since API version 13. 90 91**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 92 93**Parameters** 94 95 96| Name | Type | Mandatory| Description | 97| ------- | ------- | ---- | ---------------------------- | 98| startTime| number| Yes | Start playback time, in ms.<br>Value range: [0,3000].| 99| endTime| number| Yes | End playback time, in ms.<br>Value range: [0,3000].| 100 101### autoPlay<sup>13+</sup> 102 103autoPlay(isAutoPlay: boolean) 104 105Sets autoplay. After the moving photo is played once, a static image is displayed. 106 107**Atomic service API**: This API can be used in atomic services since API version 13. 108 109**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 110 111**Parameters** 112 113 114| Name | Type | Mandatory| Description | 115| ------- | ------- | ---- | ---------------------------- | 116| isAutoPlay| boolean| Yes | Whether to enable autoplay.<br>The value **true** means to enable autoplay; the value **false** means the opposite.<br>Default value: **false**| 117 118### repeatPlay<sup>13+</sup> 119 120repeatPlay(isRepeatPlay: boolean) 121 122Sets repeat play. **repeatPlay** is mutually exclusive with **autoPlay** and **Long Press**, and takes precedence over them. 123 124**Atomic service API**: This API can be used in atomic services since API version 13. 125 126**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 127 128**Parameters** 129 130 131| Name | Type | Mandatory| Description | 132| ------- | ------- | ---- | ---------------------------- | 133| isRepeatPlay| boolean| Yes | Whether to enable repeat play.<br>The value **true** means to enable repeat play; the value **false** means the opposite.<br>Default value: **false**| 134 135### enableAnalyzer<sup>14+</sup> 136 137enableAnalyzer(enabled: boolean) 138 139Sets the AI analyzer. Currently, the AI analyzer supports features, such as subject recognition, text recognition, and object search. 140 141**Atomic service API**: This API can be used in atomic services since API version 14. 142 143**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 144 145**Parameters** 146 147 148| Name | Type | Mandatory| Description | 149| ------- | ------- | ---- | ---------------------------- | 150| enabled| boolean| Yes | Whether to enable the AI analyzer.<br>The value **true** means to enable the AI analyzer, and **false** means the opposite.<br>Default value: **true**| 151 152## Events 153 154In addition to [universal events](../apis-arkui/arkui-ts/ts-universal-events-click.md), the following events are supported. 155 156### onComplete<sup>13+</sup> 157 158onComplete(callback: MovingPhotoViewEventCallback) 159 160Called when the image of a moving photo is loaded. 161 162**Atomic service API**: This API can be used in atomic services since API version 13. 163 164**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 165 166**Parameters** 167 168 169| Name | Type | Mandatory| Description | 170| -------- | ------------------------------------------------------------- | ---- | ------------------------------ | 171| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | Yes | Callback to be invoked when the image of a moving photo is loaded.| 172 173### onStart 174 175onStart(callback: MovingPhotoViewEventCallback) 176 177Called when a moving photo starts playing. 178 179**Atomic service API**: This API can be used in atomic services since API version 12. 180 181**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 182 183**Parameters** 184 185 186| Name | Type | Mandatory| Description | 187| -------- | ------------------------------------------------------------- | ---- | ------------------------------ | 188| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | Yes | Callback to be invoked when a moving photo starts playing.| 189 190### onPause 191 192onPause(callback: MovingPhotoViewEventCallback) 193 194Called when the playback is paused. 195 196**Atomic service API**: This API can be used in atomic services since API version 12. 197 198**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 199 200**Parameters** 201 202 203| Name | Type | Mandatory| Description | 204| -------- | ------------------------------------------------------------- | ---- | ------------------------------ | 205| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | Yes | Callback to be invoked when the playback of a moving photo is paused.| 206 207### onFinish 208 209onFinish(callback: MovingPhotoViewEventCallback) 210 211Called when the playback is finished. 212 213**Atomic service API**: This API can be used in atomic services since API version 12. 214 215**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 216 217**Parameters** 218 219 220| Name | Type | Mandatory| Description | 221| -------- | ------------------------------------------------------------- | ---- | ------------------------------ | 222| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | Yes | Callback to be invoked when the playback of a moving photo ends.| 223 224### onError 225 226onError(callback: MovingPhotoViewEventCallback) 227 228Called when the playback fails. 229 230**Atomic service API**: This API can be used in atomic services since API version 12. 231 232**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 233 234**Parameters** 235 236 237| Name | Type | Mandatory| Description | 238| -------- | ------------------------------------------------------------- | ---- | ------------------------------ | 239| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | Yes | Callback to be invoked when the playback of a moving photo fails.| 240 241### onStop 242 243onStop(callback: MovingPhotoViewEventCallback) 244 245Called when the playback is stopped by **stop()**. 246 247**Atomic service API**: This API can be used in atomic services since API version 12. 248 249**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 250 251**Parameters** 252 253 254| Name | Type | Mandatory| Description | 255| -------- | ------------------------------------------------------------- | ---- | ------------------------------ | 256| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | Yes | Callback to be invoked when the playback of a moving photo is stopped.| 257 258## MovingPhotoViewEventCallback 259 260declare type MovingPhotoViewEventCallback = () => void 261 262Defines a callback to be invoked when the playback status of a moving photo changes. 263 264## MovingPhotoViewController 265 266A **MovingPhotoViewController** object can be used to control a **MovingPhotoView** component. For details, see [@ohos.multimedia.media](../apis-media-kit/js-apis-media.md). 267 268### startPlayback 269 270startPlayback(): void 271 272Starts playback. 273 274**Atomic service API**: This API can be used in atomic services since API version 12. 275 276**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 277 278### stopPlayback 279 280stopPlayback(): void 281 282Stops playback. Once started again, the playback starts from the beginning. 283 284**Atomic service API**: This API can be used in atomic services since API version 12. 285 286**System capability**: SystemCapability.FileManagement.PhotoAccessHelper.Core 287 288## Example 1: Play a moving photo in multiple modes. 289 290```ts 291// xxx.ets 292import { photoAccessHelper } from '@kit.MediaLibraryKit'; 293import { emitter } from '@kit.BasicServicesKit'; 294import { dataSharePredicates } from '@kit.ArkData'; 295import { MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit'; 296 297const PHOTO_SELECT_EVENT_ID: number = 80001 298 299@Entry 300@Component 301struct MovingPhotoViewDemo { 302 @State src: photoAccessHelper.MovingPhoto | undefined = undefined 303 @State isMuted: boolean = false 304 controller: MovingPhotoViewController = new MovingPhotoViewController() 305 306 aboutToAppear(): void { 307 emitter.on({ 308 eventId: PHOTO_SELECT_EVENT_ID, 309 priority: emitter.EventPriority.IMMEDIATE, 310 }, (eventData: emitter.EventData) => { 311 this.src = AppStorage.get<photoAccessHelper.MovingPhoto>('mv_data') as photoAccessHelper.MovingPhoto 312 }) 313 } 314 315 aboutToDisappear(): void { 316 emitter.off(PHOTO_SELECT_EVENT_ID) 317 } 318 319 build() { 320 Column() { 321 Row() { 322 Button('PICK') 323 .margin(5) 324 .onClick(async () => { 325 let context = getContext(this) 326 try { 327 let uris: Array<string> = [] 328 const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions() 329 photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE 330 photoSelectOptions.maxSelectNumber = 2 331 const photoViewPicker = new photoAccessHelper.PhotoViewPicker() 332 let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoViewPicker.select(photoSelectOptions) 333 uris = photoSelectResult.photoUris 334 if (uris[0]) { 335 this.handlePickerResult(context, uris[0], new MediaDataHandlerMovingPhoto()) 336 } 337 } catch (e) { 338 console.error(`pick file failed`) 339 } 340 }) 341 } 342 .alignItems(VerticalAlign.Center) 343 .justifyContent(FlexAlign.Center) 344 .height('15%') 345 346 Row() { 347 Column() { 348 MovingPhotoView({ 349 movingPhoto: this.src, 350 controller: this.controller 351 }) 352 .width('100%') 353 .height('100%') 354 .muted(this.isMuted) 355 .autoPlay(true) 356 .repeatPlay(false) 357 .autoPlayPeriod(0, 600) 358 .objectFit(ImageFit.Cover) 359 .onComplete(() => { 360 console.log('Completed'); 361 }) 362 .onStart(() => { 363 console.log('onStart') 364 }) 365 .onFinish(() => { 366 console.log('onFinish') 367 }) 368 .onStop(() => { 369 console.log('onStop') 370 }) 371 .onError(() => { 372 console.log('onError') 373 }) 374 } 375 } 376 .height('70%') 377 378 Row() { 379 Button('start') 380 .onClick(() => { 381 this.controller.startPlayback() 382 }) 383 .margin(5) 384 Button('stop') 385 .onClick(() => { 386 this.controller.stopPlayback() 387 }) 388 .margin(5) 389 Button('mute') 390 .onClick(() => { 391 this.isMuted = !this.isMuted 392 }) 393 .margin(5) 394 } 395 .alignItems(VerticalAlign.Center) 396 .justifyContent(FlexAlign.Center) 397 .height('15%') 398 } 399 } 400 401 async handlePickerResult(context: Context, uri: string, handler: photoAccessHelper.MediaAssetDataHandler<photoAccessHelper.MovingPhoto>): Promise<void> { 402 let uriPredicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates(); 403 uriPredicates.equalTo('uri', uri) 404 let fetchOptions: photoAccessHelper.FetchOptions = { 405 fetchColumns: [], 406 predicates: uriPredicates 407 }; 408 let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context) 409 let assetResult = await phAccessHelper.getAssets(fetchOptions) 410 let asset = await assetResult.getFirstObject() 411 let requestOptions: photoAccessHelper.RequestOptions = { 412 deliveryMode: photoAccessHelper.DeliveryMode.FAST_MODE, 413 } 414 try { 415 photoAccessHelper.MediaAssetManager.requestMovingPhoto(context, asset, requestOptions, handler) 416 } catch (err) { 417 console.error("request error: ", err) 418 } 419 } 420} 421 422class MediaDataHandlerMovingPhoto implements photoAccessHelper.MediaAssetDataHandler<photoAccessHelper.MovingPhoto> { 423 async onDataPrepared(movingPhoto: photoAccessHelper.MovingPhoto) { 424 AppStorage.setOrCreate('mv_data', movingPhoto) 425 emitter.emit({ 426 eventId: PHOTO_SELECT_EVENT_ID, 427 priority: emitter.EventPriority.IMMEDIATE, 428 }, { 429 }) 430 } 431} 432``` 433## Example 2: Enable the AI analyzer. 434 435```ts 436// xxx.ets 437import { photoAccessHelper } from '@kit.MediaLibraryKit'; 438import { emitter } from '@kit.BasicServicesKit'; 439import { dataSharePredicates } from '@kit.ArkData'; 440import { MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit'; 441import visionImageAnalyzer from '@hms.ai.visionImageAnalyzer'; 442const PHOTO_SELECT_EVENT_ID: number = 80001 443 444@Entry 445@Component 446struct MovingPhotoViewDemo { 447 @State src: photoAccessHelper.MovingPhoto | undefined = undefined 448 @State isMuted: boolean = false 449 controller: MovingPhotoViewController = new MovingPhotoViewController() 450 private aiController: visionImageAnalyzer.VisionImageAnalyzerController = 451 new visionImageAnalyzer.VisionImageAnalyzerController() 452 private options: ImageAIOptions = { 453 types: [ImageAnalyzerType.SUBJECT, ImageAnalyzerType.TEXT, ImageAnalyzerType.OBJECT_LOOKUP], 454 aiController: this.aiController 455 } 456 457 aboutToAppear(): void { 458 emitter.on({ 459 eventId: PHOTO_SELECT_EVENT_ID, 460 priority: emitter.EventPriority.IMMEDIATE, 461 }, (eventData: emitter.EventData) => { 462 this.src = AppStorage.get<photoAccessHelper.MovingPhoto>('mv_data') as photoAccessHelper.MovingPhoto 463 }) 464 } 465 466 aboutToDisappear(): void { 467 emitter.off(PHOTO_SELECT_EVENT_ID) 468 } 469 470 build() { 471 Column() { 472 Row() { 473 Button('PICK') 474 .margin(5) 475 .onClick(async () => { 476 let context = getContext(this) 477 try { 478 let uris: Array<string> = [] 479 const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions() 480 photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE 481 photoSelectOptions.maxSelectNumber = 2 482 const photoViewPicker = new photoAccessHelper.PhotoViewPicker() 483 let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoViewPicker.select(photoSelectOptions) 484 uris = photoSelectResult.photoUris 485 if (uris[0]) { 486 this.handlePickerResult(context, uris[0], new MediaDataHandlerMovingPhoto()) 487 } 488 } catch (e) { 489 console.error(`pick file failed`) 490 } 491 }) 492 } 493 .alignItems(VerticalAlign.Center) 494 .justifyContent(FlexAlign.Center) 495 .height('15%') 496 497 Row() { 498 Column() { 499 MovingPhotoView({ 500 movingPhoto: this.src, 501 controller: this.controller, 502 imageAIOptions: this.options 503 }) 504 .width('100%') 505 .height('100%') 506 .muted(this.isMuted) 507 .autoPlay(true) 508 .repeatPlay(false) 509 .autoPlayPeriod(0, 600) 510 .objectFit(ImageFit.Cover) 511 .enableAnalyzer(true) 512 .onComplete(() => { 513 console.log('Completed'); 514 }) 515 .onStart(() => { 516 console.log('onStart') 517 }) 518 .onFinish(() => { 519 console.log('onFinish') 520 }) 521 .onStop(() => { 522 console.log('onStop') 523 }) 524 .onError(() => { 525 console.log('onError') 526 }) 527 } 528 } 529 .height('70%') 530 531 Row() { 532 Button('start') 533 .onClick(() => { 534 this.controller.startPlayback() 535 }) 536 .margin(5) 537 Button('stop') 538 .onClick(() => { 539 this.controller.stopPlayback() 540 }) 541 .margin(5) 542 Button('mute') 543 .onClick(() => { 544 this.isMuted = !this.isMuted 545 }) 546 .margin(5) 547 } 548 .alignItems(VerticalAlign.Center) 549 .justifyContent(FlexAlign.Center) 550 .height('15%') 551 } 552 } 553 554 async handlePickerResult(context: Context, uri: string, handler: photoAccessHelper.MediaAssetDataHandler<photoAccessHelper.MovingPhoto>): Promise<void> { 555 let uriPredicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates(); 556 uriPredicates.equalTo('uri', uri) 557 let fetchOptions: photoAccessHelper.FetchOptions = { 558 fetchColumns: [], 559 predicates: uriPredicates 560 }; 561 let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context) 562 let assetResult = await phAccessHelper.getAssets(fetchOptions) 563 let asset = await assetResult.getFirstObject() 564 let requestOptions: photoAccessHelper.RequestOptions = { 565 deliveryMode: photoAccessHelper.DeliveryMode.FAST_MODE, 566 } 567 try { 568 photoAccessHelper.MediaAssetManager.requestMovingPhoto(context, asset, requestOptions, handler) 569 } catch (err) { 570 console.error("request error: ", err) 571 } 572 } 573} 574 575class MediaDataHandlerMovingPhoto implements photoAccessHelper.MediaAssetDataHandler<photoAccessHelper.MovingPhoto> { 576 async onDataPrepared(movingPhoto: photoAccessHelper.MovingPhoto) { 577 AppStorage.setOrCreate('mv_data', movingPhoto) 578 emitter.emit({ 579 eventId: PHOTO_SELECT_EVENT_ID, 580 priority: emitter.EventPriority.IMMEDIATE, 581 }, { 582 }) 583 } 584} 585``` 586## Example 3: Use moving photos in an atomic service. 587 588```ts 589// xxx.ets 590import { photoAccessHelper, MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit'; 591 592let context = getContext(this) 593let data: photoAccessHelper.MovingPhoto 594async function loading() { 595 try { 596 // Ensure that the media assets corresponding to imageFileUri and videoFileUri exist in the application sandbox directory. 597 let imageFileUri = 'file://{bundleName}/data/storage/el2/base/haps/entry/files/xxx.jpg'; 598 let videoFileUri = 'file://{bundleName}/data/storage/el2/base/haps/entry/files/xxx.mp4'; 599 data = await photoAccessHelper.MediaAssetManager.loadMovingPhoto(context, imageFileUri, videoFileUri); 600 console.info('load moving photo successfully'); 601 } catch (err) { 602 console.error(`load moving photo failed with error: ${err.code}, ${err.message}`); 603 } 604} 605@Entry 606@Component 607struct Index { 608 controller: MovingPhotoViewController = new MovingPhotoViewController() 609 @State ImageFit: ImageFit | undefined | null = ImageFit.Contain; 610 @State flag: boolean = true; 611 @State autoPlayFlag: boolean = true; 612 @State repeatPlayFlag: boolean = false; 613 @State autoPlayPeriodStart: number = 0; 614 @State autoPlayPeriodEnd: number = 500; 615 aboutToAppear(): void { 616 loading() 617 } 618 619 build() { 620 NavDestination() { 621 Column() { 622 Stack({ alignContent: Alignment.BottomStart }) { 623 MovingPhotoView({ 624 movingPhoto: data, 625 controller: this.controller 626 }) 627 .width(300) 628 .height(400) 629 .muted(this.flag) 630 .objectFit(this.ImageFit) 631 .autoPlay(this.autoPlayFlag) 632 .autoPlayPeriod(this.autoPlayPeriodStart, this.autoPlayPeriodEnd) 633 .repeatPlay(this.repeatPlayFlag) 634 .onComplete(() => { 635 console.info('onComplete') 636 }) 637 .onStart(() => { 638 console.info('onStart') 639 }) 640 .onStop(() => { 641 console.info('onStop') 642 }) 643 .onPause(() => { 644 console.info('onPause') 645 }) 646 .onFinish(() => { 647 console.info('onFinish') 648 }) 649 .onError(() => { 650 console.info('onError') 651 }) 652 } 653 654 Row() { 655 Button('Play') 656 .onClick(() => { 657 this.controller.startPlayback() 658 }) 659 Button('StopPlay') 660 .onClick(() => { 661 this.controller.stopPlayback() 662 }) 663 Button('mute').id('MovingPhotoView_true') 664 .onClick(() => { 665 this.flag = false 666 }) 667 } 668 } 669 } 670 } 671} 672``` 673