1/*
2 * Copyright (c) 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 { Dispatch, OhCombinedState } from '../../redux/store';
17import { getStore } from '../../redux/store';
18import { Action } from '../../redux/actions/Action';
19import { EventBus } from '../../worker/eventbus/EventBus';
20import { EventBusManager } from '../../worker/eventbus/EventBusManager';
21import { CameraId } from '../../setting/settingitem/CameraId';
22
23let SHOW_FOLD_CANVAS: number = 0
24let SHOW_NOT_TAKE_VIDEO_CANVAS: number = 1
25let SHOW_TAKING_VIDEO_CANVAS: number = 2
26
27class StateStruct {
28  mode: string = 'PHOTO';
29  videoState: string = 'beforeTakeVideo';
30  cameraPosition: CameraId = CameraId.BACK;
31  zoomRatio: number = 1;
32  isShowZoomText: boolean = false;
33  showZoomLabelValue: boolean = true;
34  minZoomRatio: number = 1;
35  maxZoomRatio: number = 6;
36}
37
38
39class ZoomViewDispatcher {
40  private mDispatch: Dispatch = (data) => data;
41
42  public setDispatch(dispatch: Dispatch) {
43    this.mDispatch = dispatch;
44  }
45
46  public updateZoomRatio(zoomRatio: number): void {
47    this.mDispatch(Action.changeZoomRatio(zoomRatio));
48  }
49
50  public updateShowZoomFlag(flag: boolean): void {
51    this.mDispatch(Action.updateShowZoomTextFlag(flag));
52  }
53
54  public updateShowZoomLabelValue(flag: boolean): void {
55    this.mDispatch(Action.updateShowZoomLabelValue(flag));
56  }
57}
58
59class ZoomRatioStruct {
60  zoomRatio: number = 0;
61}
62
63class VideoStateStruct {
64  videoState: string = '';
65}
66
67@Component
68export struct ZoomViewLand {
69  private appEventBus: EventBus = EventBusManager.getInstance().getEventBus()
70  private mAction: ZoomViewDispatcher = new ZoomViewDispatcher();
71
72  @State state: StateStruct = new StateStruct()
73  @State @Watch('onZoomRatioRefresh') zoomRatio: number = 0
74  @State @Watch('onZoomRatioRefresh') showZoomLabelValue: boolean = true
75  @State @Watch('onZoomRatioRefresh') isShowZoomText: boolean = false
76  @State @Watch('onZoomRatioRefresh') curZoomRatio: number = 0
77  @State offsetY: number = 0
78  @State triggerRebuildNum: number = 0
79
80  private canvasWidth: number = 82
81  private notTakeVideoExtCanvasHeight: number = 360
82  private takingVideoExtCanvasHeight: number = 196
83  private foldCanvasHeight: number = 94
84
85  private touchedOffsetY: number = this.takingVideoExtCanvasHeight / 2
86  private startOffsetY: number = 0
87
88  private canvasSettings: RenderingContextSettings = new RenderingContextSettings(true)
89  private notTakeVideoExtCanvasCxt: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.canvasSettings)
90  private takingVideoExtCanvasCxt: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.canvasSettings)
91  private foldCanvasCxt: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.canvasSettings)
92  private notTakeVideoExtOffCanvasCxt: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(
93  this.canvasWidth, this.notTakeVideoExtCanvasHeight, this.canvasSettings)
94  private takingVideoExtOffCanvasCxt: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(
95  this.canvasWidth, this.notTakeVideoExtCanvasHeight, this.canvasSettings)
96  private foldOffCanvasCxt: OffscreenCanvasRenderingContext2D = new OffscreenCanvasRenderingContext2D(
97  this.canvasWidth, this.foldCanvasHeight, this.canvasSettings)
98
99  private lpgTimer: number = 0
100  private pgTimer: number = 0
101  private lpgExp: boolean = false
102  private pgExp: boolean = false
103  private zoomTimer: number = 0
104  private baseZoomRatio: number = 1
105
106  private mainDotRadius: number = 1.5
107  private secDotRadius: number = 0.75
108  private centerDotRadius: number = 2.5
109  private dotSpacing: number = 4
110  private refreshSwitchCanvas: number = -1
111
112  aboutToAppear(): void {
113    getStore().subscribe((state: OhCombinedState) => {
114      this.state = {
115        mode: state.modeReducer.mode,
116        videoState: state.recordReducer.videoState,
117        cameraPosition: state.cameraReducer.cameraPosition,
118        zoomRatio: state.zoomReducer.zoomRatio,
119        isShowZoomText: state.zoomReducer.isShowZoomText,
120        showZoomLabelValue: state.zoomReducer.showZoomLabelValue,
121        minZoomRatio: state.zoomReducer.minZoomRatio,
122        maxZoomRatio: state.zoomReducer.maxZoomRatio
123      };
124    }, (dispatch: Dispatch) => {
125      this.mAction.setDispatch(dispatch);
126    });
127
128    this.appEventBus.on(Action.ACTION_CHANGE_ZOOM_RATIO, (data: ZoomRatioStruct) => this.updateZoomOffset(data))
129    this.appEventBus.on(Action.ACTION_UPDATE_VIDEO_STATE, (data: VideoStateStruct) => this.updateZoomState(data))
130  }
131
132  aboutToDisappear(): void {
133    this.appEventBus.off(Action.ACTION_CHANGE_ZOOM_RATIO, (data: ZoomRatioStruct) => this.updateZoomOffset(data))
134    this.appEventBus.off(Action.ACTION_UPDATE_VIDEO_STATE, (data: VideoStateStruct) => this.updateZoomState(data))
135  }
136
137  private getCurrentCanvasType(): number {
138    if (this.isShowZoomText && (this.state.videoState === 'beforeTakeVideo'
139    && (this.state.mode === 'PHOTO' || this.state.mode === 'VIDEO'))) {
140      return SHOW_NOT_TAKE_VIDEO_CANVAS
141    } else if (this.state.mode === 'VIDEO'
142    && (this.isShowZoomText || this.state.videoState !== 'beforeTakeVideo')) {
143      return SHOW_TAKING_VIDEO_CANVAS
144    } else {
145      return SHOW_FOLD_CANVAS
146    }
147  }
148
149  private changeZoomRatioOnTakingVideoExt(): void {
150    if (this.touchedOffsetY < this.takingVideoExtCanvasHeight / 2) {
151      this.addZoomRatio()
152    } else if (this.touchedOffsetY > this.takingVideoExtCanvasHeight / 2) {
153      this.subtractZoomRatio()
154    } else {
155      this.triggerRebuildNum = this.triggerRebuildNum + 0.0001
156      this.mAction.updateShowZoomFlag(false)
157    }
158  }
159
160  private addZoomRatio(): void {
161    this.curZoomRatio = this.zoomRatio + 0.1
162    if (this.curZoomRatio > this.state.maxZoomRatio) {
163      this.curZoomRatio = this.state.maxZoomRatio
164    }
165    this.mAction.updateZoomRatio(this.curZoomRatio)
166    this.mAction.updateShowZoomFlag(true)
167    this.triggerRebuildNum = this.triggerRebuildNum + 0.0001
168  }
169
170  private subtractZoomRatio(): void {
171    this.curZoomRatio = this.zoomRatio - 0.1
172    if (this.curZoomRatio < this.state.minZoomRatio) {
173      this.curZoomRatio = this.state.minZoomRatio
174    }
175    this.mAction.updateZoomRatio(this.curZoomRatio)
176    this.mAction.updateShowZoomFlag(true)
177    this.triggerRebuildNum = this.triggerRebuildNum - 0.0001
178  }
179
180  private takingVideoExtTouched(event?: TouchEvent): void {
181    if (!event) {
182      return;
183    }
184    if (event.type === TouchType.Down) {
185      this.touchedOffsetY = event.touches[0].y
186      this.startOffsetY = event.touches[0].y
187      this.changeZoomRatioOnTakingVideoExt()
188    }
189    if (event.type === TouchType.Up) {
190      this.touchedOffsetY = this.takingVideoExtCanvasHeight / 2
191      this.changeZoomRatioOnTakingVideoExt()
192    }
193  }
194
195  private takingVideoExtLongPgAction(event?: GestureEvent): void {
196    if (!event) {
197      return;
198    }
199    this.touchedOffsetY = event.fingerList[0].localY
200    this.changeZoomRatioOnTakingVideoExt()
201  }
202
203
204  private takingVideoExtLongPgActionEnd(): void {
205    this.touchedOffsetY = this.takingVideoExtCanvasHeight / 2
206    this.changeZoomRatioOnTakingVideoExt()
207  }
208
209  private takingVideoExtPgActionStart(event?: GestureEvent): void {
210    if (!event) {
211      return;
212    }
213    this.touchedOffsetY = this.startOffsetY + event.offsetY
214    this.changeZoomRatioOnTakingVideoExt()
215  }
216
217  private takingVideoExtPgActionUpdate(event?: GestureEvent): void {
218    if (!event) {
219      return;
220    }
221    this.touchedOffsetY = this.startOffsetY + event.offsetY
222    let takingVideoExtMaxOffsetY = this.takingVideoExtCanvasHeight - this.getZoomBtnRadius() - this.secDotRadius
223    let takingVideoExtMinOffsetY = this.getZoomBtnRadius() + this.secDotRadius
224    if (this.touchedOffsetY > takingVideoExtMaxOffsetY) {
225      this.touchedOffsetY = takingVideoExtMaxOffsetY
226    } else if (this.touchedOffsetY < takingVideoExtMinOffsetY) {
227      this.touchedOffsetY = takingVideoExtMinOffsetY
228    }
229    this.changeZoomRatioOnTakingVideoExt()
230  }
231
232  private takingVideoExtPgActionEnd(event?: GestureEvent): void {
233    if (!event) {
234      return;
235    }
236    this.touchedOffsetY = this.takingVideoExtCanvasHeight / 2
237    this.startOffsetY = 0
238    this.changeZoomRatioOnTakingVideoExt()
239  }
240
241  private subtractTouched(event?: TouchEvent): void {
242    if (!event) {
243      return;
244    }
245    if (event.type === TouchType.Down) {
246      this.subtractZoomRatio()
247    }
248    if (event.type === TouchType.Up) {
249      this.mAction.updateShowZoomFlag(false)
250    }
251  }
252
253  private subtractLongOnAction(event?: GestureEvent): void {
254    if (!event) {
255      return;
256    }
257    this.subtractZoomRatio()
258  }
259
260  private subtractLongOnActionEnd(): void {
261    this.mAction.updateShowZoomFlag(false)
262  }
263
264  private addTouched(event?: TouchEvent): void {
265    if (!event) {
266      return;
267    }
268    if (event.type === TouchType.Down) {
269      this.addZoomRatio()
270    }
271    if (event.type === TouchType.Up) {
272      this.mAction.updateShowZoomFlag(false)
273    }
274  }
275
276  private addLongOnAction(): void {
277    this.addZoomRatio()
278  }
279
280  private addLongOnActionEnd(): void {
281    this.mAction.updateShowZoomFlag(false)
282  }
283
284  private lpgOnAction(): void {
285    this.clearTimer()
286    this.mAction.updateShowZoomFlag(true)
287    this.baseZoomRatio = this.zoomRatio
288    this.offsetY = (this.zoomRatio - 1) * this.getZoomOffsetUnit()
289    this.lpgExp = true
290    this.pgExp = false
291    this.triggerRebuildNum = this.triggerRebuildNum + 0.0001
292  }
293
294  private lpgOnActionEnd(): void {
295    if (this.lpgTimer) {
296      clearTimeout(this.lpgTimer)
297    }
298    this.lpgTimer = setTimeout(() => {
299      if (this.lpgExp && !this.pgExp) {
300        this.mAction.updateShowZoomFlag(false)
301        this.triggerRebuildNum = this.triggerRebuildNum - 0.0001
302      }
303      this.lpgExp = false
304    }, 3000)
305  }
306
307  private pgOnActionStart(): void {
308    this.clearTimer()
309    this.mAction.updateShowZoomFlag(true)
310    this.mAction.updateShowZoomLabelValue(false)
311    this.baseZoomRatio = this.state.zoomRatio
312    this.pgExp = true
313    this.lpgExp = false
314  }
315
316  private pgOnActionUpdate(event?: GestureEvent): void {
317    if (!event) {
318      return;
319    }
320    this.offsetY = (this.baseZoomRatio - this.state.minZoomRatio) * this.getZoomOffsetUnit() + event.offsetY
321    this.updateZoomRatio()
322  }
323
324  private pgOnActionEnd(): void {
325    this.mAction.updateShowZoomLabelValue(true)
326    if (this.pgTimer) {
327      clearTimeout(this.pgTimer)
328    }
329    this.pgTimer = setTimeout(() => {
330      if (this.pgExp && !this.lpgExp) {
331        this.mAction.updateShowZoomFlag(false)
332      }
333      this.pgExp = false
334    }, 3000)
335  }
336
337  private mOnTouch(event: TouchEvent): void {
338    if (event.type === TouchType.Down) {
339      this.clearTimer()
340      this.mAction.updateShowZoomFlag(true)
341      this.pgExp = true
342      this.lpgExp = false
343
344      let y = event.touches[0].y
345      let zoomRatio = this.zoomRatio
346      if (this.state.videoState === 'beforeTakeVideo' && this.getCurrentCanvasType() === SHOW_NOT_TAKE_VIDEO_CANVAS) {
347        if (y < vp2px(36)) {
348          zoomRatio = this.state.maxZoomRatio
349        }
350        if (y > this.notTakeVideoExtCanvasHeight - vp2px(36)) {
351          zoomRatio = this.state.minZoomRatio
352        }
353        if (y > vp2px(36) && y < this.notTakeVideoExtCanvasHeight - vp2px(36)) {
354          this.offsetY = this.notTakeVideoExtCanvasHeight - y - this.getPadding()
355          this.updateZoomRatio()
356          return;
357        }
358      }
359      this.offsetY = (zoomRatio - 1) * this.getZoomOffsetUnit()
360      this.updateZoomRatio()
361    } else if (event.type === TouchType.Up) {
362      if (this.pgTimer) {
363        clearTimeout(this.pgTimer)
364      }
365      this.pgTimer = setTimeout(() => {
366        if (this.pgExp && !this.lpgExp) {
367          this.mAction.updateShowZoomFlag(false)
368        }
369        this.pgExp = false
370      }, 3000)
371    }
372  }
373
374  private getZoomBtnCenterY(): number {
375    if (this.getCurrentCanvasType() === SHOW_TAKING_VIDEO_CANVAS) {
376      return this.touchedOffsetY
377    }
378    if (this.offsetY === 0 && this.zoomRatio !== 1) {
379      this.offsetY = (this.zoomRatio - this.state.minZoomRatio) * this.getZoomOffsetUnit()
380    }
381    if (this.zoomRatio === 1 && this.offsetY !== 0) {
382      this.offsetY = 0
383    }
384    let padding = this.getPadding()
385    let result = this.notTakeVideoExtCanvasHeight - padding - this.offsetY
386    return result
387  }
388
389  private getZoomOffsetUnit(): number {
390    let padding = this.getPadding()
391    let fullHeight = this.notTakeVideoExtCanvasHeight - padding * 2 - this.mainDotRadius * 2
392    return fullHeight / (this.state.maxZoomRatio - this.state.minZoomRatio)
393  }
394
395
396  private updateZoomOffset(data: ZoomRatioStruct): void {
397    let offset = (data.zoomRatio - this.state.minZoomRatio) * this.getZoomOffsetUnit();
398    this.offsetY = offset;
399  }
400
401  private updateZoomState(data: VideoStateStruct): void {
402    if (data.videoState === 'beforeTakeVideo') {
403      this.clearTimer();
404      this.mAction.updateShowZoomFlag(false);
405      this.pgExp = false;
406    }
407  }
408
409  private clearTimer(): void {
410    if (this.pgTimer) {
411      clearTimeout(this.pgTimer)
412    }
413    if (this.lpgTimer) {
414      clearTimeout(this.lpgTimer)
415    }
416  }
417
418  private updateZoomRatio(): void {
419    let padding = this.getPadding()
420    let fullHeight = this.notTakeVideoExtCanvasHeight - padding * 2 - this.mainDotRadius * 2
421    this.curZoomRatio = (this.offsetY / fullHeight) * (this.state.maxZoomRatio - this.state.minZoomRatio) + this.state.minZoomRatio
422    if (this.curZoomRatio > this.state.maxZoomRatio) {
423      this.curZoomRatio = this.state.maxZoomRatio
424    }
425    if (this.curZoomRatio < this.state.minZoomRatio) {
426      this.curZoomRatio = this.state.minZoomRatio
427    }
428    this.mAction.updateZoomRatio(this.curZoomRatio)
429  }
430
431  private getPadding(): number {
432    if (this.getCurrentCanvasType() === SHOW_NOT_TAKE_VIDEO_CANVAS) {
433      return 32
434    } else if (this.getCurrentCanvasType() === SHOW_TAKING_VIDEO_CANVAS) {
435      return 15.5
436    } else {
437      return 32
438    }
439  }
440
441  private getZoomText() {
442    return `${Number(this.zoomRatio.toFixed(1))}x`
443  }
444
445  private getZoomBtnRadius(): number {
446    if (!this.showZoomLabelValue) {
447      return 17.25
448    } else {
449      return 15.25
450    }
451  }
452
453  private onZoomRatioRefresh() {
454    if (this.getCurrentCanvasType() === this.refreshSwitchCanvas) {
455      this.refreshCanvas(this.refreshSwitchCanvas)
456    }
457  }
458
459  private canvasInit(mType: number): void {
460    this.refreshSwitchCanvas = mType
461    this.refreshCanvas(mType)
462  }
463
464  private refreshCanvas(mType: number): void {
465    switch (mType) {
466      case SHOW_NOT_TAKE_VIDEO_CANVAS:
467        this.notTakeVideoCanvas();
468        break;
469      case SHOW_TAKING_VIDEO_CANVAS:
470        this.takingVideoCanvas();
471        break;
472      default:
473        this.foldCanvas()
474    }
475  }
476
477  private notTakeVideoCanvas(): void {
478    this.notTakeVideoExtCanvasCxt.clearRect(0, 0, this.canvasWidth, this.notTakeVideoExtCanvasHeight)
479    this.notTakeVideoExtOffCanvasCxt.clearRect(0, 0, this.canvasWidth, this.notTakeVideoExtCanvasHeight)
480    this.notTakeVideoExtOffCanvasCxt.strokeStyle = '#ffffff'
481    this.notTakeVideoExtOffCanvasCxt.fillStyle = '#ffffff'
482    this.notTakeVideoExtOffCanvasCxt.lineWidth = 1.5
483    this.notTakeVideoExtOffCanvasCxt.beginPath()
484    this.notTakeVideoExtOffCanvasCxt.arc(this.canvasWidth / 2, this.getZoomBtnCenterY(), this.getZoomBtnRadius(), 0, 6.28)
485    this.notTakeVideoExtOffCanvasCxt.stroke()
486    if (this.showZoomLabelValue) {
487      this.notTakeVideoExtOffCanvasCxt.font = `bold ${vp2px(11)}px`
488      this.notTakeVideoExtOffCanvasCxt.textAlign = 'center'
489      this.notTakeVideoExtOffCanvasCxt.fillText(this.getZoomText(), this.canvasWidth / 2, this.getZoomBtnCenterY() + 5)
490    } else {
491      this.notTakeVideoExtOffCanvasCxt.beginPath()
492      this.notTakeVideoExtOffCanvasCxt.arc(this.canvasWidth / 2, this.getZoomBtnCenterY(), this.centerDotRadius, 0, 6.28)
493      this.notTakeVideoExtOffCanvasCxt.fill()
494    }
495
496    let spotCount = (this.notTakeVideoExtCanvasHeight - this.getPadding() * 2 - this.mainDotRadius * 4 - this.dotSpacing) / (this.dotSpacing + this.secDotRadius * 2) + 2
497    for (let i = 0; i < spotCount; i++) {
498      let spotCenter = 0
499      let spotRadius = 0
500      if (i === 0) {
501        spotRadius = this.mainDotRadius
502        spotCenter = this.notTakeVideoExtCanvasHeight - this.getPadding() - spotRadius
503        this.notTakeVideoExtOffCanvasCxt.font = `bold ${vp2px(11)}px`
504        this.notTakeVideoExtOffCanvasCxt.textAlign = 'right'
505        this.notTakeVideoExtOffCanvasCxt.fillText(`${this.state.minZoomRatio}x`,this.canvasWidth / 2 - (!this.showZoomLabelValue ? 26: 24), spotCenter)
506      } else if (i === spotCount - 1) {
507        spotRadius = this.mainDotRadius
508        spotCenter = this.getPadding() + spotRadius
509        this.notTakeVideoExtOffCanvasCxt.font = `bold ${vp2px(11)}px`
510        this.notTakeVideoExtOffCanvasCxt.textAlign = 'right'
511        this.notTakeVideoExtOffCanvasCxt.fillText(`${this.state.maxZoomRatio}x`,this.canvasWidth / 2 - (!this.showZoomLabelValue ? 26: 24), spotCenter)
512      } else {
513        spotRadius = this.secDotRadius
514        spotCenter = this.notTakeVideoExtCanvasHeight - this.getPadding() - this.mainDotRadius * 2 - (2 * i - 1) * this.secDotRadius - i * this.dotSpacing
515        this.notTakeVideoExtOffCanvasCxt.globalAlpha = 0.2
516      }
517      if (spotCenter < this.getZoomBtnCenterY() - this.getZoomBtnRadius() || spotCenter > this.getZoomBtnCenterY() + this.getZoomBtnRadius()) {
518        this.notTakeVideoExtOffCanvasCxt.beginPath()
519        this.notTakeVideoExtOffCanvasCxt.arc(this.canvasWidth / 2, spotCenter, spotRadius, 0, 6.28)
520        this.notTakeVideoExtOffCanvasCxt.fill()
521      }
522      this.notTakeVideoExtOffCanvasCxt.globalAlpha = 1
523    }
524    this.notTakeVideoExtCanvasCxt.transferFromImageBitmap(this.notTakeVideoExtOffCanvasCxt.transferToImageBitmap())
525  }
526
527  private takingVideoCanvas(): void {
528    this.takingVideoExtCanvasCxt.clearRect(0, 0, this.canvasWidth, this.takingVideoExtCanvasHeight)
529    this.takingVideoExtOffCanvasCxt.clearRect(0, 0, this.canvasWidth, this.takingVideoExtCanvasHeight)
530    this.takingVideoExtOffCanvasCxt.strokeStyle = '#ffffff'
531    this.takingVideoExtOffCanvasCxt.fillStyle = '#ffffff'
532    this.takingVideoExtOffCanvasCxt.lineWidth = 1.5
533    this.takingVideoExtOffCanvasCxt.beginPath()
534    this.takingVideoExtOffCanvasCxt.arc(this.canvasWidth / 2, this.getZoomBtnCenterY(), this.getZoomBtnRadius(), 0, 6.28)
535    this.takingVideoExtOffCanvasCxt.stroke()
536    if (this.isShowZoomText) {
537      this.takingVideoExtOffCanvasCxt.beginPath()
538      this.takingVideoExtOffCanvasCxt.arc(this.canvasWidth / 2, this.getZoomBtnCenterY(), this.centerDotRadius, 0, 6.28)
539      this.takingVideoExtOffCanvasCxt.fill()
540    } else {
541      this.takingVideoExtOffCanvasCxt.font = `bold ${vp2px(11)}px`
542      this.takingVideoExtOffCanvasCxt.textAlign = 'center'
543      this.takingVideoExtOffCanvasCxt.fillText(this.getZoomText(), this.canvasWidth / 2, this.getZoomBtnCenterY() + 5)
544    }
545
546    let spotCount = 30
547    for (let i = 0; i < spotCount; i++) {
548      let spotCenter = 0
549      let spotRadius = 0
550      spotRadius = this.secDotRadius
551      spotCenter = this.getPadding() + (2 * i + 1) * this.secDotRadius + i * this.dotSpacing
552      this.takingVideoExtOffCanvasCxt.globalAlpha = 0.2
553      if (spotCenter < this.getZoomBtnCenterY() - this.getZoomBtnRadius() || spotCenter > this.getZoomBtnCenterY() + this.getZoomBtnRadius()) {
554        this.takingVideoExtOffCanvasCxt.beginPath()
555        this.takingVideoExtOffCanvasCxt.arc(this.canvasWidth / 2, spotCenter, spotRadius, 0, 6.28)
556        this.takingVideoExtOffCanvasCxt.fill()
557      }
558      this.takingVideoExtOffCanvasCxt.globalAlpha = 1
559    }
560
561    this.takingVideoExtCanvasCxt.transferFromImageBitmap(this.takingVideoExtOffCanvasCxt.transferToImageBitmap())
562  }
563
564  private foldCanvas(): void {
565    this.foldCanvasCxt.clearRect(0, 0, this.canvasWidth, this.foldCanvasHeight)
566    this.foldOffCanvasCxt.clearRect(0, 0, this.canvasWidth, this.foldCanvasHeight)
567    this.foldOffCanvasCxt.strokeStyle = '#ffffff'
568    this.foldOffCanvasCxt.fillStyle = '#ffffff'
569    this.foldOffCanvasCxt.lineWidth = 1.5
570    this.foldOffCanvasCxt.beginPath()
571    this.foldOffCanvasCxt.arc(this.canvasWidth / 2, this.foldCanvasHeight / 2, this.getZoomBtnRadius(), 0, 6.28)
572    this.foldOffCanvasCxt.stroke()
573
574    this.foldOffCanvasCxt.font = `bold ${vp2px(10)}px`
575    this.foldOffCanvasCxt.textAlign = 'center'
576    this.foldOffCanvasCxt.fillText(this.getZoomText(), this.canvasWidth / 2, this.foldCanvasHeight / 2 + 3)
577
578    let fullHeight = this.foldCanvasHeight / 2 - this.mainDotRadius
579    let spotCount = (fullHeight - this.mainDotRadius * 2 - this.dotSpacing) / (this.dotSpacing + this.secDotRadius * 2) + 2
580    let spotOffset = (this.zoomRatio === this.state.maxZoomRatio) ? this.foldCanvasHeight / 2 + fullHeight
581                                                                  : this.foldCanvasHeight / 2
582    for (let i = 0; i < spotCount; i++) {
583      let spotCenter = 0
584      let spotRadius = 0
585      if (i === 0) {
586        spotRadius = this.mainDotRadius
587        spotCenter = spotOffset - spotRadius
588      } else if (i === spotCount - 1) {
589        spotRadius = this.mainDotRadius
590        spotCenter = spotOffset - this.mainDotRadius * 2 - (i - 1) * this.dotSpacing - (2 * i - 1) * this.secDotRadius + this.secDotRadius - spotRadius
591      } else {
592        spotRadius = this.secDotRadius
593        spotCenter = spotOffset - this.mainDotRadius * 2 - (i - 1) * this.dotSpacing - (2 * i - 1) * this.secDotRadius - spotRadius
594        this.foldOffCanvasCxt.globalAlpha = 0.2
595      }
596      if (spotCenter > this.foldCanvasHeight / 2 + this.getZoomBtnRadius() || spotCenter < this.foldCanvasHeight / 2 - this.getZoomBtnRadius()) {
597        this.foldOffCanvasCxt.beginPath()
598        this.foldOffCanvasCxt.arc(this.canvasWidth / 2, spotCenter, spotRadius, 0, 6.28)
599        this.foldOffCanvasCxt.fill()
600      }
601      this.foldOffCanvasCxt.globalAlpha = 1
602    }
603    this.foldCanvasCxt.transferFromImageBitmap(this.foldOffCanvasCxt.transferToImageBitmap())
604  }
605
606  build() {
607    Stack({ alignContent: Alignment.Start}) {
608      Stack({ alignContent: Alignment.Top }).width(this.triggerRebuildNum).height(this.offsetY + this.touchedOffsetY + this.zoomRatio).visibility(Visibility.None)
609      if (this.getCurrentCanvasType() === SHOW_NOT_TAKE_VIDEO_CANVAS) {
610        Canvas(this.notTakeVideoExtCanvasCxt)
611          .width(this.canvasWidth)
612          .height(this.notTakeVideoExtCanvasHeight)
613          .onReady(() => this.canvasInit(SHOW_NOT_TAKE_VIDEO_CANVAS))
614          .gesture(
615          GestureGroup(
616          GestureMode.Parallel,
617          PanGesture({ fingers: 1, distance: 1, direction: PanDirection.Vertical})
618            .onActionStart(() => this.pgOnActionStart())
619            .onActionUpdate((event?: GestureEvent) => {
620              if (event) {
621                return this.pgOnActionUpdate(event);
622              }
623            })
624            .onActionEnd(() => this.pgOnActionEnd())))
625          .onTouch((event?: TouchEvent) => {
626            if (event) {
627              return this.mOnTouch(event);
628            }
629          })
630      } else if (this.getCurrentCanvasType() === SHOW_TAKING_VIDEO_CANVAS) {
631        Column() {
632          Image($r('app.media.ic_camera_public_focus_ev_bright_add'))
633            .width(24)
634            .height(24)
635            .fillColor(Color.White)
636            .onTouch((event?: TouchEvent) => this.addTouched(event))
637            .gesture(
638            GestureGroup(
639            GestureMode.Parallel,
640            LongPressGesture({ repeat: true })
641              .onAction(() => this.addLongOnAction())
642              .onActionEnd(() => this.addLongOnActionEnd()),
643            )
644            )
645          Canvas(this.takingVideoExtCanvasCxt)
646            .width(this.canvasWidth)
647            .height(this.takingVideoExtCanvasHeight)
648            .onReady(() => this.canvasInit(SHOW_TAKING_VIDEO_CANVAS))
649            .gesture(
650            GestureGroup(
651            GestureMode.Parallel,
652            LongPressGesture({ repeat: true })
653              .onAction((event?: GestureEvent) => this.takingVideoExtLongPgAction(event))
654              .onActionEnd(() => this.takingVideoExtLongPgActionEnd()),
655            PanGesture({ fingers: 1, distance: 1, direction: PanDirection.Horizontal })
656              .onActionStart((event?: GestureEvent) => this.takingVideoExtPgActionStart(event))
657              .onActionUpdate((event?: GestureEvent) => this.takingVideoExtPgActionUpdate(event))
658              .onActionEnd((event?: GestureEvent) => this.takingVideoExtPgActionEnd(event))
659            ))
660            .onTouch((event?: TouchEvent) => this.takingVideoExtTouched(event))
661          Image($r('app.media.ic_camera_public_focus_ev_bright_subtract'))
662            .width(24)
663            .height(24)
664            .fillColor(Color.White)
665            .onTouch((event?: TouchEvent) => this.subtractTouched(event))
666            .gesture(
667            GestureGroup(
668            GestureMode.Parallel,
669            LongPressGesture({ repeat: true })
670              .onAction((event?: GestureEvent) => this.subtractLongOnAction(event))
671              .onActionEnd(() => this.subtractLongOnActionEnd()),
672            )
673            )
674        }.width('100%').height(this.notTakeVideoExtCanvasHeight).padding({ top: 58, bottom: 58 })
675      } else {
676        Canvas(this.foldCanvasCxt)
677          .width(this.canvasWidth)
678          .height(this.foldCanvasHeight)
679          .onReady(() => this.canvasInit(SHOW_FOLD_CANVAS))
680          .gesture(
681          GestureGroup(
682          GestureMode.Parallel,
683          LongPressGesture({ repeat: true })
684            .onAction(() => this.lpgOnAction())
685            .onActionEnd(() => this.lpgOnActionEnd()),
686          PanGesture({ fingers: 1, distance: 1, direction: PanDirection.Horizontal })
687            .onActionStart(() => this.pgOnActionStart())
688            .onActionUpdate((event?: GestureEvent) => this.pgOnActionUpdate(event))
689            .onActionEnd(() => this.pgOnActionEnd())
690          )
691          )
692      }
693    }.width(82).height('100%')
694  }
695}