I have this svg:
I would like to have a HTML page where the top part is the SVG and the bottom part is a div of dynamic height that continues the gradient
Like this:
<svg>...</svg>
<div id="gradient-box" >Gradient box of dynamic height</div>
I know that one can create gradients with CSS with color, opacity and degree
Compute the slope and the angle of the gradient. Note that in the coordinate system 0deg points to the right, while 0deg in CSS points upwards. Both angle values run clockwise.
const slope = (p2.y - p1.y) / (p2.x - p1.x) const angle = (Math.atan2(p2.y - p1.y, p2.x - p1.x) / Math.PI * 180 270) % 360;
Compute the positions of
0%
and100%
as defined in CSS as coordinates. As seen in the diagram, these points are selected in such a way that the opposing corners get a color value that exactly matches those at these two values. The line connecting the two points goes through the center of the box. I'll leave out the trigonometry involved and just give the results for the pointsq1
andq2
.if (angle == 0) { q1.x = width / 2 q1.y = height q2.x = width / 2 q2.y = 0 } else if (angle == 180) { q1.x = width / 2 q1.y = 0 q2.x = width / 2 q2.y = height } else { q1.x = (width * slope - height) / (slope 1/slope) / 2 q1.y = -(width - height / slope) / (slope 1/slope) / 2 q2.x = width - (width * slope - height) / (slope 1/slope) / 2 q2.y = height (width - height / slope) / (slope 1/slope) / 2 }
Compute the two points
pp1
andpp2
on the line that are nearest top1
andp2
. This is a bit more of math, instead of spelling it out, refer to other questions like this one.The percentage values belonging to
pp1
andpp2
can be found as a proportion of the distance between the two CSS end points.const f1 = (pp1.x - q1.x) / (q2.x - q1.x) || (pp1.y - q1.y) / (q2.y - q1.y); const f2 = (pp2.x - q1.x) / (q2.x - q1.x) || (pp2.y - q1.y) / (q2.y - q1.y);
Compute the relative position of each color stop between
f1
andf2
.const stops = [...gradient.querySelectorAll('stop')].map(stop => { const color = stop.getAttribute('stop-color'); const opacity = stop.getAttribute('stop-opacity'); const offset = parseFloat(stop.getAttribute('offset')); const fraction = offset * (f2 - f1) f1 return {color, opacity, fraction}; });
Now, you can finally express the CSS function. The last hurdle to overcome is to convert the RGB color
string plus the opacity
value to rgba()
notation. You can find apropriate libraries for that. I am going to assume color-string:
const stopStrs = stops.map(({color, opacity, fraction}) => {
const rgbValue = colorString.get(color);
const opacityValue = parseFloat(opacity);
const rgba = colorString.to.rgb(rgbValue, opacityValue);
return `${fraction * 100}% ${rgba}`
};
const gradientFunction = `linearGradient(${angle}deg, ${stopStrs.join(', ')})`;