background
In some cases you may need to use canvas for linear gradients, where the gradient fills the canvas exactly, and the gradient is not horizontal or vertical, but at any Angle. For example, cSS3 implementation:
background: linear-gradient(60deg, red, blue);
Copy the code
To do this with canvas, first create a gradient object
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
context.createLinearGradient(x0, y0, x1, y1);
Copy the code
X0, y0 are the start of the gradient, and x1, y1 are the end of the gradient.
For details, see API.
So the question is, how do you determine the coordinate parameters x0, y0, x1, y1?
implementation
Before determining the coordinate parameters, briefly understand the implementation principle of CSS, directly put the figure:
By default, the gradient transitions smoothly from one color to another, meaning that the gradient line passes through the element’s midpoint, as mentioned aboveThe gradient just fills the canvasIf the coordinate system is established with the midpoint of the element as the origin, the following figure can be obtained:
The rectangle is the element, M is the gradient line, from the bottom left to the top right, Q and W are the two diagonal points crossing the rectangle respectively and the two lines perpendicular to M, the range of color gradient is between these two lines, (x0 ‘,y0 ‘), (x1 ‘,y1 ‘) are the coordinates of the two vertical points respectively, let’s call them I point and J point, and we require (x0,y0), (x1, y1) is the coordinate system is transformed into canvas (upper left corner of the origin, down for the Y axis is the direction) after the I and J, the coordinates of the rectangle’s width height and gradient line Angle value, according to the simple geometric knowledge can work out the coordinates of two points, but it is important to note that the gradient line and rectangular intersection may fall on the horizon, It may also fall on a vertical line, so it needs to be divided into two pieces of logic for calculation, and the code is as follows:
/ * * *@description: Calculates the canvas gradient starting coordinate *@param {number} canvas width
* @param {number} canvas height
* @param {number} Angle Angle *@return {*}* /
function calculateGradientCoordinate(
width,
height,
angle = 180.) {
if (angle >= 360) angle = angle - 360;
if (angle < 0) angle = angle + 360;
angle = Math.round(angle);
// Two results when the gradient axis is perpendicular to the horizontal side of the rectangle
if (angle === 0) {
return {
x0: Math.round(width / 2),
y0: height,
x1: Math.round(width / 2),
y1: 0}; }if (angle === 180) {
return {
x0: Math.round(width / 2),
y0: 0.x1: Math.round(width / 2),
y1: height,
};
}
// Two results when the gradient axis is perpendicular to the vertical edge of the rectangle
if (angle === 90) {
return {
x0: 0.y0: Math.round(height / 2),
x1: width,
y1: Math.round(height / 2),}; }if (angle === 270) {
return {
x0: width,
y0: Math.round(height / 2),
x1: 0.y1: Math.round(height / 2),}; }// The diagonal Angle from the lower left corner of the rectangle to the upper right corner
const alpha = Math.round(
(Math.asin(width / Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2))) *
180) /
Math.PI,
);
// Four results when the gradient axes coincide on the two diagonals of the rectangle
if (angle === alpha) {
return {
x0: 0.y0: height,
x1: width,
y1: 0}; }if (angle === 180 - alpha) {
return {
x0: 0.y0: 0.x1: width,
y1: height,
};
}
if (angle === 180 + alpha) {
return {
x0: width,
y0: 0.x1: 0.y1: height,
};
}
if (angle === 360 - alpha) {
return {
x0: width,
y0: height,
x1: 0.y1: 0}; }// Set up a cartesian coordinate system with the center point of the rectangle as the origin of the coordinates, with the positive Y-axis up and the positive X-axis to the right
let x0 = 0,
y0 = 0,
x1 = 0,
y1 = 0;
// When the intersection of the gradient axis and rectangle falls on a horizontal line
if (
angle < alpha || // Is in the first quadrant
(angle > 180 - alpha && angle < 180) | |// Is in the second quadrant
(angle > 180 && angle < 180 + alpha) || // Is in the third quadrant
angle > 360 - alpha // Is in the fourth quadrant
) {
// Multiply the Angle by (PI/180) to convert to radians
const radian = (angle * Math.PI) / 180;
// when in the first or fourth quadrant, y is height / 2, otherwise y is -height / 2
const y = angle < alpha || angle > 360 - alpha ? height / 2 : -height / 2;
const x = Math.tan(radian) * y;
// When in the first or second quadrant, l is width / 2-x, otherwise l is -width / 2-x
const l =
angle < alpha || (angle > 180 - alpha && angle < 180)? width /2 - x
: -width / 2 - x;
const n = Math.pow(Math.sin(radian), 2) * l;
x1 = x + n;
y1 = y + n / Math.tan(radian);
x0 = -x1;
y0 = -y1;
}
// When the intersection of the gradient axis and rectangle falls on the vertical line
if (
(angle > alpha && angle < 90) | |// Is in the first quadrant
(angle > 90 && angle < 90 + alpha) || // Is in the second quadrant
(angle > 180 + alpha && angle < 270) | |// Is in the third quadrant
(angle > 270 && angle < 360 - alpha) // Is in the fourth quadrant
) {
// Multiply the Angle by (PI/180) to convert to radians
const radian = ((90 - angle) * Math.PI) / 180;
// When in the first or second quadrant, x is width / 2, otherwise x is -width / 2
const x =
(angle > alpha && angle < 90) || (angle > 90 && angle < 90 + alpha)
? width / 2
: -width / 2;
const y = Math.tan(radian) * x;
// When in the first or fourth quadrant l is height / 2-y, otherwise l is -height / 2-y
const l =
(angle > alpha && angle < 90) || (angle > 270 && angle < 360 - alpha)
? height / 2 - y
: -height / 2 - y;
const n = Math.pow(Math.sin(radian), 2) * l;
x1 = x + n / Math.tan(radian);
y1 = y + n;
x0 = -x1;
y0 = -y1;
}
// Change the coordinate system to canvas standard with positive direction down the Y axis
x0 = Math.round(x0 + width / 2);
y0 = Math.round(height / 2 - y0);
x1 = Math.round(x1 + width / 2);
y1 = Math.round(height / 2 - y1);
return { x0, y0, x1, y1 };
}
Copy the code
validation
The Angle of 270 deg
CSS implementation:
width: 500px;
height: 500px;
background: linear-gradient(270deg, red, blue);
Copy the code
Canvas implementation:
var canvas = document.getElementById('d')
var ctx = canvas.getContext('2d')
var { x0, y0, x1, y1 } = calculateGradientCoordinate(500.500.270)
var gi = ctx.createLinearGradient(x0, y0, x1, y1)
gi.addColorStop(0.'red')
gi.addColorStop(1.'blue')
ctx.fillStyle = gi
ctx.fillRect(0.0.500.500)
Copy the code
Effect:
The Angle of 100 deg
CSS implementation:
background: linear-gradient(100deg, red, blue);
Copy the code
Canvas implementation:
var { x0, y0, x1, y1 } = calculateGradientCoordinate(500.500.100)
Copy the code
Effect:
Perspective – 76 deg
CSS implementation:
background: linear-gradient(-76deg, red, blue);
Copy the code
Canvas implementation:
var { x0, y0, x1, y1 } = calculateGradientCoordinate(500.500, -76)
Copy the code
Effect:
And you can see that they’re doing the same thing.