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