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...