How is a CSS gradient path calculated?

2.4k views Asked by At

When using a linear gradient such as

div {
  width: 300px;
  height: 50px;
  background: linear-gradient(to right, blue, red);
}
<div></div>

the colors could be changed via different paths.

In the example above, it could be done by just modifying linearly the R and B channel, without touching to the G one -- but the variation could also be non-linear (to provide, say, a sense of linearity because it would be more physiological), or by tinkering with the G channel (again because it might seem to be a more realistic 'red to blue transition' to our eyes).

What is the formula used in linear-gradient to switch between two colors?

1

There are 1 answers

0
mherzig On BEST ANSWER

Gradients in HTML/CSS are linear interpolations, purely mathematical. Per the W3C canvas spec:

Once a gradient has been created (see below), stops are placed along it to define how the colors are distributed along the gradient. The color of the gradient at each stop is the color specified for that stop. Between each such stop, the colors and the alpha component must be linearly interpolated over the RGBA space without premultiplying the alpha value to find the color to use at that offset. Before the first stop, the color must be the color of the first stop. After the last stop, the color must be the color of the last stop. When there are no stops, the gradient is transparent black.

SVGs work the same way.

CSS gradients are the same, except for one difference (emphasis mine):

Between two color stops, the line’s color is interpolated between the colors of the two color stops, with the interpolation taking place in premultiplied RGBA space.

So all three use linear interpolation, and canvas/SVG use non-premultiplied alpha while CSS uses premultiplied alpha (which does look nicer). As to why that makes a difference, see this example:

html, body, svg, div {
  display: block;
  width: 100%;
  height: 100%;
  margin: 0; 
  padding: 0;
  background: white;
}
svg {height: 60%;}
div.gradient {
  height: 20%;
  margin-top: 0.2%;
  background: linear-gradient(to right,
  rgba(0%, 100%, 0%, 1),
  rgba(0,0,0,0));
}
<svg>
  <linearGradient id="a">
    <stop offset="0" stop-color="lime"
      stop-opacity="1" />
    <stop offset="1" stop-color="lime"
      stop-opacity="0" />
  </linearGradient>
  <linearGradient id="b">
    <stop offset="0" stop-color="lime"
      stop-opacity="1" />
    <stop offset="1" stop-color="black"
      stop-opacity="0" />
  </linearGradient>
  <linearGradient id="c">
    <stop offset="0" stop-color="rgba(0%, 100%, 0%, 1)" />
    <stop offset="1" stop-color="rgba(0,0,0,0)" />
  </linearGradient>
  <rect width="100%" height="33%"
    fill="url(#a)" />
  <rect width="100%" height="33%" y="33.5%"
    fill="url(#b)" />
  <rect width="100%" height="33%" y="67%"
    fill="url(#c)" />
</svg>
<div class="gradient"></div>
<ul>
  <li>Top: SVG gradient with constant stop-color and transitioned stop-opacity;</li>
  <li>2nd: SVG gradient with stop-color transitioning to black and stop-opacity transitioning to zero;</li>
  <li>3rd: SVG gradient with rgba colors;</li>
  <li>Bottom: CSS gradient with the same rgba colors.</li>
</ul>
<p>All transition from opaque lime to fully transparent; in all but the first SVG gradient, the final stop is transparent black.  The CSS gradient scales the intensity of the color by the alpha value before transitioning, so you don't get the fade to gray effect.</p>

Disclaimer: That's not my snippet! I took it from this CodePen example, but SO won't let me link to it without adding code myself.