javascript - 绘制饱和度/亮度渐变

标签 javascript canvas linear-gradients color-picker pixel-manipulation

我正在尝试在 Canvas 中绘制以下渐变图像,但右下角出现问题。

期望的效果:

enter image description here

当前输出:

enter image description here

我可能在这里遗漏了一些非常简单的东西。

function color(r, g, b) {
  var args = Array.prototype.slice.call(arguments);
  if (args.length == 1) {
    args.push(args[0]);
    args.push(args[0]);
  } else if (args.length != 3 && args.length != 4) {
    return;
  }
  return "rgb(" + args.join() + ")";
}

function drawPixel(x, y, fill) {
  var fill = fill || "black";
  context.beginPath();
  context.rect(x, y, 1, 1);
  context.fillStyle = fill;
  context.fill();
  context.closePath();
}

var canvas = document.getElementById("primary");
var context = canvas.getContext("2d");

canvas.width = 256;
canvas.height = 256;

for (var x = 0; x < canvas.width; x++) {
  for (var y = 0; y < canvas.height; y++) {
    var r = 255 - y;
    var g = 255 - x - y;
    var b = 255 - x - y;
    drawPixel(x, y, color(r, g, b));
  }
}
#primary {
    display: block;
    border: 1px solid gray;
}
<canvas id="primary"></canvas>

JSFiddle

最佳答案

使用渐变。

您可以让 GPU 为您完成大部分处理。二维合成操作 multiply 有效地将每个像素的两种颜色相乘。因此对于每个 channel 和每个像素 colChanDest = Math.floor(colChanDest * (colChanSrc/255)) 是通过 GPU 的大规模并行处理能力完成的,而不是在单个线程上运行的低共享线程核心(JavaScript 执行上下文)。

两个渐变

  1. 一个是背景从上到下白到黑

    var gradB = ctx.createLinearGradient(0,0,0,255); gradB.addColorStop(0,"白色"); gradB.addColorStop(1,"black");

  2. 另一个是从左到右由透明渐变到不透明的Hue

    var swatchHue var col = "rgba(0,0,0,0)" var gradC = ctx.createLinearGradient(0,0,255,0); gradC.addColorStop(0,``hsla(${hueValue},100%,50%,0)``); gradC.addColorStop(1,``hsla(${hueValue},100%,50%,1)``);

注意上面的字符串引用在 SO 上没有正确呈现所以我只是将它们加倍显示,使用单引号作为演示片段中的做法。

渲染

然后将两者分层,首先是背景(灰度),然后是复合操作“相乘”

ctx.fillStyle = gradB;
ctx.fillRect(0,0,255,255);
ctx.fillStyle = gradC;
ctx.globalCompositeOperation = "multiply";
ctx.fillRect(0,0,255,255);
ctx.globalCompositeOperation = "source-over";

仅适用于 Hue

重要的是颜色(色调)是纯色值,不能使用随机的rgb值。如果您有选定的 rgb 值,则需要从 rgb 中提取色调值。

以下函数会将 RGB 值转换为 HSL 颜色

function rgbToLSH(red, green, blue, result = {}){
    value hue, sat, lum, min, max, dif, r, g, b;
    r = red/255;
    g = green/255;
    b = blue/255;
    min = Math.min(r,g,b);
    max = Math.max(r,g,b);
    lum = (min+max)/2;
    if(min === max){
        hue = 0;
        sat = 0;
    }else{
        dif = max - min;
        sat = lum > 0.5 ? dif / (2 - max - min) : dif / (max + min);
        switch (max) {
        case r:
            hue = (g - b) / dif;
            break;
        case g:
            hue = 2 + ((b - r) / dif);
            break;
        case b:
            hue = 4 + ((r - g) / dif);
            break;
        }
        hue *= 60;
        if (hue < 0) {
            hue += 360;
        }        
    }
    result.lum = lum * 255;
    result.sat = sat * 255;
    result.hue = hue;
    return result;
}

把它们放在一起

该示例每 3 秒渲染一个随机红、绿、蓝值的样本。

请注意,此示例使用 Balel,因此它可以在 IE 上运行

