HarmonyOS鸿蒙Next中怎么实现进度水波纹动画?

HarmonyOS鸿蒙Next中怎么实现进度水波纹动画? 主体为中间有水波纹效果的圆形控件,需要实现0->100->0的加载动画。

4 回复

效果图:

https://alliance-communityfile-drcn.dbankcdn.com/FileServer/getFile/cmtybbs/127/948/569/0150086000127948569.20251209213718.13762485756168272422121233394212:50001231000000:2800:BDEED7EB7419192053A2CA82AF63D917A9C4CF0AF2C7BB54CEF6654E55319B05.gif

改进自:Circle显示不同进度水波纹

核心代码:

const startAnimation = () => {
  let v = 0; // 当前值 0-100
  let dir = 1; // 步进方向:+1 或 -1

  const tick = () => {
    v += dir; // 先走一步
    if (v === 0 || v === 100) {
      dir *= -1;
    } // 头尾掉头

    this.outSetValue = v;
    this.pathCommands = this.calPathCommands(v);

    const delay = (v === 0 || v === 100) ? 500 : 40; // 头尾停 500ms,其余 40ms
    setTimeout(tick, delay);
  };

  tick();
};

startAnimation();

完整代码:

const COLOR_TRANSPARENT = '#00000000';
const COLOR_BACKGROUND_FILL = '#007DFF';
const DIAMETER = 200;
const BIG_DIAMETER = 220;

@Entry
@ComponentV2
struct WaterRipplePage {
  @Local outSetValue: number = 50;
  @Local pathCommands: string = '';
  private RADIUS_IN_PX: number = this.getUIContext().vp2px(DIAMETER / 2.0);

  aboutToAppear() {
    const startAnimation = () => {
      let v = 0; // 当前值 0-100
      let dir = 1; // 步进方向:+1 或 -1

      const tick = () => {
        v += dir; // 先走一步
        if (v === 0 || v === 100) {
          dir *= -1;
        } // 头尾时掉头

        this.outSetValue = v;
        this.pathCommands = this.calPathCommands(v);

        const delay = (v === 0 || v === 100) ? 500 : 40; // 头尾停 500ms,其余 40ms
        setTimeout(tick, delay);
      };

      tick();
    };

    startAnimation();
  }

  calXSquare(y: number) {
    return this.RADIUS_IN_PX * this.RADIUS_IN_PX - (y - this.RADIUS_IN_PX) * (y - this.RADIUS_IN_PX);
  }

  calY(k: number) {
    return (1 - k) * this.RADIUS_IN_PX * 2;
  }

  formatPathCommands(x1: number, x2: number, y: number, radius: number) {
    return `M${x1} ${y} A${radius} ${radius} 0 ${y > this.RADIUS_IN_PX ? 0 : 1} 0  ${x2} ${y} ` +
      `Q${(x1 + 3 * x2) / 4} ${y + 12.5 * (x2 - x1) / radius}, ${(x1 + x2) / 2} ${y} T${x1} ${y}`;
  }

  calPathCommands(value: number): string {
    let y = this.calY(value / 100.0);
    let squareX = this.calXSquare(y);
    if (squareX >= 0) {
      let x = Math.sqrt(squareX);
      let x1 = this.RADIUS_IN_PX - x;
      let x2 = this.RADIUS_IN_PX + x;
      return this.formatPathCommands(x1, x2, y, this.RADIUS_IN_PX);
    }
    return '';
  }

  build() {
    Column() {
      Stack() {
        // 外框圆环
        Circle({ width: BIG_DIAMETER, height: BIG_DIAMETER })
          .fill(COLOR_TRANSPARENT)
          .stroke(COLOR_BACKGROUND_FILL)
          .strokeWidth(5);
        // 进度显示
        Circle({ width: DIAMETER, height: DIAMETER })
          .fill(this.outSetValue == 100 ? COLOR_BACKGROUND_FILL : COLOR_TRANSPARENT);
        Path()
          .width(DIAMETER)
          .height(DIAMETER)
          .stroke(COLOR_BACKGROUND_FILL)
          .commands(this.pathCommands)
          .fill(COLOR_BACKGROUND_FILL);
        // 进度
        Text(this.outSetValue.toFixed(0) + '%')
          .fontSize(60);
      }
      .width(BIG_DIAMETER)
      .height(BIG_DIAMETER);
    }
    .width('100%')
    .height('100%')
    .justifyContent(FlexAlign.Center);
  }
}

更多关于HarmonyOS鸿蒙Next中怎么实现进度水波纹动画?的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,可通过ArkUI的Canvas组件绘制进度水波纹动画。使用CanvasRenderingContext2D的API,结合正弦函数计算波纹路径,并通过requestAnimationFrame逐帧更新绘制位置与透明度,形成动态效果。关键步骤包括定义波纹参数、实现绘制函数以及控制动画循环。

在HarmonyOS Next中实现进度水波纹动画,可以通过Canvas绘制结合动画API完成。以下是核心实现步骤:

  1. 创建Canvas组件:在ArkUI中使用Canvas组件作为绘制容器。

    Canvas(this.context)
      .width('100%')
      .height('100%')
    
  2. 绘制水波纹:通过Path2D绘制贝塞尔曲线模拟波纹,使用gradient填充实现渐变效果。

    // 创建路径
    const path = new Path2D();
    path.moveTo(startX, startY);
    // 添加贝塞尔曲线形成波纹
    path.bezierCurveTo(...);
    // 创建线性渐变
    const gradient = this.context.createLinearGradient(...);
    this.context.fillStyle = gradient;
    this.context.fill(path);
    
  3. 实现动画循环:利用requestAnimationFrame更新波纹相位和高度,形成动态效果。

    let progress = 0;
    let direction = 1;
    
    const animate = () => {
      // 更新进度值(0->100->0循环)
      progress += direction * speed;
      if (progress >= 100 || progress <= 0) direction *= -1;
      
      // 计算波纹参数并重绘
      redrawWaves(progress);
      requestAnimationFrame(animate);
    };
    
  4. 性能优化:使用OffscreenCanvas进行离屏渲染,避免主线程阻塞。通过willReadFrequently属性提升渲染效率。

关键点:

  • 通过正弦函数控制波纹形状:y = amplitude * sin(frequency * x + phase)
  • 使用多层波纹叠加增强视觉效果
  • 通过opacity和颜色渐变实现自然过渡

完整实现需结合具体进度值动态计算波纹振幅、频率和相位偏移量。动画平滑度可通过调整帧率和插值函数进一步优化。

回到顶部