I am trying to create a curve with a gradient that follows it tangentially. Since its not possible to do that by default, i’ve taken to splitting the curve into a series of quadrilaterals and applying a gradient to each one. However, it seems that the gradient is not going in the correct direction. I’ve made the quadrilaterals larger to make it easier to see here. The black lines represent the x0, y0
and x1, y1
inputs to ctx.createLinearGradient()
for each shape.
So i calculate the x
and y
for points 1, 2, 3, 4
(which go in clockwise direction around the shape starting from the top left), and then calculate the gradient direction line from the middle of the top of the shape to the middle of the bottom.
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
ctx.lineTo(x3, y3);
ctx.lineTo(x4, y4);
const grd_x1 = (x1 + x2) / 2;
const grd_y1 = (y1 + y2) / 2;
const grd_x2 = (x3 + x4) / 2;
const grd_y2 = (y3 + y4) / 2;
const gradient = ctx.createLinearGradient(
grd_x1,
grd_y1,
grd_x2,
grd_y2
);
gradient.addColorStop(0, "#3acbfcc3");
gradient.addColorStop(1, "#ffffff00");
ctx.fillStyle = gradient;
ctx.fill();
Hence i would expect the gradient would follow the direction of the black lines like this. However, in the shapes it is clear that the gradient is not going in that direction like this. I expect that the top two corner have the same colour (blue) and the bottom two corners have the same colour (white). Am i interpreting the inputs to the function wrong? The diagram at the top of this doc makes me think it should work.
2
Answers
This doesn’t work with gradients…
So replace it with this…
Or specify some other region… but it’s gotta be a region.
NB: Change "C" with your canvas identifier.
This test is for somethinghere…
JS:
HTML:
Which one works?
However paradoxical it may seem at first glance, but in your case gradients shouldn’t be rendered the way how you intuitively think you want them to be oriented.
As can be seen from the article you already linked to:
Gradients are rendered parallel to the specified vector. Which means that the color is constant along any perpendicular to that vector.
Yet your vectors are not orthogonal to the frontal segments of the wave, so you end up with gradients which are:
In order to align gradients properly, you have to make them orthogonal to their corresponding frontal segments, and in the same time ensure that colors propagate uniformly along common desired gradient vectors.
How both requirements are met, can be visualized using a parallelogram created from the frontal segment and desired gradient vector attached to it (which is how you are already constructing your quadrilaterals). Now you only have to connect the line of the frontal side with the line of the opposite side, by drawing a perpendicular.
This perpendicular is the solution you are looking for.
The starting point can lie anywhere on the frontal segment, since by definition (and by design) it will be orthogonal to the gradient. The ending point can be found by solving linear algebra equations.
I wanted to practice anyway, so here’s the demo!
Also note that iteratively filling paths with
CanvasRenderingContext2D
always leaves gaps between adjacent shapes, which otherwise are expected to be rendered seamlessly. This is an unfortunate "feature" of antialiased rendering when performed in several steps on top of each other rather than drawing everything at once (but canvas is leaving us with no choice anyway). I tried to minimize these artifacts by slightly overlapping quadrilaterals, so I hope they are not so noticeable and annoying now.Of course, there’s WebGL at everyone’s service, which allows to make quality and performant shaders, provided that one is ready to take the time to learn it.