在这里插入图片描述
在这里插入图片描述

一、设计思路

"波点变动"是本章技术核心:一个波点沿直线 y = kx + b 匀速往返,让抽象的线性关系"动"起来。实现要点有四:帧驱动(setInterval 定时步进)、状态机(movingRight 控制往返)、坐标映射(数学系 ↔ 像素系)、象限判定(坐标符号 → 象限文本)。本篇拆解这四块代码。

二、帧驱动:setInterval 每 30ms 步进一步

@Entry
@Component
struct LinearEquation {
  private timer: number = 0;          // 定时器句柄(销毁时清理)

  aboutToAppear(): void {
    this.solve();
    this.draw();
    // 波点变动:每 30ms 前进 0.2,≈33 帧/秒,视觉平滑
    this.timer = setInterval(() => { this.tick(); }, 30);
  }

  aboutToDisappear(): void {
    clearInterval(this.timer);        // 离开页面必须清理,否则后台空转
  }

  // 每一帧:x 步进 → y 重算 → 象限刷新 → 重绘
  private tick(): void {
    let nx = this.dotX + (this.movingRight ? 0.2 : -0.2);
    if (nx >= this.xRange) {          // 到达右边界
      nx = this.xRange;
      this.movingRight = false;       // 掉头向左
    }
    if (nx <= -this.xRange) {         // 到达左边界
      nx = -this.xRange;
      this.movingRight = true;        // 掉头向右
    }
    this.dotX = nx;
    this.dotY = this.yOf(nx);         // 波点永远"贴"在直线上
    this.dotQuadrant = this.quadrantOf(nx, this.dotY);
    this.draw();
  }
}

要点:

  • setInterval 返回 number 型句柄,必须保存,aboutToDisappearclearInterval,这是 HarmonyOS 页面生命周期的最佳实践。
  • 步长 0.2、间隔 30ms → 速度约 6.7 单位/秒,x 全范围 20 单位约 3 秒一趟往返,节奏适中。
  • tick() 是"纯步进":只修改 @State,不做计算外的事情,职责单一。

三、坐标映射:数学系 ↔ 像素系

// 世界坐标 → 画布像素(画布中心 = 原点,y 轴向上为正)
private toPixel(x: number, y: number, w: number, h: number): Pixel {
  return {
    px: w / 2 + (x / this.xRange) * (w / 2),
    py: h / 2 - (y / this.yRange) * (h / 2)
  };
}
坐标系 原点 y 方向 x 范围 y 范围
数学系 画布中心 向上为正 ±10 ±6
像素系 左上角 向下为正 0~w 0~h

映射公式拆解:

  • px = w/2 + (x/10) * (w/2):x = 0 时在中心;x = +10 时到右边缘;x = −10 到左边缘。
  • py = h/2 − (y/6) * (h/2):唯一差别是 y 前是负号——把数学的"向上"翻转为像素的"向下"。
  • 所有绘制(网格、轴线、直线、波点)都经过这一个函数,保证全图坐标一致。

四、象限判定:坐标符号 → 象限文本

private quadrantOf(x: number, y: number): string {
  if (x >= 0 && y >= 0) return '第一象限';
  if (x <= 0 && y >= 0) return '第二象限';
  if (x <= 0 && y <= 0) return '第三象限';
  return '第四象限';
}

象限判定就是符号表的代码化

象限 x 符号 y 符号 口诀
+ + 同正
+ 左正上
同负
+ 右下

边界(坐标轴上)归入非负区:x = 0 且 y > 0 判为Ⅱ象限,保证任何时刻都有确定结果。

五、波点绘制:光晕 + 实心 + 描边

const dp = this.toPixel(this.dotX, this.dotY, w, h);
// 外圈光晕:半径 10 的半透明绿
ctx.beginPath(); ctx.arc(dp.px, dp.py, 10, 0, Math.PI * 2);
ctx.fillStyle = 'rgba(16,185,129,0.25)';
ctx.fill();
// 实心核心:半径 5 的亮绿
ctx.beginPath(); ctx.arc(dp.px, dp.py, 5, 0, Math.PI * 2);
ctx.fillStyle = '#10B981';
ctx.fill();
// 白色描边:增强对比,深色背景也清晰
ctx.strokeStyle = Color.White;
ctx.lineWidth = 2;
ctx.stroke();

三层绘制的意义:光晕制造"发光感",让运动中的点更醒目;白描边保证任何背景色下都可见。Color.White 是 ArkUI 内置枚举,Canvas 可直接用作 strokeStyle。

六、动画与 UI 双刷新的配合

// 数据面板:@State 自动刷新 —— 不需要任何手动操作
Text(this.dotX.toFixed(1))       // 每次 tick 改 dotX 后自动重渲染

// Canvas 图:手动重绘 —— 需要每帧调用 draw()
// 因此 tick() 末尾固定调 this.draw()
// 波点数据面板(UI 层,@State 驱动,自动刷新)
Row({ space: 10 }) {
  Column({ space: 4 }) {
    Text('横坐标 x').fontSize(12).fontColor('#9CA3AF')
    Text(this.dotX.toFixed(1))
      .fontSize(18).fontWeight(FontWeight.Bold).fontColor('#10B981')
  }.layoutWeight(1)
  Column({ space: 4 }) {
    Text('纵坐标 y').fontSize(12).fontColor('#9CA3AF')
    Text(this.dotY.toFixed(1))
      .fontSize(18).fontWeight(FontWeight.Bold).fontColor('#10B981')
  }.layoutWeight(1)
  Column({ space: 4 }) {
    Text('所在象限').fontSize(12).fontColor('#9CA3AF')
    Text(this.dotQuadrant)
      .fontSize(14).fontWeight(FontWeight.Bold).fontColor('#4C7DFF')
  }.layoutWeight(1.4)
}.width('100%').margin({ top: 4 })

性能注意:30ms 一帧、一帧只改 3 个 @State + 一次 Canvas 重绘,开销极小;不要在 tick 里做字符串拼接、数组操作等重活。

七、调试与扩展建议

需求 改哪里
加快/减慢波点 setInterval 间隔或步长 0.2
换运动轨迹 tick() 中 x 的更新规则(如正弦:x = sin(t)
显示轨迹残影 clearRect,改用半透明底色填充即可
显示坐标网格刻度 在网格线上加 fillText 标注数字
暂停/继续 用 @State 布尔值包住 tick() 主体

八、波点变动与数学本质

波点变动不只是动画——它演示了函数的本质:给定 x,函数唯一确定 y。波点每走一步,y = kx + b 都被验证一次;当它扫过交点(方程 ax + b = 0 的解在 x 轴上对应的点)时,可以引导观察"y = 0 时 x 的值正是方程的解"。代码与数学在这个页面上互为注解。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