Mam płótno gradientowe HSV Raindow, które po kliknięciu dodaje element w tym miejscu z jego tłem jako kolorem klikniętego piksela.Obliczyć pozycję piksela X, Y na gradiencie w oparciu o kolor szesnastkowy za pomocą Javascriptu
Chciałbym też, aby działało również w odwrotnej kolejności. Na przykład, jeśli masz kolor heksadecymalny, chciałbym znaleźć ten piksel na płótnie i utworzyć element w tej pozycji.
Moja pierwsza myśl polegała na użyciu systemu macierzy/kwadrantu. Moja następna myśl była taka, że od kiedy używam HSV, mógłbym użyć moich punktów lokalizacji gradientu HSV, aby znaleźć lokalizację. Problem polega na tym, że moje punkty nie są od siebie równe, co sprawia, że jest trudniej. Ponadto mam biały gradient i czarny gradient pokrywający główny gradient kolorów i muszę to uwzględnić.
Moje pytanie brzmi: jak znaleźć położenie piksela koloru lub przynajmniej jego najbliższe dopasowanie za pomocą kodu szesnastkowego?
Oto mój kod do tej pory: http://codepen.io/shelbywhite/pen/EyqPWY?editors=1000
HTML:
<div class="container">
<canvas class="colorSpectrum"></canvas>
<div class="circle"></div>
</div>
CSS:
.container {
background: grey;
height: 350px;
width: 400px;
}
.circle {
background: transparent;
box-shadow: 0 0 8px rgba(0,0,0,0.2);
border-radius: 50%;
border: 2px solid #fff;
height: 20px;
margin: -12px;
width: 20px;
position: absolute;
}
.colorSpectrum {
display: block;
height: 100%;
transform: translateZ(0);
width: 100%;
}
Javascript:
$(function() {
var closest = function(num, arr) {
var curr = arr[0];
var diff = Math.abs(num - curr);
for (var val = 0; val < arr.length; val++) {
var newdiff = Math.abs(num - arr[val]);
if (newdiff < diff) {
diff = newdiff;
curr = arr[val];
}
}
return curr;
};
var container = $('.container');
var containerWidth = container.width();
var containerHeight = container.height();
var verticalGradientsHeight = Math.round(containerHeight * .34);
console.log('verticalGradientsHeight', verticalGradientsHeight);
var round = function(value, decimals) {
return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
};
// Draws the color spectrum onto the canvas
var drawColorSpectrum = function() {
// Cache canvas element
var canvasElement = $('.colorSpectrum');
// Cache javascript element
var canvas = canvasElement[0];
// Get canvas context
var ctx = canvas.getContext('2d');
// Cache page height
var canvasWidth = containerWidth;
// Cache page height
var canvasHeight = containerHeight - 72;
// Bottom gradient start position
var blackStartYPos = canvasHeight - verticalGradientsHeight;
// Bottom gradient end position
var blackEndYPos = canvasHeight;
// Create white gradient element
var white = ctx.createLinearGradient(0, 0, 0, verticalGradientsHeight);
// Create black gradient element
var black = ctx.createLinearGradient(0, blackStartYPos, 0, blackEndYPos);
// Create new instance of image
var img = new Image();
// Cache container
_colorSpectrumContainer = canvasElement.parent();
// Set global var
spectrumCanvas = canvasElement;
// Set width of canvas
canvas.width = canvasWidth;
// Set height of canvas
canvas.height = canvasHeight;
// Image load listener
img.onload = function() {
// Draw intial image
ctx.drawImage(this, 0, 0, canvasWidth, canvasHeight);
// Draw white to transparent gradient
white.addColorStop(0, "hsla(0,0%,100%,1)");
white.addColorStop(0.05, "hsla(0,0%,100%,1)");
white.addColorStop(0.20, "hsla(0,0%,100%,0.89)");
white.addColorStop(0.38, "hsla(0,0%,100%,0.69)");
white.addColorStop(0.63, "hsla(0,0%,100%,0.35)");
white.addColorStop(0.78, "hsla(0,0%,100%,0.18)");
white.addColorStop(0.91, "hsla(0,0%,100%,0.06)");
white.addColorStop(1, "hsla(0,0%,100%,0)");
ctx.fillStyle = white;
ctx.fillRect(0, 0, canvasWidth, verticalGradientsHeight);
// Draw black to transparent gradient
black.addColorStop(0, "hsla(0,0%,0%,0)");
black.addColorStop(0.20, "hsla(0,0%,0%,0.01)");
black.addColorStop(0.28, "hsla(0,0%,0%,0.04)");
black.addColorStop(0.35, "hsla(0,0%,0%,0.09)");
black.addColorStop(0.51, "hsla(0,0%,0%,0.26)");
black.addColorStop(0.83, "hsla(0,0%,0%,0.69)");
black.addColorStop(1, "hsla(0,0%,0%,1)");
ctx.fillStyle = black;
ctx.fillRect(0, blackStartYPos, canvasWidth, verticalGradientsHeight);
}
// Set image source
img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAAABCAYAAACbv+HiAAAA0ElEQVR4AYWSh2oDMAwFz6u7//+d2YmXalGBIBM47nnPIIEtmd8FGBTgDbPxDmbn49pX+cZX+Nz4mkZ2SECEAXTCAprlalntBC5whdUJnOfKEy5DjZYtB+o0D3XUMk0tkaZZEn2VuyiJQQQywS/P4c25ucTrfF3ndsoVdjmy3NMiuptR1eHfNcBFM2orW1ZXru00JZiBDrIII5AG5AlloX5TcG6/ywuuv0zAbyL4TWRZmIvU5TNBTjCPIIu5N3YgO7Wxtbot3q4+2LgTyFnZ/QHzBZD1KDpyqQAAAABJRU5ErkJggg==";
};
//
var hexToRgb = function(hex) {
hex = hex.replace('#','');
r = parseInt(hex.substring(0, 2), 16);
g = parseInt(hex.substring(2, 4), 16);
b = parseInt(hex.substring(4, 6), 16);
return [r, g, b];
};
//
var rgbToHsb = function(r, g, b) {
var rr, gg, bb,
r = r/255,
g = g/255,
b = b/255,
h, s,
v = Math.max(r, g, b),
diff = v - Math.min(r, g, b),
diffc = function(c){
return (v - c)/6/diff + 1/2;
};
if (diff == 0) {
h = s = 0;
} else {
s = diff/v;
rr = diffc(r);
gg = diffc(g);
bb = diffc(b);
if (r === v) {
h = bb - gg;
}else if (g === v) {
h = (1/3) + rr - bb;
}else if (b === v) {
h = (2/3) + gg - rr;
}
if (h < 0) {
h += 1;
}else if (h > 1) {
h -= 1;
}
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
b: Math.round(v * 100)
};
};
// Find hue in stop range
var findHueInStopRange = function(hue) {
// Array of hue stops with HSV, RGB, and HEX info
var stops = [{
h: 0,
l: 0,
s: 100,
b: 100
}, {
h: 60,
l: 21,
s: 100,
b: 100
}, {
h: 120,
l: 40,
s: 85,
b: 85
}, {
h: 180,
l: 56,
s: 85,
b: 85
}, {
h: 237,
l: 72,
s: 86,
b: 96
}, {
h: 300,
l: 89,
s: 86,
b: 96
}, {
h: 359,
l: 100,
s: 100,
b: 100
}];
// Total number of stops
var stopsLength = stops.length;
// Loop through stops
for (var i = 0; i < stopsLength; i += 1) {
// Temp set
var currentStop = stops[i];
// Temp set
// var nextStop = stops[i + 1];
var nextStop = (i + 1 > stopsLength - 1) ? currentStop : stops[i + 1];
// Location is a percentage
var huePos;
// Temp set
var xPos = false;
console.log('hue', currentStop.h, '>>', hue, '<<', nextStop.h);
// Find which range of hue stops the current color is
// Hue is between current and next hue stop
if (hue >= currentStop.h && hue <= nextStop.h) {
// hue is current stop
if (hue === currentStop.h) {
// Set as location
huePos = currentStop.l;
// hue is next stop
} else if (hue === nextStop.h) {
// Set as location
huePos = nextStop.l;
// Hue is somewhere between stops
} else {
// Get percentage location between hue stops
var relativeHuePos = (hue - currentStop.h)/(nextStop.h - currentStop.h);
// Normalized to fit custom gradient stop locations
huePos = relativeHuePos * (nextStop.l - currentStop.l) + currentStop.l;
}
// A location was found
if (huePos) {
// Convert from percentage to pixel position
xPos = Math.round(containerWidth * (huePos/100));
return xPos;
} else {
continue;
}
}
}
};
// Find saturation in stop range
var findSaturationInStopRange = function (saturation) {
// Array of hue stops with HSV, RGB, and HEX info
var stops = [{
l: 0,
s: 0
}, {
l: 0.05,
s: 6
}, {
l: 0.20,
s: 18
}, {
l: 0.38,
s: 35
}, {
l: 0.63,
s: 69
}, {
l: 0.78,
s: 89,
}, {
l: 0.91,
s: 100,
}, {
l: 1,
s: 100,
}];
// Total number of stops
var stopsLength = stops.length;
// Loop through stops
for (var i = 0; i < stopsLength; i += 1) {
// Temp set
var currentStop = stops[i];
// Temp set
var nextStop = (i + 1 > stopsLength - 1) ? currentStop : stops[i + 1];
// Location is a percentage
var satPos;
// Temp set
var yPos = false;
// Convert location to percentage
var currentStopLocation = currentStop.l * 100;
// Convert location to percentage
var nextStopLocation = nextStop.l * 100;
// Find which range of hue stops the current color is
// Hue is between current and next hue stop
if (saturation >= currentStop.s && saturation <= nextStop.s) {
// hue is current stop
if (saturation === currentStop.s) {
// Set as location
satPos = currentStopLocation;
// hue is next stop
} else if (saturation === nextStop.s) {
// Set as location
satPos = nextStopLocation;
// Hue is somewhere between stops
} else {
// Get percentage location between gradient stops
var ratioBetweenSaturation = (saturation - currentStop.s)/(nextStop.s - currentStop.s);
// Normalized to fit custom gradient stop locations
satPos = ratioBetweenSaturation * (nextStopLocation - currentStopLocation) + currentStopLocation;
}
console.log('ratioBetweenSaturation', ratioBetweenSaturation);
console.log('satPos', satPos);
console.log('saturation', saturation, '>=', currentStop.s, saturation, '<=', nextStop.s);
// A location was found
if (satPos !== false) {
// Convert from percentage to pixel position
yPos = Math.round(verticalGradientsHeight * (satPos/100));
return yPos;
} else {
continue;
}
}
}
};
// Find brightness in stop range
var findBrightnessInStopRange = function (brightness) {
// Array of hue stops with HSV, RGB, and HEX info
var stops = [{
l: 0,
b: 100
}, {
l: 0.20,
b: 88
}, {
l: 0.28,
b: 69
}, {
l: 0.35,
b: 26
}, {
l: 0.51,
b: 9
}, {
l: 0.83,
b: 4,
}, {
l: 1,
b: 0,
}];
// Total number of stops
var stopsLength = stops.length;
// Loop through stops
for (var i = 0; i < stopsLength; i += 1) {
// Temp set
var currentStop = stops[i];
// Temp set
var nextStop = (i + 1 > stopsLength - 1) ? currentStop : stops[i + 1];
// Location is a percentage
var brightPos;
// Temp set
var yPos = false;
// Convert location to percentage
var currentStopLocation = currentStop.l * 100;
// Convert location to percentage
var nextStopLocation = nextStop.l * 100;
console.log('brightness', brightness, '>=', currentStop.b, brightness, '<=', nextStop.b);
// Find which range of hue stops the current color is
// Hue is between current and next hue stop
if (brightness <= currentStop.b && brightness >= nextStop.b) {
// hue is current stop
if (brightness === currentStop.b) {
// Set as location
brightPos = currentStopLocation;
// hue is next stop
} else if (brightness === nextStop.b) {
// Set as location
brightPos = nextStopLocation;
// Hue is somewhere between stops
} else {
// Get percentage location between gradient stops
var ratioBetweenBrightness = (brightness - currentStop.b)/(nextStop.b - currentStop.b);
// Normalized to fit custom gradient stop locations
brightPos = ratioBetweenBrightness * (nextStopLocation - currentStopLocation) + currentStopLocation;
}
console.log('ratioBetweenBrightness', ratioBetweenBrightness);
console.log('brightPos', brightPos);
console.log('brightness', brightness, '>=', currentStop.b, brightness, '<=', nextStop.b);
// A location was found
if (brightPos !== false) {
// Convert from percentage to pixel position
yPos = Math.round(verticalGradientsHeight * (brightPos/100));
return yPos;
} else {
continue;
}
}
}
};
// Get coordinates from hue, brightness, saturation
var getColorCoordinates = function (hex) {
// Convert hex to rgb
var rgb = hexToRgb(hex);
console.log('rgb', rgb);
// Convert rgb to hsb
var hsb = rgbToHsb(rgb[0], rgb[1], rgb[2]);
console.log('hsb', hsb);
// Set x position to position of hue
var xPos = findHueInStopRange(hsb.h);
var yPos = 0;
// if 100, get (containerHeight - verticalGradientHeight) + whatever position is set with bottom gradient
//
// Saturation and brightness are both maxed
if (hsb.s === 100 && hsb.b === 100) {
// Set y position at center of container
yPos = containerHeight * 0.5;
} else {
console.log('using nothing', hsb.s, hsb.b);
//
if (hsb.s < 100) {
// Saturation y position (upper quadrant)
yPos = findSaturationInStopRange(hsb.s);
console.log('using saturation', yPos);
} else if (hsb.b < 100) {
// Brightness y position (lower quadrant)
yPos = findBrightnessInStopRange(hsb.b);
console.log('using brightness', yPos);
}
}
return { x: xPos, y: yPos };
}
// Get hue location
var position = false;
// Temp set
var hex = '42ad40';
// Draw gradient
drawColorSpectrum();
// Find x position
position = getColorCoordinates(hex); //91ff26
console.log('location', position);
// Draw line
$('.circle').css({
top: position.y + 'px',
left: position.x + 'px',
background: '#' + hex
});
});
** AKTUALIZACJA **
Próbuję to zrobić w HSV, a nie HSL. Nie mam innych preferencji poza tym, że użyłem Photoshopa do wygenerowania gładkiego gradientu.
Dodałem też nowy przykład z tym, co stworzyłem. Jedna z wyżej wymienionych odpowiedzi poniżej podpowiada, jak osiągnąć to, co próbuję zrobić, ale jak dotąd nie udało mi się tego zrobić.
Zaktualizowany kod będzie pod tym linkiem, ja również uaktualniony kod powyżej: http://codepen.io/shelbywhite/pen/EyqPWY?editors=1000
Dzięki za pomoc tutaj. Konwersja z RGB na HSL jest dość łatwa, ale wykreślenie koloru na spektrum kolorów jest naprawdę tam, gdzie leży problem. Głównie dlatego, że używam niestandardowego gradientu HSL, w którym nie każdy kolor ma 60 stopni od drugiego. Czy mógłbyś bardziej rozwinąć swoją odpowiedź? – stwhite
@stWhite Gradienty używane na płótnie są liniowe. Więc utrzymuj tablicę z Hues i dopasowanym stopniem gradientu 'st = [{h: 0, s: 0}, {h: 60, s: 0.2}, ...' (oszczędność miejsca h = hue s = stop) gdy dostaniesz odcień, odszukaj elementy tablicy, których odcień znajduje się pomiędzy 'if (h> = st [i] .h && h <= st [i + 1].h) {'następnie znajdź pozycję' pos = (h-st [i] .h)/(st [i + 1] .h-st [i] .h) 'następnie do przestrzeni gradientowej' g = pos * (st [i + 1] .s-st [i] .s) + st [i] .s' teraz g jest znormalizowany dla gradientu. Tak więc, aby uzyskać 'xpos = gradientWidth * g'. To samo dla lum – Blindman67
@stwhite To jest oczekiwane zachowanie. Od czerni, szarości, do bieli nie ma odcienia, powrót odcienia dawałby zły wynik, jeśli odcień jest naN, kolor jest szary, czarny lub biały. Wszystkie inne wartości mają kolor. – Blindman67