HarmonyOS 使用画布 (Canvas)绘制自定义时间轴,放缩重绘闪烁跳动,导致放缩时间轴功能难以使用

自定义绘制时间轴,要求可以自由放缩和上下拖动,目前采用的方案是:使用画布Canvas绘制自定义时间轴,通过添加拖动手势实现时间轴的上下拖动,通过添加捏合手势实现时间轴高度的放缩,监听状态变量放缩比例实时重新绘制时间轴刻度线,重绘时间轴和刻度线的过程跳动频繁,导致时间轴放缩功能无法正常使用。

Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {  
            Canvas(this.context)  
              .backgroundColor($r('app.color.uni_color_f7f7f7'))  
              .onReady(() => {  
                this.drawTimeline();  
              })  
              .width(LayoutConstant.MATCH_PARENT)  
              .height(this.timeLineHeight * this.scaleValue)// 在组件上绑定缩放比例,可以通过修改缩放比例来实现组件的缩小或者放大  
              .translate({ x: 0, y: this.offsetY, z: 0 })// 以组件左上角为坐标原点进行移动  
              .priorityGesture(  
                GestureGroup(GestureMode.Exclusive,  
                  PanGesture(this.panOption)  
                    .onActionStart((event: GestureEvent) => {  
                      console.info('Pan start')  
                      this.onScrolling?.(true)  
                    })  
                    .onActionUpdate((event: GestureEvent) => {  
                      this.isScrollPanGestureActioning = true;  
                      if (event) {  
                        let tempOffsetY = this.positionY + event.offsetY;  
                        let oneSecondHeight: number =  
                          (this.timeLineHeight * this.scaleValue - this.blankHeight) / (24 * 3600); //每秒的高度  
                        let maxOffsetY = oneSecondHeight * 12 * 3600;  
                        let minOffsetY = -(oneSecondHeight * 12 * 3600);  
                        if (tempOffsetY > maxOffsetY) {  
                          this.offsetX = this.positionX + event.offsetX  
                          this.offsetY = maxOffsetY  
                        } else if (tempOffsetY < minOffsetY) {  
                          this.offsetX = this.positionX + event.offsetX  
                          this.offsetY = minOffsetY  
                        } else {  
                          this.offsetX = this.positionX + event.offsetX  
                          this.offsetY = tempOffsetY  
                        }  
                        this.calculateCurrentBaseLineTime();  
                      }  
                    })  
                    .onActionEnd(() => {  
                      this.isScrollPanGestureActioning = false;  
                      this.positionX = this.offsetX  
                      this.positionY = this.offsetY  
                      console.info('Pan end')  
                      if (this.onSelectTimeSecond != null) {  
                        this.onSelectTimeSecond(this.currentSecond);  
                      }  
                      // 延迟 300 ms回调  
                      setTimeout(() => {  
                        this.onScrolling?.(false)  
                      }, 300)  
                    }),  
                  // 在组件上绑定三指触发的捏合手势  
                  PinchGesture({ fingers: 2, distance: 2 })  
                    .onActionStart((event: GestureEvent | undefined) => {  
                      console.info('Pinch start');  
                      hjqlog.debug2(`Pinch start:${this.scaleValue}`)  
                      this.onScrolling?.(true)  
                    })// 当捏合手势触发时,可以通过回调函数获取缩放比例,从而修改组件的缩放比例  
                    .onActionUpdate((event: GestureEvent | undefined) => {  
                      console.info('Pinch update');  
                      if (event) {  
                        let temp = this.pinchValue * event.scale;  
                        if (temp > MAX_SCALE || temp < MIN_SCALE) {  
                          if (temp < MIN_SCALE) {  
                            this.scaleValue = MIN_SCALE;  
                          } else if (temp > MAX_SCALE) {  
                            this.scaleValue = MAX_SCALE;  
                          }  
                          this.pinchX = event.pinchCenterX;  
                          this.pinchY = event.pinchCenterY;  
                        } else {  
                          this.scaleValue = this.pinchValue * event.scale;  
                          this.pinchX = event.pinchCenterX;  
                          this.pinchY = event.pinchCenterY;  
                        }  
                        hjqlog.debug2(`Pinch update:${this.scaleValue},event.scale(${event.scale})`)  
                      }  
                    })  
                    .onActionEnd(() => {  
                      this.pinchValue = this.scaleValue;  
                      hjqlog.debug2(`Pinch end:${this.scaleValue}`)  
                      console.info('Pinch end');  
                      let currentSelectTime = this.calculateCurrentBaseLineTime();                  
                      // 延迟 300 ms回调  
                      setTimeout(() => {  
                        this.onScrolling?.(false)  
                      }, 300)  
                    })  
                )  
              )  
          }  
          .width(this.timeLineWidth)  
          .height(this.timeLineHeight * this.scaleValue)  
          .zIndex(0)  
