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

如何在Canvas 2D中实现平滑滚动的无跳变重复线性渐变

Canvas 2D 无缝循环滚动渐变实现方案

跳变原因

Canvas 2D 标准的createLinearGradient API 原生不支持渐变重复平铺,没有等价于CSS repeating-linear-gradient的能力。原有代码直接将移动到pos>1位置的色标重置到pos=0,会打断色标间的颜色插值连续性,必然产生视觉跳变。

核心实现逻辑

通过双段色带拼接+全局偏移控制实现无跳变循环,不需要离屏绘制:

  • 固定所有色标的原始位置,单独维护全局滚动偏移量,从根源避免单个色标位置跳变
  • 将渐变线长度扩展为单组色带的2倍,在渐变区间内放置两组完全相同的色带,首尾色值保持一致保证衔接平滑
  • 每帧累加偏移量,当偏移量走完1个完整色带周期时直接将偏移量减1,此时第二组色带刚好完全对齐原第一组色带的显示位置,视觉上完全重合,无任何跳变
  • 添加色标时,单个色标的实际位置为原始位置 + 组序号 - 全局偏移,保证所有色标落在0~2的有效渐变区间内

修正后完整代码

JavaScript

const colors = [
  { color: "#FF0000", pos: 0 },
  { color: "#FFFF00", pos: 1 / 5 },
  { color: "#00FF00", pos: 2 / 5 },
  { color: "#0000FF", pos: 3 / 5 },
  { color: "#FF00FF", pos: 4 / 5 },
  { color: "#FF0000", pos: 1 },
];

const angleStep = 0.2;
const linearStep = 0.001;

function init() {
  const canvas = document.querySelector("canvas");
  const context = canvas.getContext("2d");

  const mw = canvas.width;
  const mh = canvas.height;
      
  let angle = 0;
  // 全局偏移量,替代直接修改色标pos的逻辑
  let offset = 0;

  function drawScreen() {
    angle = (angle + angleStep) % 360;
    // 偏移走满一个周期直接重置,无视觉跳变
    offset += linearStep;
    if (offset >= 1) offset -= 1;

    const [x1, y1, x2, y2] = angleToPoints(angle, mw, mh);
    // 渐变线延长为原来2倍,容纳两组重复色带
    const gradient = context.createLinearGradient(
      x1 - (x2 - x1), y1 - (y2 - y1),
      x2, y2
    );
    // 填充两组完全一致的色带
    for (let i = 0; i < 2; i++) {
      for (const colorStop of colors) {
        const realPos = colorStop.pos + i - offset;
        gradient.addColorStop(realPos, colorStop.color);
      }
    }

    context.fillStyle = gradient;
    context.fillRect(0, 0, canvas.width, canvas.height);
  }
      
  function loop() {
    drawScreen()
    window.requestAnimationFrame(loop);
  }
  
  loop();
}
    

function angleToPoints(angle, width, height){
  const rad = ((180 - angle) / 180) * Math.PI;
  const length = Math.abs(width * Math.sin(rad)) + Math.abs(height * Math.cos(rad));
  const halfx = (Math.sin(rad) * length) / 2.0
  const halfy = (Math.cos(rad) * length) / 2.0
  const cx = width / 2.0
  const cy = height / 2.0
  const x1 = cx - halfx
  const y1 = cy - halfy
  const x2 = cx + halfx
  const y2 = cy + halfy
  return [x1, y1, x2, y2];
}

init();

CSS

html,body, canvas {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
}

HTML

<canvas width="128" height="72"></canvas>

效果说明

该实现和CSS repeating-linear-gradient实现的滚动渐变效果完全一致,任意角度下都不会出现边缘色值突变、帧跳变的问题,不需要额外创建离屏Canvas做二次绘制,性能和原有实现基本持平。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 18:27:25