Canvas sketch中如何实现圆弧的平滑过渡动画效果
问题原因
你现在的动画出现跳顿感核心是两个问题:
- 配置项里
fps设为1.2远低于流畅动画需要的24+帧率,同时playbackRate: 'throttle'会强制按低帧率节流渲染,本身就会造成跳帧 - 每帧渲染时都调用
random.range()重新生成所有圆弧的半径、起止角度、线宽参数,相当于每帧直接跳到完全随机的新状态,没有过渡过程,自然会出现类似GIF的卡顿跳变
实现思路
和你之前做点位平滑移动的逻辑完全通用:
- 不要在每帧渲染时随机生成参数,在初始化阶段提前为每一段圆弧预先生成固定的基准随机值、运动相位差
- 利用canvas-sketch渲染回调提供的
time(从动画开始累计的时间,单位秒)参数,用正弦/余弦等周期函数对圆弧的半径、角度、线宽做连续的插值计算,让参数随时间平滑变化,不要每帧重置随机值 - 把帧率调整到流畅动画的标准值,取消强制节流配置
修改后可直接运行的代码
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
相关产品推荐
相关产品推荐

