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

如何让HTML5 Canvas旋转渐变围绕中心完全覆盖画布?

实现任意角度下完全覆盖Canvas的线性渐变

我需要实现一个无论角度如何都能完全覆盖HTML5 Canvas的线性渐变效果,参考相关方法后发现存在问题——画布无法被渐变完全覆盖。我修改了代码,用黄色区域标记本应被黑白渐变覆盖的区域(正常情况下黄色应该完全不可见),同时用绿色点绘制了渐变的控制点,正确计算的话这些点应该延伸到画布边缘。

问题复现代码

HTML

<canvas id="canvas" width="300" height="200"></canvas>

CSS

canvas {
  border : 2px solid red;
}

JavaScript

var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;

function bestFitGradient(angle){
    
    var dist = Math.sqrt(w * w + h * h) / 2; // 获取对角线长度的一半
    var diagAngle = Math.asin((h / 2) / dist); // 获取对角线对应的角度

    // 角度对称处理,转到第一象限
    var a1 = ((angle % (Math.PI *2))+ Math.PI*4) % (Math.PI * 2);
    if(a1 > Math.PI){ a1 -= Math.PI }
    if(a1 > Math.PI / 2 && a1 <= Math.PI){ a1 = (Math.PI / 2) - (a1 - (Math.PI / 2)) }
    // 计算渐变方向及右侧到边缘的角度
    var ang1 = Math.PI/2 - diagAngle - Math.abs(a1);
    var ang2 = Math.abs(diagAngle - Math.abs(a1));
    
    // 获取中心到水平和垂直边缘的距离
    var dist1 = Math.cos(ang1) * h;
    var dist2 = Math.cos(ang2) * w;
    
    // 取最大距离
    var scale = Math.max(dist2, dist1) / 2;
    
    // 计算渐变起点和终点的向量
    var dx = Math.cos(angle) * scale;
    var dy = Math.sin(angle) * scale;
    
    var x0 =  w / 2 + dx;
    var y0 =  h / 2 + dy;
    var x1 =  w / 2 - dx;
    var y1 =  h / 2 - dy;
    
    // 创建渐变
    const g = ctx.createLinearGradient(x0, y0, x1, y1);
    
    // 添加颜色节点
    g.addColorStop(0, "yellow");
    g.addColorStop(0, "white");
    g.addColorStop(.5, "black");
    g.addColorStop(1, "white");
    g.addColorStop(1, "yellow");
    
    return {
      g: g,
      x0: x0,
      y0: y0,
      x1: x1,
      y1: y1
   };
}   

function update(timer){
    var r = bestFitGradient(timer / 1000);
    
    // 绘制渐变
    ctx.fillStyle = r.g;
    ctx.fillRect(0,0,w,h);
    
    // 绘制控制点
    ctx.lineWidth = 3;
    ctx.fillStyle = '#00FF00';
    ctx.strokeStyle = '#FF0000';
    ctx.beginPath();
    ctx.arc(r.x0, r.y0, 5, 0, 2 * Math.PI, false);
    ctx.stroke();
    ctx.fill();
    ctx.beginPath();
    ctx.arc(r.x1, r.y1, 5, 0, 2 * Math.PI, false);
    ctx.stroke();
    ctx.fill();
    
    requestAnimationFrame(update);
}
requestAnimationFrame(update);

问题分析与修正方案

原代码的核心问题在于渐变端点的计算逻辑错误,导致渐变覆盖范围不足。正确思路是:计算画布四个顶点到渐变方向线的垂直投影长度,取最大值作为渐变需要延伸的长度,确保渐变能覆盖整个画布。

修正后的JavaScript代码

var ctx = canvas.getContext("2d");
var w = canvas.width;
var h = canvas.height;

function bestFitGradient(angle) {
    // 计算渐变方向的单位向量
    const dirX = Math.cos(angle);
    const dirY = Math.sin(angle);

    // 画布四个顶点的坐标
    const points = [
        {x: 0, y: 0},
        {x: w, y: 0},
        {x: w, y: h},
        {x: 0, y: h}
    ];

    // 计算每个顶点到渐变中心连线在渐变方向上的投影长度
    let maxProjection = 0;
    const centerX = w / 2;
    const centerY = h / 2;
    for (const p of points) {
        const dx = p.x - centerX;
        const dy = p.y - centerY;
        // 计算投影长度(点积)
        const proj = dx * dirX + dy * dirY;
        maxProjection = Math.max(maxProjection, Math.abs(proj));
    }

    // 计算渐变的起点和终点
    const x0 = centerX + dirX * maxProjection;
    const y0 = centerY + dirY * maxProjection;
    const x1 = centerX - dirX * maxProjection;
    const y1 = centerY - dirY * maxProjection;

    // 创建渐变
    const g = ctx.createLinearGradient(x0, y0, x1, y1);
    g.addColorStop(0, "white");
    g.addColorStop(0.5, "black");
    g.addColorStop(1, "white");

    return {
        g: g,
        x0: x0,
        y0: y0,
        x1: x1,
        y1: y1
    };
}

function update(timer) {
    var r = bestFitGradient(timer / 1000);

    ctx.fillStyle = r.g;
    ctx.fillRect(0, 0, w, h);

    // 绘制控制点
    ctx.lineWidth = 3;
    ctx.fillStyle = '#00FF00';
    ctx.strokeStyle = '#FF0000';
    ctx.beginPath();
    ctx.arc(r.x0, r.y0, 5, 0, 2 * Math.PI, false);
    ctx.stroke();
    ctx.fill();
    ctx.beginPath();
    ctx.arc(r.x1, r.y1, 5, 0, 2 * Math.PI, false);
    ctx.stroke();
    ctx.fill();

    requestAnimationFrame(update);
}
requestAnimationFrame(update);

关键修改说明

  1. 投影长度计算:通过计算画布四个顶点到渐变中心的向量在渐变方向上的投影,取最大绝对值作为渐变需要延伸的长度,确保渐变覆盖所有顶点。
  2. 简化角度处理:无需复杂的象限转换,直接基于原始角度计算方向向量,逻辑更清晰。
  3. 移除冗余颜色节点:删除了用于标记问题的黄色节点,渐变仅保留黑白过渡,确保完全覆盖画布。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 06:15:37