График HSL Color на градиенте с использованием Javascript - PullRequest
0 голосов
/ 16 июня 2019

Я пытаюсь использовать шестнадцатеричный цвет в качестве входных данных для извлечения информации о HSL, чтобы я мог рассчитать положение поверх спектра градиента для размещения цветовой точки.

Хороший пример этого можно найти в фотошопе при выборе палитры цветов или при наборе цвета. Другой пример можно увидеть здесь : просто введите цвет, и цветная точка переместится в положение поверх градиента.

Вот мой код:

function rgbToHsl(r, g, b) {
    r /= 255;
    g /= 255;
    b /= 255;
    var max = Math.max(r, g, b);
    var min = Math.min(r, g, b);
    var d = max - min;
    var h;
    if (d === 0) h = 0;
    else if (max === r) h = (g - b) / d % 6;
    else if (max === g) h = (b - r) / d + 2;
    else if (max === b) h = (r - g) / d + 4;
    var l = (min + max) / 2;
    var s = d === 0 ? 0 : d / (1 - Math.abs(2 * l - 1));
    return [h * 60, s, l];
}

function hslToRgb(h, s, l) {
    var c = (1 - Math.abs(2 * l - 1)) * s;
    var hp = h / 60.0;
    var x = c * (1 - Math.abs((hp % 2) - 1));
    var rgb1;
    if (isNaN(h)) rgb1 = [0, 0, 0];
    else if (hp <= 1) rgb1 = [c, x, 0];
    else if (hp <= 2) rgb1 = [x, c, 0];
    else if (hp <= 3) rgb1 = [0, c, x];
    else if (hp <= 4) rgb1 = [0, x, c];
    else if (hp <= 5) rgb1 = [x, 0, c];
    else if (hp <= 6) rgb1 = [c, 0, x];
    var m = l - c * 0.5;
    return [
        Math.round(255 * (rgb1[0] + m)),
        Math.round(255 * (rgb1[1] + m)),
        Math.round(255 * (rgb1[2] + m))
    ];
}

function hexToRgb(hex) {
    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
    var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
    hex = hex.replace(shorthandRegex, function(m, r, g, b){
        return r + r + g + g + b + b;
    });

    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ?
    [
        parseInt(result[1], 16),
        parseInt(result[2], 16),
        parseInt(result[3], 16)

    ] : null;
}

function rgbToHex(r, g, b) {
    return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}

function formatAsRGB(rgb) {
	return 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
}

function getDotCoordinates(lightness, saturation, canvasWidth, canvasHeight) {
    var x = 0;
    var y = 0;

    // lighter (going across x-axis)
    if (lightness >= .5 && lightness <= 1) {
        x = 0;
        y = 0;

        // darker (going down y-axis) 
        // on color scale, moving from top to bottom
        // lightness between .5 - 1
    } else if (lightness >= 0 && lightness <= .5) {
        x = (1 - saturation) * canvasWidth;
        y = (1 - lightness) * canvasHeight;
    }

    if (x >= canvasWidth) {
        x = canvasWidth - 1;
    }

    if (y >= canvasHeight) {
        y = canvasHeight - 1;
    }

    return [x, y];
}


var inputColor = '#969141';
var rgbColor = hexToRgb(inputColor);
var hslColor = rgbToHsl(rgbColor[0],rgbColor[1],rgbColor[2]);
var hexColor = rgbToHex(rgbColor[0],rgbColor[1],rgbColor[2]);
var hueColor = hslToRgb(hslColor[0], 1, .5);

var spectrum = $('canvas').get(0);
var ctx = spectrum.getContext('2d');
var colorDot = $('.colorDot');

var colorGradient;
var blackGradient;
var height = 200;
var width = 300;
var positions;


spectrum.width = width;
spectrum.height = height;
ctx.clearRect(0, 0, width, height);

colorGradient = ctx.createLinearGradient(1, 1, width - 1, -1);
colorGradient.addColorStop(0, '#fff');
colorGradient.addColorStop(1, formatAsRGB(hueColor));
ctx.fillStyle = colorGradient;
ctx.fillRect(0, 0, width, height);

blackGradient = ctx.createLinearGradient(0, 0, -1, height - 1);
blackGradient.addColorStop(0, 'transparent');
blackGradient.addColorStop(1, '#000');
ctx.fillStyle = blackGradient;
ctx.fillRect(0, 1, width, height);

// add input color as dot background
colorDot.css('background-color', hexColor);

// get x, y coordinates to position color dot over color spectrum
positions = getDotCoordinates(hslColor[1], hslColor[2], width, height);		

// here is the code to position a circle 
colorDot.css({
	top: positions[0] + 'px',
	left: positions[1] + 'px'
});
.container {
  position: relative;
}

.colorDot {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: 2px white solid;
  position: absolute;
  top: 20px;
  left: 20px;
  background: transparent;
  transform: translate(-50%, -50%);
  z-index: 2;
  box-shadow: 0 0 5px 2px rgba(0, 0, 0, .05), 1px 1px 3px 1px rgba(0, 0, 0, .15);
  
}

canvas {
    width: 300px;
    height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>

<div class="container">
  <canvas></canvas>
  <div class="colorDot"></div>
</div>

1 Ответ

0 голосов
/ 16 июня 2019

Большая часть проблемы заключается в том, что наложение двух градиентов точно не представляет цветовое пространство, а цветовое пространство hsl выглядит очень по-разному.Я сделал рабочую версию вашего средства выбора здесь: https://jsfiddle.net/4sy82q57/

Самое большое изменение заключается в том, что я добавил функцию для создания hsl-фона:

function renderHSL(context, hue, width, height) {
  const imgData = context.getImageData(0, 0, width, height);
  const pixels = imgData.data;
  let x, y, rgb, o;
  for (y = 0; y < height; y++) { 
    for (x = 0; x < width; x++) {
      o = (y * width + x) * 4;
      rgb = hslToRgb(hue, x / width, y / height);
      if (x == 100 && y == 100) {
        console.log(rgb);
      }
      pixels[o] = rgb[0];
      pixels[o+1] = rgb[1];
      pixels[o+2] = rgb[2];
      pixels[o+3] = 255;
    }
  }
  context.putImageData(imgData, 0, 0);
}

При этом getDotCoordinatesфункция стала намного проще.

...