CSS Linear gradient is inaccurate?

倖福魔咒の 提交于 2021-02-09 09:21:50

问题


For my application I'm looking to make a color palette that can serve any color hue from 0 degrees to 360 degrees. I'm currently using this code to make the palette. Let's use hue 120 (pure green) as an example:

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>

The issue I'm having is that this doesn't seem to be a perfect gradient. For example, the color at the top right is #02FF02 instead of #00FF00 (pure green). You can use a color picker to see this for yourself.

Is there any way to make a perfect linear gradient that is usable for a color palette?


回答1:


No, the gradient is returning the correct result. The top right pixel is between the start and end points of the gradient, and has an interpolated value to reflect that.

Imagine a single pixel canvas with a single horizontal or vertical gradient. The gradient's start point is [0,0] and the gradient's end is [1,0]. The middle of our sole pixel should have a value half way between the start and end values. If our start value is 255,255,255 and the end value is 0,255,0, then the sole pixel should be 128,255,128, as seen below:

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>

If we have a ten pixel by ten pixel canvas, the gradient stretches from 0 to 10, with the middle of the last column of pixels being 9.5 pixels over. Which means, the gradient's interpolated value should be 95% of the way between the start and end values of the gradient. Using the same gradient as above, that should be: 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>

As the canvas gets bigger, the difference between the first/last values with the applied gradient and the gradient start/end values decreases. If you change the canvas in your example to have a size of 1000x1000, the top right pixel will have a value of 0,255,0 due to rounding:

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>

The gradient includes the pixels between the start and end points. If you don't want them to be gradient-ized, then you'll have to modify the gradient so that it starts one pixel later and ends one pixel sooner:

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>


来源:https://stackoverflow.com/questions/65639332/css-linear-gradient-is-inaccurate

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!