首页 > 解决方案 > CSS线性渐变不准确?

问题描述

对于我的应用程序,我正在寻找可以提供从 0 度到 360 度的任何色调的调色板。我目前正在使用此代码来制作调色板。让我们以色调 120(纯绿色)为例:

function drawPalette(hue) {
  var ctx = document.querySelector("canvas").getContext("2d");

  let w = ctx.canvas.width;
  let h = ctx.canvas.height;

  var grH = ctx.createLinearGradient(0, 0, w, 0);
  grH.addColorStop(0, '#fff');
  grH.addColorStop(1, 'hsl(' + hue + ', 100%, 50%)');

  ctx.fillStyle = grH;
  ctx.fillRect(0, 0, w, h);

  var grV = ctx.createLinearGradient(0, 0, 0, h);
  grV.addColorStop(0, 'rgba(0,0,0,0)');
  grV.addColorStop(1, '#000');

  ctx.fillStyle = grV;
  ctx.fillRect(0, 0, w, h);
}

drawPalette(120);
<canvas></canvas>

我遇到的问题是这似乎不是一个完美的渐变。例如,右上角的颜色#02FF02不是#00FF00(纯绿色)。您可以使用颜色选择器自己查看。

有没有办法制作可用于调色板的完美线性渐变?

标签: javascripthtmlcss

解决方案


不,渐变返回正确的结果。右上角的像素位于渐变的起点和终点之间,并有一个插值来反映这一点。

想象一下具有单个水平或垂直渐变的单个像素画布。渐变的起点是 [0,0],渐变的终点是 [1,0]。我们唯一像素的中间应该有一个介于开始值和结束值之间的值。如果我们的起始值为 255,255,255,结束值为 0,255,0,那么唯一的像素应该是 128,255,128,如下所示:

function drawPalette(hue) {
  var ctx = document.querySelector("canvas").getContext("2d");

  let w = ctx.canvas.width;
  let h = ctx.canvas.height;

  var grH = ctx.createLinearGradient(0, 0, w, 0);
  grH.addColorStop(0, '#fff');
  grH.addColorStop(1, 'hsl(' + hue + ', 100%, 50%)');

  ctx.fillStyle = grH;
  ctx.fillRect(0, 0, w, h);

  
  let data = ctx.getImageData(w-1,0, 1, 1).data;
  console.log(data);

}

drawPalette(120);
<canvas width="1" height="1"></canvas>

如果我们有一个 10 x 10 像素的画布,渐变从 0 延伸到 10,最后一列像素的中间超过 9.5 像素。这意味着,渐变的插值应该是渐变的开始值和结束值之间的 95%。使用与上面相同的梯度,应该是:13、255、13:

function drawPalette(hue) {
  var ctx = document.querySelector("canvas").getContext("2d");

  let w = ctx.canvas.width;
  let h = ctx.canvas.height;

  var grH = ctx.createLinearGradient(0, 0, w, 0);
  grH.addColorStop(0, '#fff');
  grH.addColorStop(1, 'hsl(' + hue + ', 100%, 50%)');

  ctx.fillStyle = grH;
  ctx.fillRect(0, 0, w, h);

  
  let data = ctx.getImageData(w-1,0, 1, 1).data;
  console.log(data);

}

drawPalette(120);
<canvas width="10" height="10"></canvas>

随着画布变大,应用渐变的第一个/最后一个值与渐变开始/结束值之间的差异会减小。如果您将示例中的画布更改为 1000x1000 的大小,则由于四舍五入,右上角的像素将具有 0,255,0 的值:

function drawPalette(hue) {
  var ctx = document.querySelector("canvas").getContext("2d");

  let w = ctx.canvas.width;
  let h = ctx.canvas.height;

  var grH = ctx.createLinearGradient(0, 0, w, 0);
  grH.addColorStop(0, '#fff');
  grH.addColorStop(1, 'hsl(' + hue + ', 100%, 50%)');

  ctx.fillStyle = grH;
  ctx.fillRect(0, 0, w, h);

  var grV = ctx.createLinearGradient(0, 0, 0, h);
  grV.addColorStop(0, 'rgba(0,0,0,0)');
  grV.addColorStop(1, '#000');

  ctx.fillStyle = grV;
  ctx.fillRect(0, 0, w, h);
  
  let data = ctx.getImageData(w-1,0, 1, 1).data;
  console.log(data);
  
}

drawPalette(120);
<canvas width="1000" height="1000"></canvas>

渐变包括起点和终点之间的像素。如果您不希望它们被渐变化,那么您必须修改渐变,使其晚一个像素开始并早一点结束:

function drawPalette(hue) {
  var ctx = document.querySelector("canvas").getContext("2d");

  let w = ctx.canvas.width;
  let h = ctx.canvas.height;
  
  // to ensure we know we're covering the whole thing:
  ctx.fillRect(0,0,w,h)
  //

  var grH = ctx.createLinearGradient(1, 0, w-2, 0);
  grH.addColorStop(0, '#fff');
  grH.addColorStop(1, 'hsl(' + hue + ', 100%, 50%)');

  ctx.fillStyle = grH;
  ctx.fillRect(0, 0, w, h);

  
  let data = ctx.getImageData(w-1,0, 1, 1).data;
  console.log(data);

}

drawPalette(120);
<canvas width="100" height="100"></canvas>


推荐阅读