Rotating a Gradient.
Simple rotation.
If you are not worried about the gradient fitting the canvas then a simple rotation will solve the problem.
First you must work out the max length of the gradient so that when it is diagonal it will still fit the canvas.
const maxLength = Math.sqrt(canvas.width * canvas.width + canvas.height * canvas.height);
Then you can create the gradient as follows
var angle = ?; // The angle in radians
// A value of 0 if a gradient from right to left
const gradient = ctx.createLinearGradient(
// the start of the gradient added to the center
canvas.width / 2 + Math.cos(angle) * maxLength * 0.5,
canvas.height / 2 + Math.sin(angle) * maxLength * 0.5,
// the end of the gradient subtracted from the center
canvas.width / 2 - Math.cos(angle) * maxLength * 0.5,
canvas.height / 2 - Math.sin(angle) * maxLength * 0.5
)
This works but will clip the gradient when it is not along the diagonal line.
Fit diagonal example
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = Math.sqrt(w * w + h * h) / 2;
// empty colour items are skipped when creating the gradient
const gradientColours = ["white","blue",,,,"green","yellow","green",,,,"cyan","black"];
function createRotatedGradient(angle, colors){
const g = ctx.createLinearGradient(
w / 2 + Math.cos(angle) * maxWidth, // start pos
h / 2 + Math.sin(angle) * maxWidth,
w / 2 - Math.cos(angle) * maxWidth, // end pos
h / 2 - Math.sin(angle) * maxWidth
);
// add colours
eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
return g;
}
function update(timer){
ctx.fillStyle = createRotatedGradient(timer / 1000,gradientColours);
ctx.fillRect(0,0,w,h);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas><
This can be modified to fit either the width or height by setting the maxWidth
of the gradient to either the canvas.height
or canvas.width
Fit Height example
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = h / 2;
// empty colour items are skipped when creating the gradient
const gradientColours = ["white","green",,,,"blue","cyan","blue",,,,"yellow","black"];
function createRotatedGradient(angle, colors){
const g = ctx.createLinearGradient(
w / 2 + Math.cos(angle) * maxWidth, // start pos
h / 2 + Math.sin(angle) * maxWidth,
w / 2 - Math.cos(angle) * maxWidth, // end pos
h / 2 - Math.sin(angle) * maxWidth
);
// add colours
eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
return g;
}
function update(timer){
ctx.fillStyle = createRotatedGradient(timer / 1000,gradientColours);
ctx.fillRect(0,0,w,h);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas><
Fit Width example
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = w / 2;
// empty colour items are skipped when creating the gradient
const gradientColours = ["white","blue",,,,"yellow","green","yellow",,,,"cyan","black"];
function createRotatedGradient(angle, colors){
const g = ctx.createLinearGradient(
w / 2 + Math.cos(angle) * maxWidth, // start pos
h / 2 + Math.sin(angle) * maxWidth,
w / 2 - Math.cos(angle) * maxWidth, // end pos
h / 2 - Math.sin(angle) * maxWidth
);
// add colours
eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
return g;
}
function update(timer){
ctx.fillStyle = createRotatedGradient(timer / 1000,gradientColours);
ctx.fillRect(0,0,w,h);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas><
Simple dynamic fit
To fit both the width and height you can use a very simple scaling of the y axis. This scale is the ratio of the width and height.
const maxLen = canvas.width;
const aspect = canvas.height / canvas.width;
const angle = ?
const gradient = ctx.createLinearGradient(
// the start of the gradient added to the center
canvas.width / 2 + Math.cos(angle) * maxLen * 0.5,
canvas.height / 2 + Math.sin(angle) * maxLen * 0.5 * aspect,
// the end of the gradient subtracted from the center
canvas.width / 2 - Math.cos(angle) * maxLen * 0.5,
canvas.height / 2 - Math.sin(angle) * maxLen * 0.5 * aspect
)
Example of aspect scaling
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = w / 2;
const aspect = h / w;
// empty colour items are skipped when creating the gradient
const gradientColours = ["white","red",,,,"yellow","green","yellow",,,,"red","black"];
function createRotatedGradient(angle, colors){
const g = ctx.createLinearGradient(
w / 2 + Math.cos(angle) * maxWidth, // start pos
h / 2 + Math.sin(angle) * maxWidth * aspect,
w / 2 - Math.cos(angle) * maxWidth, // end pos
h / 2 - Math.sin(angle) * maxWidth * aspect
);
// add colours
eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
return g;
}
function update(timer){
ctx.fillStyle = createRotatedGradient(timer / 1000,gradientColours);
ctx.fillRect(0,0,w,h);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas><
Best fit.
The example site you provided fitted the gradient so that it was from the closest edges, this is a better fit but still is not a perfect fit as the gradient will be too short at times. As you have the method from that site I will not include it here.
The best fit is a little more complex but will always fit the canvas so that there is no over or under flow of the canvas ( no pixels will be set from outside the gradient and all of the gradient will be visible at any angle)
See the example for information. The maths was taken and adapted from this answer that fits a rotated image.
Example of best fit.
const eachOf=(a,cb)=>{var i=0;const len=a.length;while(i<len)cb(a[i], i++);};
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const maxWidth = w / 2;
const aspect = h / w;
// empty colour items are skipped when creating the gradient
const gradientColours = ["black","white",,,,"white","red",,,,,,,,,"yellow","green","yellow",,,,,,,,,"red","black",,,,"black","white"];
function bestFitGradient(angle, colors){
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;
// create the gradient
const g = ctx.createLinearGradient(
w / 2 + dx, // start pos
h / 2 + dy,
w / 2 - dx, // end pos
h / 2 - dy
);
// add colours
eachOf(colors,(col,i)=> col && g.addColorStop(i / (colors.length - 1), col) );
return g;
}
function update(timer){
ctx.fillStyle = bestFitGradient(timer / 1000,gradientColours);
ctx.fillRect(0,0,w,h);
requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { border : 2px solid black; }
<canvas id="canvas"></canvas><