1# @ohos.multimedia.movingphotoview (动态照片)
2
3用于播放动态照片文件并控制其播放状态的组件。
4
5> **说明:**
6>
7> 该组件从API Version 12开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
8> 当前不支持在预览器中使用MovingPhotoView组件。
9
10## 导入模块
11
12```
13import { MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit';
14```
15
16## MovingPhotoView
17
18> **说明:**
19>
20> - 当前不支持动态属性设置。
21> - 当前不支持ArkUI通用属性ComponentOptions中expandSafeArea属性设置。
22> - 该组件长按触发播放时组件区域放大为1.1倍。
23> - 该组件使用[AVPlayer](../apis-media-kit/_a_v_player.md#avplayer)进行播放,同时开启的[AVPlayer](../apis-media-kit/_a_v_player.md#avplayer)个数不建议超过3个,超过3个可能会出现视频播放卡顿现象。
24
25MovingPhotoView(options: MovingPhotoViewOptions)
26
27**参数:**
28
29
30| 参数名  | 参数类型                                                  | 必填 | 参数描述       |
31| ------- | --------------------------------------------------------- | ---- | -------------- |
32| options | [MovingPhotoViewOptions](#movingphotoviewoptions) | 是   | 动态照片信息。 |
33
34## MovingPhotoViewOptions
35
36
37| 参数名      | 参数类型                                                                                         | 必填 | 参数描述                                                                                                                                        |
38| ----------- | ------------------------------------------------------------------------------------------------ | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
39| movingPhoto | [MovingPhoto](js-apis-photoAccessHelper.md#movingphoto12) | 是   | 支持媒体库MovingPhoto数据源,具体信息详见[MovingPhoto说明](js-apis-photoAccessHelper.md#movingphoto12)。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 |
40| controller  | [MovingPhotoViewController](#movingphotoviewcontroller)                                          | 否   | 设置动态照片控制器,可以控制动态照片的播放状态。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。                      |
41
42## 属性
43
44除支持[通用属性](../apis-arkui/arkui-ts/ts-universal-attributes-size.md)外,还支持以下属性:
45
46### muted
47
48muted(isMuted: boolean)
49
50设置是否静音。
51
52**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
53
54**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
55
56**参数:**
57
58
59| 参数名  | 类型    | 必填 | 说明                         |
60| ------- | ------- | ---- | ---------------------------- |
61| isMuted | boolean | 是   | 是否静音。<br/>默认值:false<br/>false:非静音<br/>true:静音|
62
63### objectFit
64
65objectFit(value: ImageFit)
66
67设置动态照片显示模式。
68
69**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
70
71**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
72
73**参数:**
74
75
76| 参数名 | 类型                                                                          | 必填 | 说明                             |
77| ------ | ----------------------------------------------------------------------------- | ---- | -------------------------------- |
78| value  | [ImageFit](../apis-arkui/arkui-ts/ts-appendix-enums.md#imagefit) | 是   | 视频显示模式。<br/>默认值:Cover |
79
80### autoPlayPeriod<sup>13+</sup>
81
82autoPlayPeriod(startTime: number, endTime: number)
83
84设置自动播放区间,附属于autoPlay的子配置项。
85
86在调用此方法前,需将[autoPlay](#autoplay13)设置为true,设置自动播放,否则指定的视频区间(startTime, endTime)无法生效。
87
88**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。
89
90**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
91
92**参数:**
93
94
95| 参数名  | 类型    | 必填 | 说明                         |
96| ------- | ------- | ---- | ---------------------------- |
97| startTime| number| 是   | 区间播放开始时间,单位:ms。<br/>取值范围:[0,3000]|
98| endTime| number| 是   | 区间播放结束时间,单位:ms。<br/>取值范围:[0,3000]|
99
100### autoPlay<sup>13+</sup>
101
102autoPlay(isAutoPlay: boolean)
103
104设置自动播放,自动播放一遍视频,完成播放后显示静态图。
105
106**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。
107
108**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
109
110**参数:**
111
112
113| 参数名  | 类型    | 必填 | 说明                         |
114| ------- | ------- | ---- | ---------------------------- |
115| isAutoPlay| boolean| 是   | 是否自动播放。<br/>false:不自动播放<br/>true:自动播放<br/>默认值:false|
116
117### repeatPlay<sup>13+</sup>
118
119repeatPlay(isRepeatPlay: boolean)
120
121设置循环播放,重复播放视频。 repeatPlay与autoPlay及长按播放互斥,repeatPlay设置时,autoPlay和长按播放均不生效。
122
123**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。
124
125**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
126
127**参数:**
128
129
130| 参数名  | 类型    | 必填 | 说明                         |
131| ------- | ------- | ---- | ---------------------------- |
132| isRepeatPlay| boolean| 是   | 是否循环播放。<br/>false:不循环播放<br/>true:循环播放<br/>默认值:false|
133
134## 事件
135
136除支持[通用事件](../apis-arkui/arkui-ts/ts-universal-events-click.md)外,还支持以下事件:
137
138### onComplete<sup>13+</sup>
139
140onComplete(callback: MovingPhotoViewEventCallback)
141
142动态照片加载完成图片时触发该事件。
143
144**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。
145
146**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
147
148**参数:**
149
150
151| 参数名   | 类型                                                          | 必填 | 说明                           |
152| -------- | ------------------------------------------------------------- | ---- | ------------------------------ |
153| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | 是   | 动态照片加载完成图片的回调。 |
154
155### onStart
156
157onStart(callback: MovingPhotoViewEventCallback)
158
159播放时触发该事件。
160
161**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
162
163**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
164
165**参数:**
166
167
168| 参数名   | 类型                                                          | 必填 | 说明                           |
169| -------- | ------------------------------------------------------------- | ---- | ------------------------------ |
170| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | 是   | 动态照片开始播放时触发的回调。 |
171
172### onPause
173
174onPause(callback: MovingPhotoViewEventCallback)
175
176播放暂停时触发该事件。
177
178**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
179
180**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
181
182**参数:**
183
184
185| 参数名   | 类型                                                          | 必填 | 说明                           |
186| -------- | ------------------------------------------------------------- | ---- | ------------------------------ |
187| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | 是   | 动态照片播放暂停时触发的回调。 |
188
189### onFinish
190
191onFinish(callback: MovingPhotoViewEventCallback)
192
193播放结束时触发该事件。
194
195**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
196
197**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
198
199**参数:**
200
201
202| 参数名   | 类型                                                          | 必填 | 说明                           |
203| -------- | ------------------------------------------------------------- | ---- | ------------------------------ |
204| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | 是   | 动态照片播放结束时触发的回调。 |
205
206### onError
207
208onError(callback: MovingPhotoViewEventCallback)
209
210播放失败时触发该事件。
211
212**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
213
214**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
215
216**参数:**
217
218
219| 参数名   | 类型                                                          | 必填 | 说明                           |
220| -------- | ------------------------------------------------------------- | ---- | ------------------------------ |
221| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | 是   | 动态照片播放失败时触发的回调。 |
222
223### onStop
224
225onStop(callback: MovingPhotoViewEventCallback)
226
227播放停止时触发该事件(当stop()方法被调用后触发)。
228
229**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
230
231**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
232
233**参数:**
234
235
236| 参数名   | 类型                                                          | 必填 | 说明                           |
237| -------- | ------------------------------------------------------------- | ---- | ------------------------------ |
238| callback | [MovingPhotoViewEventCallback](#movingphotovieweventcallback) | 是   | 动态照片停止播放时触发的回调。 |
239
240## MovingPhotoViewEventCallback
241
242declare type MovingPhotoViewEventCallback = () => void
243
244动态照片播放状态发生变化时触发的回调。
245
246## MovingPhotoViewController
247
248一个MovingPhotoViewController对象可以控制一个MovingPhotoView,可用视频播放实例请参考[@ohos.multimedia.media](../apis-media-kit/js-apis-media.md)。
249
250### startPlayback
251
252startPlayback(): void
253
254开始播放。
255
256**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
257
258**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
259
260### stopPlayback
261
262stopPlayback(): void
263
264停止播放,再次播放时从头开始播放。
265
266**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
267
268**系统能力:** SystemCapability.FileManagement.PhotoAccessHelper.Core
269
270## 示例1:多种形式播放动态照片
271
272```ts
273// xxx.ets
274import { photoAccessHelper } from '@kit.MediaLibraryKit';
275import { emitter } from '@kit.BasicServicesKit';
276import { dataSharePredicates } from '@kit.ArkData';
277import { MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit';
278
279const PHOTO_SELECT_EVENT_ID: number = 80001
280
281@Entry
282@Component
283struct MovingPhotoViewDemo {
284  @State src: photoAccessHelper.MovingPhoto | undefined = undefined
285  @State isMuted: boolean = false
286  controller: MovingPhotoViewController = new MovingPhotoViewController()
287
288  aboutToAppear(): void {
289    emitter.on({
290      eventId: PHOTO_SELECT_EVENT_ID,
291      priority: emitter.EventPriority.IMMEDIATE,
292    }, (eventData: emitter.EventData) => {
293      this.src = AppStorage.get<photoAccessHelper.MovingPhoto>('mv_data') as photoAccessHelper.MovingPhoto
294    })
295  }
296
297  aboutToDisappear(): void {
298    emitter.off(PHOTO_SELECT_EVENT_ID)
299  }
300
301  build() {
302    Column() {
303      Row() {
304        Button('PICK')
305          .margin(5)
306          .onClick(async () => {
307            let context = getContext(this)
308            try {
309              let uris: Array<string> = []
310              const photoSelectOptions = new photoAccessHelper.PhotoSelectOptions()
311              photoSelectOptions.MIMEType = photoAccessHelper.PhotoViewMIMETypes.IMAGE_VIDEO_TYPE
312              photoSelectOptions.maxSelectNumber = 2
313              const photoViewPicker = new photoAccessHelper.PhotoViewPicker()
314              let photoSelectResult: photoAccessHelper.PhotoSelectResult = await photoViewPicker.select(photoSelectOptions)
315              uris = photoSelectResult.photoUris
316              if (uris[0]) {
317                this.handlePickerResult(context, uris[0], new MediaDataHandlerMovingPhoto())
318              }
319            } catch (e) {
320              console.error(`pick file failed`)
321            }
322          })
323      }
324      .alignItems(VerticalAlign.Center)
325      .justifyContent(FlexAlign.Center)
326      .height('15%')
327
328      Row() {
329        Column() {
330          MovingPhotoView({
331            movingPhoto: this.src,
332            controller: this.controller
333          })
334            .width('100%')
335            .height('100%')
336            .muted(this.isMuted)
337            .autoPlay(true)
338            .repeatPlay(false)
339            .autoPlayPeriod(0, 600)
340            .objectFit(ImageFit.Cover)
341            .onComplete(() => {
342              console.log('Completed');
343            })
344            .onStart(() => {
345              console.log('onStart')
346            })
347            .onFinish(() => {
348              console.log('onFinish')
349            })
350            .onStop(() => {
351              console.log('onStop')
352            })
353            .onError(() => {
354              console.log('onError')
355            })
356        }
357      }
358      .height('70%')
359
360      Row() {
361        Button('start')
362          .onClick(() => {
363            this.controller.startPlayback()
364          })
365          .margin(5)
366        Button('stop')
367          .onClick(() => {
368            this.controller.stopPlayback()
369          })
370          .margin(5)
371        Button('mute')
372          .onClick(() => {
373            this.isMuted = !this.isMuted
374          })
375          .margin(5)
376      }
377      .alignItems(VerticalAlign.Center)
378      .justifyContent(FlexAlign.Center)
379      .height('15%')
380    }
381  }
382
383  async handlePickerResult(context: Context, uri: string, handler: photoAccessHelper.MediaAssetDataHandler<photoAccessHelper.MovingPhoto>): Promise<void> {
384    let uriPredicates: dataSharePredicates.DataSharePredicates = new dataSharePredicates.DataSharePredicates();
385    uriPredicates.equalTo('uri', uri)
386    let fetchOptions: photoAccessHelper.FetchOptions = {
387      fetchColumns: [],
388      predicates: uriPredicates
389    };
390    let phAccessHelper = photoAccessHelper.getPhotoAccessHelper(context)
391    let assetResult = await phAccessHelper.getAssets(fetchOptions)
392    let asset = await assetResult.getFirstObject()
393    let requestOptions: photoAccessHelper.RequestOptions = {
394      deliveryMode: photoAccessHelper.DeliveryMode.FAST_MODE,
395    }
396    try {
397      photoAccessHelper.MediaAssetManager.requestMovingPhoto(context, asset, requestOptions, handler)
398    } catch (err) {
399      console.error("request error: ", err)
400    }
401  }
402}
403
404class MediaDataHandlerMovingPhoto implements photoAccessHelper.MediaAssetDataHandler<photoAccessHelper.MovingPhoto> {
405  async onDataPrepared(movingPhoto: photoAccessHelper.MovingPhoto) {
406    AppStorage.setOrCreate('mv_data', movingPhoto)
407    emitter.emit({
408      eventId: PHOTO_SELECT_EVENT_ID,
409      priority: emitter.EventPriority.IMMEDIATE,
410    }, {
411    })
412  }
413}
414```
415
416## 示例2:在原子化服务中使用动态照片
417
418```ts
419// xxx.ets
420import { photoAccessHelper, MovingPhotoView, MovingPhotoViewController, MovingPhotoViewAttribute } from '@kit.MediaLibraryKit';
421
422let context = getContext(this)
423let data: photoAccessHelper.MovingPhoto
424async function loading() {
425  try {
426    // 需要确保imageFileUri和videoFileUri对应的资源在应用沙箱存在
427    let imageFileUri = 'file://{bundleName}/data/storage/el2/base/haps/entry/files/xxx.jpg';
428    let videoFileUri = 'file://{bundleName}/data/storage/el2/base/haps/entry/files/xxx.mp4';
429    data = await photoAccessHelper.MediaAssetManager.loadMovingPhoto(context, imageFileUri, videoFileUri);
430    console.info('load moving photo successfully');
431  } catch (err) {
432    console.error(`load moving photo failed with error: ${err.code}, ${err.message}`);
433  }
434}
435@Entry
436@Component
437struct Index {
438  controller: MovingPhotoViewController = new MovingPhotoViewController()
439  @State ImageFit: ImageFit | undefined | null = ImageFit.Contain;
440  @State flag: boolean = true;
441  @State autoPlayFlag: boolean = true;
442  @State repeatPlayFlag: boolean = false;
443  @State autoPlayPeriodStart: number = 0;
444  @State autoPlayPeriodEnd: number = 500;
445  aboutToAppear(): void {
446    loading()
447  }
448
449  build() {
450    NavDestination() {
451      Column() {
452        Stack({ alignContent: Alignment.BottomStart }) {
453          MovingPhotoView({
454            movingPhoto: data,
455            controller: this.controller
456          })
457            .width(300)
458            .height(400)
459            .muted(this.flag)
460            .objectFit(this.ImageFit)
461            .autoPlay(this.autoPlayFlag)
462            .autoPlayPeriod(this.autoPlayPeriodStart, this.autoPlayPeriodEnd)
463            .repeatPlay(this.repeatPlayFlag)
464            .onComplete(() => {
465              console.info('onComplete')
466            })
467            .onStart(() => {
468              console.info('onStart')
469            })
470            .onStop(() => {
471              console.info('onStop')
472            })
473            .onPause(() => {
474              console.info('onPause')
475            })
476            .onFinish(() => {
477              console.info('onFinish')
478            })
479            .onError(() => {
480              console.info('onError')
481            })
482        }
483
484        Row() {
485          Button('Play')
486            .onClick(() => {
487              this.controller.startPlayback()
488            })
489          Button('StopPlay')
490            .onClick(() => {
491              this.controller.stopPlayback()
492            })
493          Button('mute').id('MovingPhotoView_true')
494            .onClick(() => {
495              this.flag = false
496            })
497        }
498      }
499    }
500  }
501}
502```