HarmonyOS鸿蒙Next中如何实现一个可拖动的环形进度条,且支持自定义背景图片

HarmonyOS鸿蒙Next中如何实现一个可拖动的环形进度条,且支持自定义背景图片

想实现类似图中的环形进度条拖动效果,要求:

  1. 可以通过拖动末端(下面红框)来调整圆环的进度,从 0 到 360 度

  2. 首端(上面红框)、末端(下面红框)以及轨道(红色箭头部分)的背景可以使用自定义图片

我看了下,progress、datapanel 好像都不太适合,是不是只能用 canvas + 监听 touch 事件自行实现?期望能给出简单 demo,谢谢!

2 回复

在HarmonyOS Next中,可通过Canvas组件绘制可拖动的环形进度条。使用CanvasRenderingContext2Darc方法绘制环形轨道和进度条,通过@Watch监听触摸事件处理拖动逻辑。自定义背景图片通过Image组件或PixelMap实现,可将其设置为Canvas背景或叠加在环形图层下方。利用Canvas的矩阵变换功能调整图片尺寸和位置,确保与进度条视觉协调。具体实现涉及角度计算和手势交互处理,需结合ArkTS声明式开发完成。

更多关于HarmonyOS鸿蒙Next中如何实现一个可拖动的环形进度条,且支持自定义背景图片的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,实现可拖动的自定义环形进度条,推荐使用Canvas结合触摸事件处理,因为Progress和DataPanel组件无法直接支持拖动交互和图片背景自定义。以下是关键实现思路和代码示例:

  1. 使用Canvas绘制环形进度条:通过CanvasRenderingContext2D绘制圆环轨道、进度填充及首尾端点。
  2. 自定义图片资源:将图片资源放入resources/base/media/目录,通过ResourceManager加载。
  3. 触摸事件处理:监听onTouch事件,计算触摸点与圆心的角度,动态更新进度值并重绘。

示例代码(基于ArkTS):

@Entry
@Component
struct DraggableCircularProgress {
  private settings: RenderingContextSettings = new RenderingContextSettings(true);
  private ctx: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
  private progress: number = 0; // 进度值(0-360)
  private centerX: number = 150;
  private centerY: number = 150;
  private radius: number = 100;

  build() {
    Column() {
      Canvas(this.ctx)
        .width(300)
        .height(300)
        .backgroundColor('#f0f0f0')
        .onReady(() => {
          this.drawProgress();
        })
        .onTouch((event: TouchEvent) => {
          this.handleTouch(event);
        })
    }
  }

  // 绘制进度条
  private drawProgress() {
    this.ctx.clearRect(0, 0, 300, 300);
    
    // 绘制轨道(使用自定义图片,此处用颜色代替)
    this.ctx.beginPath();
    this.ctx.arc(this.centerX, this.centerY, this.radius, 0, Math.PI * 2);
    this.ctx.strokeStyle = '#e0e0e0';
    this.ctx.lineWidth = 10;
    this.ctx.stroke();

    // 绘制进度(使用自定义图片,此处用颜色代替)
    this.ctx.beginPath();
    this.ctx.arc(this.centerX, this.centerY, this.radius, -Math.PI/2, (this.progress * Math.PI / 180) - Math.PI/2);
    this.ctx.strokeStyle = '#007dff';
    this.ctx.lineWidth = 10;
    this.ctx.stroke();

    // 绘制首端和末端(使用自定义图片,此处用圆形代替)
    this.drawEndpoint(this.progress);
  }

  // 绘制端点
  private drawEndpoint(angle: number) {
    const radian = (angle - 90) * Math.PI / 180;
    const x = this.centerX + this.radius * Math.cos(radian);
    const y = this.centerY + this.radius * Math.sin(radian);
    
    this.ctx.beginPath();
    this.ctx.arc(x, y, 15, 0, Math.PI * 2);
    this.ctx.fillStyle = '#ff0000';
    this.ctx.fill();
  }

  // 处理触摸事件
  private handleTouch(event: TouchEvent) {
    if (event.type === TouchType.Move) {
      const touchX = event.touches[0].x;
      const touchY = event.touches[0].y;
      
      // 计算触摸点相对于圆心的角度
      let angle = Math.atan2(touchY - this.centerY, touchX - this.centerX) * 180 / Math.PI + 90;
      if (angle < 0) angle += 360;
      
      this.progress = Math.min(360, Math.max(0, angle));
      this.drawProgress();
    }
  }
}

自定义图片实现

  • 将图片文件放入resources/base/media/,通过$r('app.media.imageName')加载。
  • 在绘制轨道和端点时,使用drawImage方法替换颜色填充。

注意事项

  • 需要处理图片加载的异步逻辑,确保绘制时资源已就绪。
  • 可通过调整lineWidth和端点半径控制视觉效果。
  • 触摸事件中需添加边界判断,避免进度值超出范围。

此方案提供了完整的交互逻辑和绘制流程,实际应用中可根据需求调整样式和交互细节。

回到顶部