You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Canvas sketch中如何实现圆弧的平滑过渡动画效果

问题原因

你现在的动画出现跳顿感核心是两个问题:

  • 配置项里fps设为1.2远低于流畅动画需要的24+帧率,同时playbackRate: 'throttle'会强制按低帧率节流渲染,本身就会造成跳帧
  • 每帧渲染时都调用random.range()重新生成所有圆弧的半径、起止角度、线宽参数,相当于每帧直接跳到完全随机的新状态,没有过渡过程,自然会出现类似GIF的卡顿跳变
实现思路

和你之前做点位平滑移动的逻辑完全通用:

  1. 不要在每帧渲染时随机生成参数,在初始化阶段提前为每一段圆弧预先生成固定的基准随机值、运动相位差
  2. 利用canvas-sketch渲染回调提供的time(从动画开始累计的时间,单位秒)参数,用正弦/余弦等周期函数对圆弧的半径、角度、线宽做连续的插值计算,让参数随时间平滑变化,不要每帧重置随机值
  3. 把帧率调整到流畅动画的标准值,取消强制节流配置
修改后可直接运行的代码
const canvasSketch = require('canvas-sketch');
const math = require('canvas-sketch-util/math');
const random = require('canvas-sketch-util/random');

// 固定随机种子,每次刷新生成的图案基础形态一致,方便调试
random.setSeed(random.getRandomSeed());

const settings = {
  dimensions: [ 1080, 1080 ],
  animate: true,
  fps: 60, // 调整为流畅动画需要的帧率
  playbackRate: 'fixed' // 取消节流,按固定帧率渲染
};

const sketch = () => {
  const num = 12;
  const arcs = [];

  // 初始化阶段预生成所有圆弧的基础参数,不要在渲染循环里随机
  for (let i = 0; i < num; i++) {
    // 浅蓝内层圆弧参数
    arcs.push({
      baseAngle: i * math.degToRad(360 / num),
      baseRadius: random.range(0.7, 1.8),
      radiusAmp: random.range(0.05, 0.2), // 半径波动幅度
      baseStart: random.range(0, -5),
      startAmp: random.range(0.2, 0.8), // 起始角度波动幅度
      baseSweep: random.range(1, 7),
      sweepAmp: random.range(0.3, 1), // 扫角波动幅度
      baseLineWidth: random.range(5, 20),
      lineWidthAmp: random.range(1, 4), // 线宽波动幅度
      speed: random.range(0.2, 0.6), // 单个圆弧的运动速度
      phase: random.range(0, Math.PI * 2), // 相位差,避免所有圆弧同步运动
      color: '#52bafc'
    });

    // 深蓝外层圆弧参数
    arcs.push({
      baseAngle: i * math.degToRad(360 / num),
      baseRadius: random.range(0.9, 2),
      radiusAmp: random.range(0.06, 0.18),
      baseStart: random.range(1, -8),
      startAmp: random.range(0.25, 0.7),
      baseSweep: random.range(1, 6),
      sweepAmp: random.range(0.25, 0.9),
      baseLineWidth: random.range(8, 15),
      lineWidthAmp: random.range(0.8, 3),
      speed: random.range(0.15, 0.5),
      phase: random.range(0, Math.PI * 2),
      color: 'blue'
    });
  }

  return ({ context, width, height, time }) => {
    context.fillStyle = '#ccebff';
    context.fillRect(0, 0, width, height);

    const cx = width * 0.5;
    const cy = height * 0.5;
    const baseRadius = width * 0.2;
    const slice = math.degToRad(360 / num);
    // 整体缓慢旋转的速度系数
    const globalRotate = time * 0.1;

    arcs.forEach(arc => {
      // 用正弦函数计算当前帧的平滑参数,所有值连续变化无跳变
      const t = Math.sin(time * arc.speed + arc.phase);
      const currentRadius = baseRadius * (arc.baseRadius + t * arc.radiusAmp);
      const currentStart = slice * (arc.baseStart + t * arc.startAmp);
      const currentSweep = slice * (arc.baseSweep + t * arc.sweepAmp);
      const currentLineWidth = arc.baseLineWidth + t * arc.lineWidthAmp;
      const currentAngle = arc.baseAngle + globalRotate;

      context.save();
      context.translate(cx, cy);
      context.rotate(-currentAngle);
      context.lineWidth = currentLineWidth;
      context.beginPath();
      context.strokeStyle = arc.color;
      context.arc(0, 0, currentRadius, currentStart, currentStart + currentSweep);
      context.stroke();
      context.restore();
    });
  };
};

canvasSketch(sketch, settings);
可调参数说明
  • 如果想要动画动得更快/更慢,调整每个arc的speed取值范围,或者修改globalRotate的时间系数即可
  • 如果想要参数波动更大/更小,调整对应参数的Amp(波动幅度)值即可
  • 如果想要每次刷新生成完全不同的基础形态,删掉random.setSeed()那行就行

内容的提问来源于stack exchange,提问作者Laura Pasqualone

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 06:36:27