var canvas = document.createElement("canvas");
canvas.width = canvas.height = 255;
var ctx = canvas.getContext("2d");
document.body.appendChild(canvas);

function drawSwatch(r, g, b) {
  var col = rgbToLSH(r, g, b);
  var gradB = ctx.createLinearGradient(0, 0, 0, 255);
  gradB.addColorStop(0, "white");
  gradB.addColorStop(1, "black");
  var gradC = ctx.createLinearGradient(0, 0, 255, 0);
  gradC.addColorStop(0, `hsla(${Math.floor(col.hue)},100%,50%,0)`);
  gradC.addColorStop(1, `hsla(${Math.floor(col.hue)},100%,50%,1)`);

  ctx.fillStyle = gradB;
  ctx.fillRect(0, 0, 255, 255);
  ctx.fillStyle = gradC;
  ctx.globalCompositeOperation = "multiply";
  ctx.fillRect(0, 0, 255, 255);
  ctx.globalCompositeOperation = "source-over";
}

function rgbToLSH(red, green, blue, result = {}) {
  var hue, sat, lum, min, max, dif, r, g, b;
  r = red / 255;
  g = green / 255;
  b = blue / 255;
  min = Math.min(r, g, b);
  max = Math.max(r, g, b);
  lum = (min + max) / 2;
  if (min === max) {
    hue = 0;
    sat = 0;
  } else {
    dif = max - min;
    sat = lum > 0.5 ? dif / (2 - max - min) : dif / (max + min);
    switch (max) {
      case r:
        hue = (g - b) / dif;
        break;
      case g:
        hue = 2 + ((b - r) / dif);
        break;
      case b:
        hue = 4 + ((r - g) / dif);
        break;
    }
    hue *= 60;
    if (hue < 0) {
      hue += 360;
    }
  }
  result.lum = lum * 255;
  result.sat = sat * 255;
  result.hue = hue;
  return result;
}

function drawRandomSwatch() {
  drawSwatch(Math.random() * 255, Math.random() * 255, Math.random() * 255);
  setTimeout(drawRandomSwatch, 3000);
}
drawRandomSwatch();

要从 x 和 y 坐标计算颜色,您需要计算色调,然后是饱和度和值以获得 hsv 颜色(注意 hsl 和 hsv 是不同的颜色模型)

// saturation and value are clamped to prevent rounding errors creating wrong colour
var rgbArray = hsv_to_rgb(
    hue, // as used to create the swatch
    Math.max(0, Math.min(1, x / 255)),   
    Math.max(0, Math.min(1, 1 - y / 255))
);

获取 h、s、v 颜色的 r、g、b 值的函数。

/* Function taken from datGUI.js 
   Web site https://workshop.chromeexperiments.com/examples/gui/#1--Basic-Usage
   // h 0-360, s 0-1, and v 0-1
*/

function hsv_to_rgb(h, s, v) {
  var hi = Math.floor(h / 60) % 6;
  var f = h / 60 - Math.floor(h / 60);
  var p = v * (1.0 - s);
  var q = v * (1.0 - f * s);
  var t = v * (1.0 - (1.0 - f) * s);
  var c = [
    [v, t, p],
    [q, v, p],
    [p, v, t],
    [p, q, v],
    [t, p, v],
    [v, p, q]
  ][hi];
  return {
    r: c[0] * 255,
    g: c[1] * 255,
    b: c[2] * 255
  };
}

关于javascript - 绘制饱和度/亮度渐变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41524641/

相关文章:

javascript - 如何在圆形 Canvas 上添加线条

css - 线性梯度上的 Autoprefixer 编译错误

javascript - 跨源请求仅支持 react 中的协议(protocol)方案错误

javascript - 使用 NgTable 过滤选择

javascript - 火力地堡 JS : Database call to iterate over nested data

jquery - 使用 className 来定义 Canvas 对象而不是 ID..?

javascript - Navigator.share() 第二次不起作用

javascript - 将一个旋转对象包含在另一个旋转对象中 FabricJS

ios - 如何创建渐变

css - 将渐变转换为线性渐变语法