《HarmonyOS技术精讲-ArkGraphics 2D(方舟2D图形服务)》第七篇:动效来袭——与ArkUI结合实现2D动画与交互

HarmonyOS技术精讲-ArkGraphics 2D(方舟2D图形服务)第七篇:动效来袭------与ArkUI结合实现2D动画与交互

Canvas动画加上交互,难在哪

ArkUI 里做 Canvas 动画,大多数人第一次写都会遇到两个问题:状态同步和触摸坐标转换。

状态同步指的是动画循环里的变量(比如旋转角度)如何驱动 Canvas 重新绘制,同时又不让整个组件频繁重建。触摸坐标转换则是手指在屏幕上滑动时,如何准确映射到 Canvas 的绘制坐标系。

这两个问题单独看都不复杂,但放在一起处理时,生命周期管理、动画启停、交互响应这些细节很容易出 bug。官方示例通常只展示静态绘制或者简单动画,一旦掺入用户交互,代码就需要额外处理不少边界情况。

这篇文章用一个可拖拽旋转的风车作为案例,把状态管理、动画循环、触摸交互这三块内容串起来讲。风车叶片持续旋转,用户拖拽时改变旋转方向和速度,松手后带惯性减速。这个场景刚好覆盖了 ArkGraphics 2D 与 ArkUI 交互的核心技术点。

它解决什么问题

这个案例的目标是:在 ArkUI 中实现一个连续动画的 2D 图形,并且用户可以通过触摸直接控制动画状态。

适用场景包括:

  • 需要用户手势操控的图形化控件(如旋钮、转盘)
  • 带有惯性效果的 UI 组件(如滑动选择器)
  • 游戏中的拖拽旋转元素

不适用场景:

  • 纯静态图形展示(不需要动画循环)
  • 复杂 3D 场景(应使用 WebGL 或专用引擎)

与直接用 ArkUI 的动画属性(如 animation)相比,Canvas 方式有几个明显差异:

特性 Canvas + requestAnimationFrame ArkUI animation 属性
绘制自由度 任意 2D 图形,无组件限制 仅支持组件级属性动画
交互控制 可精细控制每帧状态 通过交互事件触发动画
性能开销 逐帧绘制,GPU 友好 组件属性变化触发重绘
惯性/物理模拟 容易实现 需要额外计算

这个案例选择 Canvas 方案,主要是因为风车的叶片形状(扇形)在 ArkUI 原生组件中无法直接实现,而 Canvas 可以自由绘制。同时,拖拽旋转和惯性效果需要逐帧控制角度,这正是 requestAnimationFrame 擅长的领域。

环境说明

text 复制代码
DevEco Studio 版本:DevEco Studio 6.1.0 及以上
HarmonyOS SDK 版本:HarmonyOS 6.1.0(23) 及以上
目标设备:手机

核心实现

整个功能分解为四个部分:状态管理、Canvas 绘制、动画循环、触摸交互。下面逐个实现。

状态管理

风车的核心状态是旋转角度角速度。角度决定当前绘制位置,角速度控制惯性旋转。

typescript 复制代码
// 风车状态
@State currentAngle: number = 0;    // 当前旋转角度(弧度)
private angularVelocity: number = 0; // 当前角速度(弧度/帧)
private isDragging: boolean = false; // 是否正在拖拽

@State 装饰器让 currentAngle 的变化自动触发 UI 刷新。angularVelocityisDragging 不需要驱动 UI,所以用 private 标记,避免不必要的状态追踪。

这里有个设计取舍:为什么不把 angularVelocity 也设为 @State?因为角速度只在动画循环中使用,不直接影响 Canvas 绘制内容。如果把它也设为响应式状态,每次修改都会触发组件更新检查,反而增加开销。

Canvas 绘制风车

风车由 4 个扇形叶片和一个中心圆组成。每个叶片使用 arc 绘制扇形,填充不同颜色。