// 绘制时间轴和时间段矩形  
  private drawTimeline(): void {  
    this.offCanvas.height = this.timeLineHeight * this.scaleValue;  
    let offContext = this.offCanvas.getContext("2d", this.settings)  
    offContext.clearRect(0, 0, this.offCanvas.width, this.offCanvas.height);  
    this.hoursPerPixel = (this.offCanvas.height - this.blankHeight) / this.totalSeconds; // 24小时对应canvas宽度  
    // 绘制时间轴  
    offContext.beginPath();  
    offContext.moveTo(this.offCanvas.width, this.offCanvas.height);  
    offContext.lineTo(this.offCanvas.width, 0);  
    offContext.lineWidth = 1;  
    offContext.strokeStyle = '#000000';  
    offContext.stroke();  
// 绘制刻度  
    for (let second = 0; second <= this.totalSeconds; second++) {  
      const y = this.offCanvas.height - second * this.hoursPerPixel - this.blankHeight / 2;  
//根据不同放缩比例绘制时间轴刻度(业务代码比较长,没有放上)  
     }  
// 将离屏绘制的内容画到canvas组件上  
    let image = this.offCanvas.transferToImageBitmap();  
    this.context.transferFromImageBitmap(image);  
}
HarmonyOS
2024-10-12 10:39:27
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
FengTianYa

Demo参考:

// xxx.ets  
@Entry  
@Component  
struct CanvasExample {  
  private settings: RenderingContextSettings = new RenderingContextSettings(true)  
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)  
  private img: ImageBitmap = new ImageBitmap("image/img_pointer_white.png")  
  private canvasCenter: number = 0  
  private lastMoveX: number = 0  
  build() {  
    Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) {  
      Canvas(this.context)  
        .width('100%')  
        .height(54)  
        .backgroundColor('#000000')  
        .onReady(() => {  
          this.canvasCenter = this.context.width / 2  
          this.context.fillStyle = Color.Red  
          this.context.fillRect(this.canvasCenter, 27, 1000, 27)  
          this.context.drawImage(this.img, this.canvasCenter - 9, 0, 18, 54)  
        })  
        .gesture(  
          GestureGroup(  
            GestureMode.Parallel,  
            PanGesture()  
              .onActionUpdate((event: GestureEvent) => {  
                let moveX = this.lastMoveX + event.offsetX  
                this.context.clearRect(0, 0, this.context.width, this.context.height)  
                let storedTransform = this.context.getTransform()  
                console.log("lastMoveX" + this.lastMoveX + "------------ offsetX" + event.offsetX +  
                  "----------canvasTransform" + storedTransform.translateX)  
                storedTransform.translateX = moveX  
                this.context.setTransform(storedTransform)  
                this.context.fillRect(this.canvasCenter, 27, 1000, 27)  
                this.context.drawImage(this.img, this.canvasCenter - 9, 0, 18, 54)  
              })  
              .onActionEnd((event: GestureEvent) => {  
                this.lastMoveX = event.offsetX  
              }),  
            PinchGesture()  
              .onActionUpdate((event: GestureEvent) => {  
                this.context.clearRect(0, 0, this.context.width, this.context.height)  
                let storedTransform = this.context.getTransform()  
                storedTransform.scaleX = event.scale;  
                this.context.setTransform(storedTransform)  
                this.context.fillRect(this.canvasCenter, 27, 1000, 27)  
                this.context.drawImage(this.img, this.canvasCenter - 9, 0, 18, 54)  
              })  
          )  
        )  
    }  
    .width('100%')  
    .height('100%')  
  }  
}
分享
微博
QQ
微信
回复
2024-10-12 15:32:08
相关问题
解决Canvas画布缩放时闪烁
1223浏览 • 1回复 待解决
HarmonyOS 自定义时间控件和日期控件
198浏览 • 1回复 待解决
HarmonyOS 如何实现Y旋转?
150浏览 • 1回复 待解决
HarmonyOS 使用自定义字体
110浏览 • 1回复 待解决
如何使用canvas绘制圆角矩形
378浏览 • 1回复 待解决
HarmonyOS 有没有类似Canvas自定义view
170浏览 • 1回复 待解决
自定义弹窗使用相关问题
819浏览 • 1回复 待解决
自定义弹窗如何嵌套使用
1401浏览 • 1回复 待解决
自定义组件onMeasureSize的使用
260浏览 • 1回复 待解决
HarmonyOS 希望优化自定义弹窗的使用
179浏览 • 1回复 待解决
画布绘制文字,垂直居中
215浏览 • 1回复 待解决
ArkTS上可以使用DataAbility功能
1792浏览 • 1回复 待解决
自定义装饰器的使用问题
663浏览 • 1回复 待解决
ArkUI中如何使用自定义字体
478浏览 • 2回复 待解决