HarmonyOS 鸿蒙Next中目前使用了canvas画布实现了手写笔相关功能软件,目前遇到的问题是在画布里面如果笔画的数量很大,会比较容易出现卡顿或者说不跟手之类的问题,应该如何解决?

HarmonyOS 鸿蒙Next中目前使用了canvas画布实现了手写笔相关功能软件,目前遇到的问题是在画布里面如果笔画的数量很大,会比较容易出现卡顿或者说不跟手之类的问题,应该如何解决? 【问题描述】:目前使用了canvas 画布实现了手写笔相关功能软件,目前遇到的问题是在画布里面如果笔画的数量很大,会比较容易出现卡顿或者说不跟手之类的问题,应该如何解决?

【问题现象】:cke_1650.png

在画布已经存在很多笔画的情况下,缩放,写字都比较卡顿,这种情况有没有什么优化方案

【版本信息】:IDE版本6.1.0,测试机API版本6.1.0(API23)

【复现代码】:参考官网demo,基于Canvas实现画布的功能:custom-canvas:基于HarmonyOS的Canvas画布功能项目 - AtomGit | GitCode


更多关于HarmonyOS 鸿蒙Next中目前使用了canvas画布实现了手写笔相关功能软件,目前遇到的问题是在画布里面如果笔画的数量很大,会比较容易出现卡顿或者说不跟手之类的问题,应该如何解决?的实战教程也可以访问 https://www.itying.com/category-93-b0.html

6 回复

当前问题可以参考双层canvas方案进行优化

底层canvas画布:只绘制已落笔的历史笔迹;在撤销/重做/清空、捏合、折叠等时整层重绘

顶层画布上下文:仅绘制当前手指未抬起时的「预览」笔迹,避免每次 Move 重画全部历史

用户所见 = 底层(白底 + 历史) + 顶层(当前笔,透明底叠在上面)

这样可以在绘画的时候永远都是第一笔,从而优化因为笔迹过多导致的卡顿

核心步骤:

两个独立上下文:

//底层画布上下文:只绘制已落笔的历史笔迹;在撤销/重做/清空、捏合、折叠等时整层重绘

  private cacheContext: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.setting);

  //顶层画布上下文:仅绘制当前手指未抬起时的「预览」笔迹,避免每次 Move 重画全部历史

  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.setting);

双层canvas:

  Stack() {

        Canvas(this.cacheContext)
          .width(CommonConstants.CANVAS_WIDTH)
          .height(CommonConstants.CANVAS_WIDTH)
          .backgroundColor($r('sys.color.white'))
          .hitTestBehavior(HitTestMode.None)
        Canvas(this.context)
          .width(CommonConstants.CANVAS_WIDTH)
          .height(CommonConstants.CANVAS_WIDTH)
          .backgroundColor(Color.Transparent)
}

//重建底层canvas:

//白底 + 重绘所有已提交路径到「底层」缓存画布

  private rebuildCommittedLayer(): void {
    const size = this.canvasExtent();
    this.cacheContext.fillStyle = Color.White;
    this.cacheContext.fillRect(CommonConstants.ZERO, CommonConstants.ZERO, size, size);
    this.drawInvoker.execute(this.cacheContext);
  }
  //清空「顶层」,露出底层已画好的内容。

  private clearPreviewLayer(): void {
    const size = this.canvasExtent();
    this.context.clearRect(CommonConstants.ZERO, CommonConstants.ZERO, size, size);
  }

//只建路径,不画

 if (event.touches.length === 1 && event.touches[0].id === 0 && event.type === TouchType.Down) {
          this.mPath = new DrawPath(this.mPaint, this.path2Db);
          this.mPath.paint = this.mPaint;
          this.mPath.path = new Path2D();
          this.mBrush.down(this.mPath.path, event.touches[0].x, event.touches[0].y);
        }