typescript 复制代码
// 绘制风车
private drawWindmill(ctx: CanvasRenderingContext2D, width: number, height: number): void {
  const cx = width / 2;
  const cy = height / 2;
  const bladeLength = 80;      // 叶片长度
  const bladeAngle = Math.PI / 3; // 叶片张开角度 60°
  const colors = ['#FF6B6B', '#4ECDC4', '#45B7D1', '#FFE66D'];

  // 清空画布
  ctx.clearRect(0, 0, width, height);

  // 绘制背景辅助圆(淡灰色)
  ctx.beginPath();
  ctx.arc(cx, cy, bladeLength + 10, 0, Math.PI * 2);
  ctx.strokeStyle = '#E0E0E0';
  ctx.lineWidth = 1;
  ctx.stroke();

  // 绘制 4 个叶片
  for (let i = 0; i < 4; i++) {
    const startAngle = this.currentAngle + i * Math.PI / 2 - bladeAngle / 2;
    const endAngle = this.currentAngle + i * Math.PI / 2 + bladeAngle / 2;

    ctx.beginPath();
    ctx.moveTo(cx, cy);
    ctx.arc(cx, cy, bladeLength, startAngle, endAngle);
    ctx.closePath();
    ctx.fillStyle = colors[i];
    ctx.fill();

    // 叶片边缘描边
    ctx.strokeStyle = '#333';
    ctx.lineWidth = 1.5;
    ctx.stroke();
  }

  // 绘制中心圆
  ctx.beginPath();
  ctx.arc(cx, cy, 14, 0, Math.PI * 2);
  ctx.fillStyle = '#2C3E50';
  ctx.fill();
  ctx.strokeStyle = '#1A252F';
  ctx.lineWidth = 2;
  ctx.stroke();

  // 中心小圆点
  ctx.beginPath();
  ctx.arc(cx, cy, 4, 0, Math.PI * 2);
  ctx.fillStyle = '#FFFFFF';
  ctx.fill();
}

这里需要注意:叶片的角度范围是 currentAngle 加上每个叶片的偏移量(i * π/2),再加减半个叶片张角。这样 4 个叶片正好均匀分布,整体旋转由 currentAngle 控制。

中心圆加两层是为了视觉层次感,小圆点作为装饰,也让触摸拖拽时有个视觉焦点。

动画循环

动画循环使用 requestAnimationFrame 驱动,每帧更新角度并重绘。

typescript 复制代码
private animatorId: number = -1;

private startAnimation(ctx: CanvasRenderingContext2D, width: number, height: number): void {
  const animate = () => {
    if (!this.isDragging && Math.abs(this.angularVelocity) > 0.001) {
      // 惯性旋转:角度累加,速度衰减
      this.currentAngle += this.angularVelocity;
      this.angularVelocity *= 0.985; // 阻尼系数
    }

    this.drawWindmill(ctx, width, height);
    this.animatorId = requestAnimationFrame(animate);
  };

  animate();
}

private stopAnimation(): void {
  if (this.animatorId !== -1) {
    cancelAnimationFrame(this.animatorId);
    this.animatorId = -1;
  }
}

惯性效果的核心逻辑:松手后 angularVelocity 逐渐衰减到零,每次衰减乘以 0.985。这个系数决定了惯性持续的时间,0.98 到 0.99 之间手感比较自然,可以根据需要调整。

示例代码地址:项目地址

相关推荐
HarmonyOS_SDK1 小时前
Account Kit新能力:AI赋能Skill接入“华为账号一键登录”,实现6倍效率飞跃
harmonyos
在书中成长1 小时前
HarmonyOS 小游戏《对战五子棋》开发第14篇 - 困难AI实现:Minimax算法原理详解
人工智能·算法·harmonyos
国服第二切图仔2 小时前
HarmonyOS APP《画伴梦工厂》开发第54篇-鸿蒙AI开放能力——语音与自然语言处理
人工智能·自然语言处理·harmonyos
在书中成长2 小时前
HarmonyOS 小游戏《对战五子棋》开发第12篇 - 简单AI实现:规则引擎与优先级策略
人工智能·华为·harmonyos
大雷神3 小时前
HarmonyOS 6.1 新能力实战:用“双镜记忆相机”把照片、地点与人重新连起来
数码相机·华为·harmonyos
心中有国也有家3 小时前
Flutter 鸿蒙编译与构建流程深度解析
学习·flutter·华为·harmonyos
水龙吟啸4 小时前
华为2026.6.24机考选择题+编程题【速刷敲黑板】
人工智能·深度学习·算法·机器学习·华为
leoZ2314 小时前
Claude Code 页面级交互实战:表单、弹窗与数据联动
交互
花开彼岸天~4 小时前
01 鸿蒙 NEXT 开发环境搭建完全指南(DevEco Studio 5.0)
华为·harmonyos