1/*
2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import Matrix4 from '@ohos.matrix4';
17import { PhotoItem } from './PhotoItem';
18import { BroadCastConstants } from '../model/common/BroadCastConstants';
19import { Log } from '../utils/Log';
20import { BroadCast } from '../utils/BroadCast';
21import Curves from '@ohos.curves';
22import { Constants } from '../model/common/Constants';
23import { Constants as BrowserConstants } from '../model/browser/photo/Constants';
24import { PhotoDataSource } from '../model/browser/photo/PhotoDataSource';
25import { MediaItem } from '../model/browser/photo/MediaItem';
26
27const TAG: string = 'common_PhotoSwiper';
28
29export interface Results {
30  data: MediaItem;
31  pos: number;
32  thumbnail: string;
33};
34
35@Component
36export struct PhotoSwiper {
37  mTransition: string = '';
38  @Consume currentIndex: number;
39  @Link broadCast: BroadCast;
40  @State mDuration: number = 300;
41  onPhotoChanged: Function = (): void => {};
42  swiperController?: SwiperController;
43  isInSelectedMode: boolean = false;
44  @Consume canSwipe: boolean;
45  swiperItemSpace: number = Constants.NUMBER_8;
46  verifyPhotoScaledFunc: (matrix?: Matrix4.Matrix4Transit) => void = (matrix?: Matrix4.Matrix4Transit): void => {}
47  @Link isRunningAnimation: boolean;
48  isLeftSwiper: boolean = false;
49  @Consume isDeleting: boolean;
50  @State isOnSwiperAnimation: boolean = false;
51  private dataSource?: PhotoDataSource;
52  private geometryTransitionEnable: boolean = false;
53  private isFromFACard: boolean = false;
54  private SWIPE_CACHE_COUNT: number = 2;
55  private onDataReloadFunc: Function = (addCount: number): void => this.onDataReload(addCount);
56  private onChangeSwiperDurationFunc: Function = (value: number): void => this.onChangeSwiperDuration(value);
57
58  private onDataReload(addCount: number): void {
59    let totalCount = this.dataSource == null ? 0 : this.dataSource.totalCount();
60    let add = addCount;
61    if (add > Constants.NUMBER_0) {
62      if (this.dataSource != null) {
63        this.dataSource.onDataReloaded();
64      }
65      if (this.currentIndex + add < totalCount) {
66        this.currentIndex += add;
67      }
68      Log.info(TAG, `ON_DATA_RELOADED: ${this.currentIndex}, ${add}`);
69      return;
70    }
71    Log.debug(TAG, 'animate to data reloaded start');
72    animateTo({
73      duration: 300, // 删除动画时长
74      curve: Curves.cubicBezier(0.0, 0.0, 0.2, 1.0), // 减速曲线参数
75      onFinish: () => {
76        if (this.dataSource != null) {
77          let totalCount = this.dataSource.totalCount();
78          this.dataSource.onDataChanged(this.currentIndex);
79          // UPDATE NEXT TWO DATA FOR AVOID NOT LOADING DATA
80          if (this.currentIndex + 1 < totalCount) {
81            this.dataSource.onDataChanged(this.currentIndex + 1);
82          }
83          if (this.currentIndex + 2 < totalCount) {
84            this.dataSource.onDataChanged(this.currentIndex + 2);
85          }
86          this.dataSource.onDataReloaded();
87        }
88      } }, () => {
89      if (this.dataSource != null && this.isDeleting) {
90        this.dataSource.deleteData(this.currentIndex);
91      }
92      if (this.dataSource != null && this.currentIndex === this.dataSource.totalCount() ||
93        (this.isDeleting && this.isLeftSwiper && this.currentIndex > 0)) {
94        this.currentIndex--;
95      }
96      this.isDeleting = false;
97    })
98  }
99
100  private onChangeSwiperDuration(value: number): void {
101    Log.debug(TAG, `change duration start ${value}`);
102    this.mDuration = value;
103  }
104
105  aboutToAppear() {
106    this.broadCast.on(BroadCastConstants.ON_DATA_RELOADED, this.onDataReloadFunc);
107    this.broadCast.on(BroadCastConstants.CHANGE_SWIPER_DURATION, this.onChangeSwiperDurationFunc);
108  }
109
110  aboutToDisappear(): void {
111    this.swiperController = undefined;
112    this.broadCast.off(BroadCastConstants.ON_DATA_RELOADED, this.onDataReloadFunc);
113    this.broadCast.off(BroadCastConstants.CHANGE_SWIPER_DURATION, this.onChangeSwiperDurationFunc);
114  }
115
116  build() {
117    Swiper(this.swiperController) {
118      LazyForEach(this.dataSource as PhotoDataSource, (item: Results, index?: number) => {
119        if (!!item) {
120          Column() {
121            PhotoItem({
122              item: item.data,
123              mPosition: item.pos,
124              thumbnail: item.thumbnail,
125              transitionTag: this.mTransition ? this.mTransition : 'default_id',
126              verifyPhotoScaled: this.verifyPhotoScaledFunc,
127              albumUri: (this.dataSource == null ? '' : this.dataSource.getAlbumUri()),
128              geometryTransitionEnable: this.geometryTransitionEnable,
129              broadCast: $broadCast,
130              isRunningAnimation: $isRunningAnimation,
131              isFromFACard: this.isFromFACard,
132              isInSelectedMode: this.isInSelectedMode,
133              isOnSwiperAnimation: $isOnSwiperAnimation,
134              dataSource: this.dataSource
135            })
136          }.zIndex(item.pos == this.currentIndex ? 2 : 1)
137        }
138      }, (item: Results, index?: number) => {
139        if (item == null || item == undefined) {
140          return JSON.stringify(item) + index;
141        }
142        return `${item.data.path}_${item.data.size}_${item.data.orientation}_${index}`;
143      })
144    }
145    .cachedCount(this.SWIPE_CACHE_COUNT)
146    .duration(BrowserConstants.PHOTO_SWIPE_DURATION)
147    .itemSpace(this.swiperItemSpace)
148    .onGestureSwipe(() => {
149      if (!this.isOnSwiperAnimation) {
150        this.isOnSwiperAnimation = true;
151      }
152    })
153    .index(this.currentIndex)
154    .indicator(false)
155    .loop(false)
156    .onChange((index: number) => {
157      if (this.currentIndex - index == 1) {
158        this.isLeftSwiper = true;
159      } else if (this.currentIndex - index == -1) {
160        this.isLeftSwiper = false;
161      }
162      if (this.mDuration != 0) {
163        this.onPhotoChanged(index);
164
165        if (!!this.verifyPhotoScaledFunc) {
166          this.verifyPhotoScaledFunc(undefined)
167        }
168      }
169    })
170    .disableSwipe(this.canSwipe)
171    .onAnimationStart((index: number) => {
172      this.isOnSwiperAnimation = true;
173    })
174    .onAnimationEnd(() => {
175      this.isOnSwiperAnimation = false;
176    })
177  }
178}