//只清顶层并重画当前笔(不重画全部历史,降低卡顿)

        if (event.touches.length === 1 && event.touches[0].id === 0 && event.type === TouchType.Move) {
          this.mBrush.move(this.mPath.path, event.touches[0].x, event.touches[0].y);
          this.clearPreviewLayer();
          this.mPath.draw(this.context);
        }

 //抬起:补全笔尾、写入命令栈、将本笔增量画入底层缓存,再清掉顶层预览(不再每次 Up 全量重画)

        if (event.touches.length === 1 && event.touches[0].id === 0 && event.type === TouchType.Up) {
          this.mBrush.up(this.mPath.path, event.touches[0].x, event.touches[0].y);
          this.add(this.mPath);
          this.mPath.draw(this.cacheContext);
          this.redoDraw = false;
          this.unDoDraw = true;
          this.clearPreviewLayer();
        }

更多关于HarmonyOS 鸿蒙Next中目前使用了canvas画布实现了手写笔相关功能软件,目前遇到的问题是在画布里面如果笔画的数量很大,会比较容易出现卡顿或者说不跟手之类的问题,应该如何解决?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


手写过程中如果每次 move 都清屏后把所有历史笔画重新画一遍,笔画一多,CPU 计算、路径重建、GPU 合成都会明显变重;再叠加缩放,卡顿和不跟手就很容易出现。

问题点:

  1. 触笔移动时,如果每一帧都重放全部历史笔画,复杂度会随着笔画数量持续上升。
  2. 缩放时如果不是对缓存结果做整体变换,而是重新计算并绘制所有路径,开销会进一步放大。
  3. 采样点过密、曲线点数过多,也会让单笔渲染成本变高。
  4. 页面本身如果还有遮罩、半透明层、重复背景等,也会带来过度绘制问题。

可以尝试按照以下思路试试

优化思路:

  1. 历史笔画先画进离屏层,书写中的当前一笔只画前景;抬笔时再合并进历史层。这样 move 过程中不需要重绘全部历史;
  2. 如果还是不行,那么建议改成ArkGraphics 2D(方舟2D图形服务)或者XComponent + Native Drawing C/C++来试试。

相关文档:

好的,

目前用canvas绘制的柱状图曲线图,目前暂时没遇到这个问题

针对鸿蒙Next canvas手写笔画卡顿、不跟手问题,建议:

  1. 采用离屏Canvas缓存已绘制笔画,仅增量更新新笔画。
  2. 启用硬件加速(如@ohos.graphics.drawing中的GPU渲染)。
  3. 使用双缓冲机制,减少绘制抖动。
  4. 限制每帧处理的笔画数,对旧笔画降采样或合并路径。
  5. 利用requestAnimationFrame同步渲染与触摸事件。

针对笔画数量大导致卡顿的问题,可重点从以下方面优化:

  1. 分层渲染与离屏缓存:已完成笔画无需反复绘制路径。将静态笔画绘制到离屏Canvas(OffscreenCanvas)并生成位图缓存,主Canvas只需drawImage该缓存,再叠加当前绘制的动态笔画,大幅降低重绘开销。

  2. 可视区域裁剪:在缩放或平移时,仅处理与当前视口相交的笔画,并利用clip限制绘制范围。可按网格或空间索引管理笔画,避免遍历全部。

  3. 笔画数据简化:存储时对轨迹点进行抽稀(如道格拉斯-普克算法),减少点数;路径密集处可不全部作为贝塞尔曲线,改用折线近似,降低计算量。

  4. 合理化刷新策略:使用requestAnimationFrame节流绘制频率;将书写手势事件与渲染解耦,避免频繁触发全量重绘。

  5. 硬件加速配置:确保Canvas创建时开启硬件加速,并检查renderMode是否设为direct,充分利用GPU合成。

通过缓存静态笔画、仅绘制可见区域并降低数据复杂度,可明显改善缩放与书写时的跟手性。

回到顶部