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