Home > Enterprise >  HTML5 Canvas rotate gradient around centre with best fit
HTML5 Canvas rotate gradient around centre with best fit

Time:01-09

I want to make a gradient that covers the whole canvas whatever the angle of it.

So I used a method found on a Stack Overflow post which is finally incorrect. The solution is almost right but, in fact, the canvas is not totally covered by the gradient.

It is this answer: enter image description here

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; // get the diagonal length    
    var diagAngle = Math.asin((h / 2) / dist); // get the diagonal angle

    // Do the symmetry on the angle (move to first quad
    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)) }
    // get angles from center to edges for along and right of gradient
    var ang1 = Math.PI/2 - diagAngle - Math.abs(a1);
    var ang2 = Math.abs(diagAngle - Math.abs(a1));
    
    // get distance from center to horizontal and vertical edges
    var dist1 = Math.cos(ang1) * h;
    var dist2 = Math.cos(ang2) * w;
    
    // get the max distance
    var scale = Math.max(dist2, dist1) / 2;
    
    // get the vector to the start and end of gradient
    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;
    
    // create the gradient
    const g = ctx.createLinearGradient(x0, y0, x1, y1);
    
    // add colours
    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);
    
    // draw gradient
    ctx.fillStyle = r.g;
    ctx.fillRect(0,0,w,h);
    
    // draw points
    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);
canvas {
  border : 2px solid red;
}
<canvas id="canvas" width="300" height="200"></canvas>

CodePudding user response:

In this fiddle there is a function that calculates the distance between a rotated line and a point:

function distanceToPoint(px, py, angle) {
  const cx = width / 2;
  const cy = height / 2;
  return Math.abs((Math.cos(angle) * (px - cx)) - (Math.sin(angle) * (py - cy)));
}

Which is then used to find the maximum distance between the line and the corner points (only two points are considered, because the distances to the other two points are mirrored):

const dist = Math.max(
  distanceToPoint(0, 0, angle),
  distanceToPoint(0, height, angle)
);

Which can be used to calculate offset points for the end of the gradient:

const ox = Math.cos(angle) * dist;
const oy = Math.sin(angle) * dist;
const gradient = context.createLinearGradient(
  width / 2   ox,
  height / 2   oy,
  width / 2 - ox,
  height / 2 - oy
)
  • Related