JSFiddle - React, Tailwind, and code Playground
by tabatkins
HTML
<p>Non-premultiplied: <canvas width=400 height=100></canvas></p>
<p>Premultiplied with one additional stop: <canvas width=400 height=100></canvas></p>
<p>Premultiplied with three additional stops: <canvas width=400 height=100></canvas></p>
CSS
p { margin: 1em 0; }
JavaScript
var canvases = document.querySelectorAll('canvas');
var cw = +canvases[0].width;
var ch = +canvases[0].height;
var postStops = [
{p: 0, c: [255, 0, 0, 1]},
{p: 1,c: [255, 255, 0, 0]}
];
var simplePreStops = [
{p: 0, c: [255, 0, 0, 1]},
{p: .5, c: [255,127,0,.5]},
{p: 1,c: [255, 255, 0, 0]}
];
var preStops = [
{p: 0, c: [255, 0, 0, 1]},
{p: .25, c: [255,64, 0, .75]},
{p: .5, c: [255,127,0,.5]},
{p: .75, c: [255,191, 0, .25]},
{p: 1,c: [255, 255, 0, 0]}
];
drawGradient(canvases[0].getContext('2d'), postStops, postBlend);
drawGradient(canvases[1].getContext('2d'), simplePreStops, preBlend);
drawGradient(canvases[2].getContext('2d'), preStops, preBlend);
function preFromPost(c) {
var a = c[3];
return [c[0] * a, c[1] * a, c[2] * a, a];
}
function postFromPre(c) {
var a = c[3];
if (a == 0) return [0, 0, 0, 0];
return [c[0] / a, c[1] / a, c[2] / a, a];
}
function preBlend(c1, c2, p) {
c1 = preFromPost(c1);
c2 = preFromPost(c2);
var c = c1.map(function(e, i) {
return e * (1 - p) + c2[i] * p;
});
return postFromPre(c);
}
function postBlend(c1, c2, p) {
return c1.map(function(e, i) {
return e * (1 - p) + c2[i] * p;
});
}
function findSurroundingColors(colors, p) {
var last = colors.length - 1;
if (p < colors[0].p) return [colors[0], colors[0]];
if (p > colors[last].p) return [colors[last], colors[last]];
var i = 1;
while (colors[i].p < p)++i;
return [colors[i - 1], colors[i]];
}
function drawStripe(context, x, color) {
color = color.map(function(e, i) {
if (i < 3) return Math.round(e);
return e;
});
context.fillStyle = 'rgba(' + color.join(',') + ')';
context.fillRect(x, 0, 1, ch);
}
function drawGradient(context, colorStops, blender) {
for (var i = 0; i < cw; i++) {
var p = i / cw;
var stops = findSurroundingColors(colorStops, p);
if (stops[0].p == stops[1].p) {
var...