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中,实现水波纹进度动画可以使用Canvas组件结合Path2D绘制贝塞尔曲线来模拟水波纹效果。通过Animator或关键帧动画改变波纹的相位偏移和振幅,形成动态进度。主要步骤包括:定义CanvasRenderingContext2D绘图上下文,使用bezierCurveTo绘制波形路径,并通过fill方法填充颜色。利用setIntervalrequestAnimationFrame更新波纹的offsetX属性,结合进度值控制波纹宽度。

在HarmonyOS Next中实现水波纹进度动画,可以通过Canvas自定义绘制结合动画API来完成。以下是核心实现思路和关键代码示例:

1. 创建自定义组件

使用@Component定义自定义组件,通过CanvasRenderingContext2D进行波纹绘制:

@Component
export struct RippleProgress {
  private settings: RenderingContextSettings = new RenderingContextSettings(true)
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings)
  
  @State private progress: number = 0
  @State private direction: number = 1 // 1表示增加,-1表示减少
}

2. 实现波纹绘制函数

onDraw方法中绘制动态波纹效果:

onDraw(context: CanvasRenderingContext2D) {
  // 清空画布
  context.clearRect(0, 0, this.width, this.height)
  
  // 绘制背景圆
  context.beginPath()
  context.arc(this.width/2, this.height/2, this.radius, 0, Math.PI * 2)
  context.fillStyle = '#F0F0F0'
  context.fill()
  
  // 计算当前波纹半径(基于progress)
  const rippleRadius = this.radius * (this.progress / 100)
  
  // 绘制水波纹
  context.beginPath()
  context.arc(this.width/2, this.height/2, rippleRadius, 0, Math.PI * 2)
  context.strokeStyle = `rgba(0, 150, 255, ${0.7 - (this.progress/100)*0.3})`
  context.lineWidth = 3
  context.stroke()
}

3. 添加动画控制

使用animateTo实现0→100→0的循环动画:

aboutToAppear() {
  this.startAnimation()
}

private startAnimation() {
  animateTo({
    duration: 2000,
    iterations: -1, // 无限循环
    onStart: () => {
      this.progress = 0
      this.direction = 1
    },
    onUpdate: (value: number) => {
      // 计算进度值
      if (this.direction > 0) {
        this.progress = value * 100
        if (this.progress >= 100) {
          this.direction = -1
        }
      } else {
        this.progress = 100 - (value * 100)
        if (this.progress <= 0) {
          this.direction = 1
        }
      }
    }
  })
}

4. 布局中使用

@Entry
@Component
struct Index {
  build() {
    Column() {
      RippleProgress()
        .width(200)
        .height(200)
    }
  }
}

关键点说明:

  1. 状态管理:使用@State装饰器管理进度值和方向状态
  2. 动画控制:通过animateToiterations: -1实现无限循环
  3. 性能优化:建议使用Canvas的离屏渲染或LazyForEach处理复杂波纹效果
  4. 自定义扩展:可通过参数控制波纹颜色、速度、振幅等视觉效果

这种实现方式利用了HarmonyOS Next的声明式UI和动画能力,能够流畅地渲染水波纹动画效果。

回到顶部