在HarmonyOS NEXT开发中如何用动画实现Canvas内容的改变?

在HarmonyOS NEXT开发中如何用动画实现Canvas内容的改变?需要用Canvas绘制一些内容,且内容在组件状态改变时,需要动画效果

阅读 757
avatarAI BotBETA

最佳实践方案(HarmonyOS NEXT API 10+)

核心步骤:

// 1. 定义动画参数
@State private animationValue: number = 0;
private animator: animator.Animator = new animator.Animator();

// 2. 创建Canvas组件
Canvas()
  .width('100%')
  .height('100%')
  .onReady(() => this.startAnimation()) // 初始化动画
  .onDraw((canvas: CanvasContext) => {
    // 3. 根据动画值动态绘制内容
    const centerX = 200;
    const centerY = 200;
    const radius = 50 + this.animationValue * 30; // 半径变化动画
    
    // 使用Path2D进行高级绘制
    const path = new Path2D();
    path.arc(centerX, centerY, radius, 0, 2 * Math.PI);
    
    // 设置渐变动画效果
    const gradient = canvas.createLinearGradient(150, 150, 250, 250);
    gradient.addColorStop(0, `rgba(255,0,0,${1 - this.animationValue})`);
    gradient.addColorStop(1, `rgba(0,0,255,${this.animationValue})`);

    canvas.fillStyle = gradient;
    canvas.fill(path);
  })

// 4. 动画控制方法
private startAnimation(): void {
  this.animator.stop();
  this.animator = animator.create({
    duration: 1000,
    iterations: Infinity,
    curve: animator.Curve.EaseInOut
  });

  this.animator.onframe = (value: number) => {
    this.animationValue = value;
    // 5. 强制重绘Canvas
    this.canvasContext?.requestRefresh();
  };
  
  this.animator.play();
}

关键特性说明:

  1. Path2D优化:使用高性能Path2D对象替代传统路径绘制
  2. 颜色动画:通过rgba透明度插值实现颜色渐变效果
  3. 动态刷新:使用requestRefresh()替代直接赋值提高性能
  4. 曲线运动:EaseInOut曲线实现自然加速/减速效果

高级技巧:

// 使用矩阵变换实现复杂动画
canvas.translate(this.animationValue * 100, Math.sin(this.animationValue * Math.PI) * 50);
canvas.rotate(this.animationValue * 2 * Math.PI);

// 实现粒子系统动画
const particles = Array(100).fill(0).map(() => ({
  x: Math.random() * 400,
  y: Math.random() * 400,
  vx: (Math.random() - 0.5) * this.animationValue * 5,
  vy: (Math.random() - 0.5) * this.animationValue * 5
}));

particles.forEach(p => {
  canvas.beginPath();
  canvas.arc(p.x + p.vx, p.y + p.vy, 3, 0, Math.PI * 2);
  canvas.fill();
});

性能优化建议:

  1. 使用OffscreenCanvas进行复杂计算
  2. 对于静态内容启用willReadFrequently标记
  3. 采用分层渲染策略(背景层/动态层分离)
  4. 使用requestAnimationFrame配合动画系统
注意: 当处理复杂动画时,建议通过@Reusable装饰器优化组件刷新性能,同时使用CanvasRenderer进行WebGL加速渲染以获得更好的性能表现。
1 个回答

canvas动画实现方式,下面的demo可以作为参考:

import curves from '@ohos.curves'; 
 
@Entry 
@Component 
struct AnimationDemo { 
  @State animate: boolean = false; // 第一步: 声明相关状态变量 
  @State rotateValue: number = 0; // 组件一旋转角度 
  @State translateX: number = 0; // 组件二偏移量 
  @State opacityValue: number = 1; // 组件二透明度 
  private settings: RenderingContextSettings = new RenderingContextSettings(true) 
  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings) // 第二步:将状态变量设置到相关可动画属性接口 
 
  build() { 
    Row() { 
      //组件一 
      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center }) { 
        Canvas(this.context) 
          .onReady(() => { 
            this.context.fillRect(0, 0, 100, 100) 
          }) 
      } 
      .opacity(this.opacityValue) 
      .rotate({ angle: this.rotateValue }) 
      .animation({ curve: curves.springMotion() }) //通过属性动画接口开启属性动画 
      .backgroundColor('#317AF7') 
      .width(100) 
      .height(100) 
      .borderRadius(30) 
      .onClick(() => { 
        this.animate = !this.animate 
        //闭包内通过状态变量改变UI界面 // 
        // 这里可以写任何能改变UI的逻辑比如数组添加,显隐控制,系统会检测改变后的UI界面与之前的UI界面的差异,对有差异的部分添加动画 // 
        // 组件一的rotate属性发生变化,所以会给组件一添加rotate旋转动画 
        this.rotateValue = this.animate ? 90 : 0 
        // 组件二的offset属性发生变化,所以会给组件二添加offset偏移动画 
        this.translateX = this.animate ? 50 : 0 
        this.opacityValue = this.animate ? 0.6 : 1 
      }) 
 
      // 组件二 
      Column() { 
      } 
      .justifyContent(FlexAlign.Center) 
      .width(100) 
      .height(100) 
      .backgroundColor('#D94838') 
      .borderRadius(30) 
      .opacity(this.opacityValue) 
      .translate({ x: this.translateX }) 
      .animation({ curve: curves.springMotion() }) 
      .width('100%') 
      .height('100%') 
      .justifyContent(FlexAlign.Center) 
    } 
 
  } 
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进