¿Es posible obtener el píxel de valor RGB con el mouse? ¿Existe un ejemplo completo de esto? Esto es lo que tengo hasta ahora:
function draw() {
var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image();
img.src = 'Your URL';
img.onload = function(){
ctx.drawImage(img,0,0);
};
canvas.onmousemove = function(e) {
var mouseX, mouseY;
if(e.offsetX) {
mouseX = e.offsetX;
mouseY = e.offsetY;
}
else if(e.layerX) {
mouseX = e.layerX;
mouseY = e.layerY;
}
var c = ctx.getImageData(mouseX, mouseY, 1, 1).data;
$('#ttip').css({'left':mouseX+20, 'top':mouseY+20}).html(c[0]+'-'+c[1]+'-'+c[2]);
};
}
javascript
html
jquery
canvas
getimagedata
vince83000
fuente
fuente
Respuestas:
Aquí hay un ejemplo completo e independiente. Primero, use el siguiente HTML:
<canvas id="example" width="200" height="60"></canvas> <div id="status"></div>
Luego coloque algunos cuadrados en el lienzo con colores de fondo aleatorios:
var example = document.getElementById('example'); var context = example.getContext('2d'); context.fillStyle = randomColor(); context.fillRect(0, 0, 50, 50); context.fillStyle = randomColor(); context.fillRect(55, 0, 50, 50); context.fillStyle = randomColor(); context.fillRect(110, 0, 50, 50);
E imprima cada color al pasar el mouse por encima:
$('#example').mousemove(function(e) { var pos = findPos(this); var x = e.pageX - pos.x; var y = e.pageY - pos.y; var coord = "x=" + x + ", y=" + y; var c = this.getContext('2d'); var p = c.getImageData(x, y, 1, 1).data; var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6); $('#status').html(coord + "<br>" + hex); });
El código anterior asume la presencia de jQuery y las siguientes funciones de utilidad:
function findPos(obj) { var curleft = 0, curtop = 0; if (obj.offsetParent) { do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); return { x: curleft, y: curtop }; } return undefined; } function rgbToHex(r, g, b) { if (r > 255 || g > 255 || b > 255) throw "Invalid color component"; return ((r << 16) | (g << 8) | b).toString(16); } function randomInt(max) { return Math.floor(Math.random() * max); } function randomColor() { return `rgb(${randomInt(256)}, ${randomInt(256)}, ${randomInt(256)})` }
Véalo en acción aquí:
Mostrar fragmento de código
// set up some sample squares with random colors var example = document.getElementById('example'); var context = example.getContext('2d'); context.fillStyle = randomColor(); context.fillRect(0, 0, 50, 50); context.fillStyle = randomColor(); context.fillRect(55, 0, 50, 50); context.fillStyle = randomColor(); context.fillRect(110, 0, 50, 50); $('#example').mousemove(function(e) { var pos = findPos(this); var x = e.pageX - pos.x; var y = e.pageY - pos.y; var coord = "x=" + x + ", y=" + y; var c = this.getContext('2d'); var p = c.getImageData(x, y, 1, 1).data; var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6); $('#status').html(coord + "<br>" + hex); }); function findPos(obj) { var curleft = 0, curtop = 0; if (obj.offsetParent) { do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } while (obj = obj.offsetParent); return { x: curleft, y: curtop }; } return undefined; } function rgbToHex(r, g, b) { if (r > 255 || g > 255 || b > 255) throw "Invalid color component"; return ((r << 16) | (g << 8) | b).toString(16); } function randomInt(max) { return Math.floor(Math.random() * max); } function randomColor() { return `rgb(${randomInt(256)}, ${randomInt(256)}, ${randomInt(256)})` }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <canvas id="example" width="200" height="60"></canvas> <div id="status"></div>
fuente
offsetParent
s anidados es una forma realmente agradable de hacerlo. Nunca pensé en eso. Pero, ¿por qué no usas unwhile
bucle regular en lugar de unif
y luego undo...while
?Sé que esta es una pregunta antigua, pero aquí hay una alternativa. Almacenaría esos datos de imagen en una matriz, luego, en el evento de movimiento del mouse sobre el lienzo:
var index = (Math.floor(y) * canvasWidth + Math.floor(x)) * 4 var r = data[index] var g = data[index + 1] var b = data[index + 2] var a = data[index + 3]
Mucho más fácil que obtener imageData cada vez.
fuente
Combinando varias referencias encontradas aquí en StackOverflow (incluido el artículo anterior) y en otros sitios, lo hice usando javascript y JQuery:
<html> <body> <canvas id="myCanvas" width="400" height="400" style="border:1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas> <script src="jquery.js"></script> <script type="text/javascript"> window.onload = function(){ var canvas = document.getElementById('myCanvas'); var context = canvas.getContext('2d'); var img = new Image(); img.src = 'photo_apple.jpg'; context.drawImage(img, 0, 0); }; function findPos(obj){ var current_left = 0, current_top = 0; if (obj.offsetParent){ do{ current_left += obj.offsetLeft; current_top += obj.offsetTop; }while(obj = obj.offsetParent); return {x: current_left, y: current_top}; } return undefined; } function rgbToHex(r, g, b){ if (r > 255 || g > 255 || b > 255) throw "Invalid color component"; return ((r << 16) | (g << 8) | b).toString(16); } $('#myCanvas').click(function(e){ var position = findPos(this); var x = e.pageX - position.x; var y = e.pageY - position.y; var coordinate = "x=" + x + ", y=" + y; var canvas = this.getContext('2d'); var p = canvas.getImageData(x, y, 1, 1).data; var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6); alert("HEX: " + hex); }); </script> <img src="photo_apple.jpg"/> </body> </html>
Esta es mi solución completa. Aquí solo usé lienzo y una imagen, pero si necesita usar
<map>
sobre la imagen, también es posible.fuente
llamar a getImageData cada vez ralentizará el proceso ... para acelerar las cosas, recomiendo almacenar datos de imagen y luego puede obtener el valor de imagen fácil y rápidamente, así que haga algo como esto para un mejor rendimiento
// keep it global let imgData = false; // initially no image data we have // create some function block if(imgData === false){ // fetch once canvas data var ctx = canvas.getContext("2d"); imgData = ctx.getImageData(0, 0, canvas.width, canvas.height); } // Prepare your X Y coordinates which you will be fetching from your mouse loc let x = 100; // let y = 100; // locate index of current pixel let index = (y * imgData.width + x) * 4; let red = imgData.data[index]; let green = imgData.data[index+1]; let blue = imgData.data[index+2]; let alpha = imgData.data[index+3]; // Output console.log('pix x ' + x +' y '+y+ ' index '+index +' COLOR '+red+','+green+','+blue+','+alpha);
fuente
Respuesta rápida
context.getImageData(x, y, 1, 1).data;
devuelve una matriz rgba. p.ej[50, 50, 50, 255]
Aquí hay una versión de la función rgbToHex de @ lwburk que toma la matriz rgba como argumento.
function rgbToHex(rgb){ return '#' + ((rgb[0] << 16) | (rgb[1] << 8) | rgb[2]).toString(16); };
fuente
[10, 42, 67, 255]
produce#a2a43
un código de color hexadecimal válido / bien formateado.Si necesita obtener el color promedio de un área rectangular, en lugar del color de un solo píxel, eche un vistazo a esta otra pregunta:
👉 JavaScript: obtenga un color promedio de un área determinada de una imagen
De todos modos, ambos se hacen de forma muy similar:
🔍 Obtener el color / valor de un solo píxel de una imagen o lienzo
Para obtener el color de un solo píxel, primero debe dibujar esa imagen en un lienzo, lo que ya ha hecho:
const image = document.getElementById('image'); const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); const width = image.width; const height = image.height; canvas.width = width; canvas.height = height; context.drawImage(image, 0, 0, width, height);
Y luego obtenga el valor de un solo píxel como este:
const data = context.getImageData(X, Y, 1, 1).data; // RED = data[0] // GREEN = data[1] // BLUE = data[2] // ALPHA = data[3]
🚀 Acelerar se reduce al obtener todos los ImageData a la vez
Necesita usar este mismo CanvasRenderingContext2D.getImageData () para obtener los valores de toda la imagen, lo que hace cambiando su tercer y cuarto parámetro. La firma de esa función es:
sx
: La coordenada x de la esquina superior izquierda del rectángulo del que se extraerán los datos de imagen.sy
: La coordenada y de la esquina superior izquierda del rectángulo del que se extraerán los ImageData.sw
: El ancho del rectángulo del que se extraerán los ImageData.sh
: La altura del rectángulo del que se extraerán los datos de imagen.Puede ver que devuelve un
ImageData
objeto, sea lo que sea . La parte importante aquí es que ese objeto tiene una.data
propiedad que contiene todos nuestros valores de píxeles.Sin embargo, tenga en cuenta que la
.data
propiedad es de una dimensiónUint8ClampedArray
, lo que significa que todos los componentes del píxel se han aplanado, por lo que obtiene algo que se parece a esto:Digamos que tienes una imagen de 2x2 como esta:
Entonces, los obtendrás así:
[ 255, 0, 0, 255, 0, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0 ] | RED PIXEL | GREEN PIXEL | BLUE PIXEL | TRANSPAERENT PIXEL | | 1ST PIXEL | 2ND PIXEL | 3RD PIXEL | 4TH PIXEL |
Como llamar
getImageData
es una operación lenta, puede llamarla solo una vez para obtener los datos de toda la imagen (sw
= ancho de imagen,sh
= alto de imagen).Entonces, en el ejemplo anterior, si desea acceder a los componentes de la
TRANSPARENT PIXEL
, es decir, el que está en la posiciónx = 1, y = 1
de esta imagen imaginaria, que encontraría su primer índicei
en suImageData
'sdata
propiedad como:const i = (y * imageData.width + x) * 4;
✨ Veámoslo en acción
const solidColor = document.getElementById('solidColor'); const alphaColor = document.getElementById('alphaColor'); const solidWeighted = document.getElementById('solidWeighted'); const solidColorCode = document.getElementById('solidColorCode'); const alphaColorCode = document.getElementById('alphaColorCode'); const solidWeightedCOde = document.getElementById('solidWeightedCode'); const brush = document.getElementById('brush'); const image = document.getElementById('image'); const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); const width = image.width; const height = image.height; const BRUSH_SIZE = brush.offsetWidth; const BRUSH_CENTER = BRUSH_SIZE / 2; const MIN_X = image.offsetLeft + 4; const MAX_X = MIN_X + width - 1; const MIN_Y = image.offsetTop + 4; const MAX_Y = MIN_Y + height - 1; canvas.width = width; canvas.height = height; context.drawImage(image, 0, 0, width, height); const imageDataData = context.getImageData(0, 0, width, height).data; function sampleColor(clientX, clientY) { if (clientX < MIN_X || clientX > MAX_X || clientY < MIN_Y || clientY > MAX_Y) { requestAnimationFrame(() => { brush.style.transform = `translate(${ clientX }px, ${ clientY }px)`; solidColorCode.innerText = solidColor.style.background = 'rgb(0, 0, 0)'; alphaColorCode.innerText = alphaColor.style.background = 'rgba(0, 0, 0, 0.00)'; solidWeightedCode.innerText = solidWeighted.style.background = 'rgb(0, 0, 0)'; }); return; } const imageX = clientX - MIN_X; const imageY = clientY - MIN_Y; const i = (imageY * width + imageX) * 4; // A single pixel (R, G, B, A) will take 4 positions in the array: const R = imageDataData[i]; const G = imageDataData[i + 1]; const B = imageDataData[i + 2]; const A = imageDataData[i + 3] / 255; const iA = 1 - A; // Alpha-weighted color: const wR = (R * A + 255 * iA) | 0; const wG = (G * A + 255 * iA) | 0; const wB = (B * A + 255 * iA) | 0; // Update UI: requestAnimationFrame(() => { brush.style.transform = `translate(${ clientX }px, ${ clientY }px)`; solidColorCode.innerText = solidColor.style.background = `rgb(${ R }, ${ G }, ${ B })`; alphaColorCode.innerText = alphaColor.style.background = `rgba(${ R }, ${ G }, ${ B }, ${ A.toFixed(2) })`; solidWeightedCode.innerText = solidWeighted.style.background = `rgb(${ wR }, ${ wG }, ${ wB })`; }); } document.onmousemove = (e) => sampleColor(e.clientX, e.clientY); sampleColor(MIN_X, MIN_Y);
body { margin: 0; height: 100vh; display: flex; flex-direction: row; align-items: center; justify-content: center; cursor: none; font-family: monospace; overflow: hidden; } #image { border: 4px solid white; border-radius: 2px; box-shadow: 0 0 32px 0 rgba(0, 0, 0, .25); width: 150px; box-sizing: border-box; } #brush { position: absolute; top: 0; left: 0; pointer-events: none; width: 1px; height: 1px; mix-blend-mode: exclusion; border-radius: 100%; } #brush::before, #brush::after { content: ''; position: absolute; background: magenta; } #brush::before { top: -16px; left: 0; height: 33px; width: 100%; } #brush::after { left: -16px; top: 0; width: 33px; height: 100%; } #samples { position: relative; list-style: none; padding: 0; width: 250px; } #samples::before { content: ''; position: absolute; top: 0; left: 27px; width: 2px; height: 100%; background: black; border-radius: 1px; } #samples > li { position: relative; display: flex; flex-direction: column; justify-content: center; padding-left: 56px; } #samples > li + li { margin-top: 8px; } .sample { position: absolute; top: 50%; left: 16px; transform: translate(0, -50%); display: block; width: 24px; height: 24px; border-radius: 100%; box-shadow: 0 0 16px 4px rgba(0, 0, 0, .25); margin-right: 8px; } .sampleLabel { font-weight: bold; margin-bottom: 8px; } .sampleCode { }
<img id="image" src="data:image/gif;base64,R0lGODlhSwBLAPEAACMfIO0cJAAAAAAAACH/C0ltYWdlTWFnaWNrDWdhbW1hPTAuNDU0NTUAIf4jUmVzaXplZCBvbiBodHRwczovL2V6Z2lmLmNvbS9yZXNpemUAIfkEBQAAAgAsAAAAAEsASwAAAv+Uj6mb4A+QY7TaKxvch+MPKpC0eeUUptdomOzJqnLUvnFcl7J6Pzn9I+l2IdfII8DZiCnYsYdK4qRTptAZwQKRVK71CusOgx2nFRrlhMu+33o2NEalC6S9zQvfi3Mlnm9WxeQ396F2+HcQsMjYGEBRVbhy5yOp6OgIeVIHpEnZyYCZ6cklKBJX+Kgg2riqKoayOWl2+VrLmtDqBptIOjZ6K4qAeSrL8PcmHExsgMs2dpyIxPpKvdhM/YxaTMW2PGr9GP76BN3VHTMurh7eoU14jsc+P845Vn6OTb/P/I68iYOfwGv+JOmRNHBfsV5ujA1LqM4eKDoNvXyDqItTxYX/DC9irKBlIhkKGPtFw1JDiMeS7CqWqySPZcKGHH/JHGgIpb6bCl1O0LmT57yCOqoI5UcU0YKjPXmFjMm0ZQ4NIVdGBdZRi9WrjLxJNMY1Yr4dYeuNxWApl1ALHb+KDHrTV1owlriedJgSr4Cybu/9dFiWYAagsqAGVkkzaZTAuqD9ywKWMUG9dCO3u2zWpVzIhpW122utZlrHnTN+Bq2Mqrlnqh8CQ+0Mrq3Kc++q7eo6dlB3rLuh3abPVbbbI2mxBdhWdsZhid8cr0oy9F08q0k5FXSadiyL1mF5z51a8VsQOp3/LlodkBfzmzWf2bOrtfzr48k/1hupDaLa9rUbO+zlwndfaOCURAXRNaCBqBT2BncJakWfTzSYkmCEFr60RX0V8sKaHOltCBJ1tAAFYhHaVVbig3jxp0IBADs=" > <div id="brush"></div> <ul id="samples"> <li> <span class="sample" id="solidColor"></span> <div class="sampleLabel">solidColor</div> <div class="sampleCode" id="solidColorCode">rgb(0, 0, 0)</div> </li> <li> <span class="sample" id="alphaColor"></span> <div class="sampleLabel">alphaColor</div> <div class="sampleCode" id="alphaColorCode">rgba(0, 0, 0, 0.00)</div> </li> <li> <span class="sample" id="solidWeighted"></span> <div class="sampleLabel">solidWeighted (with white)</div> <div class="sampleCode" id="solidWeightedCode">rgb(0, 0, 0)</div> </li> </ul>
⚠️ Tenga en cuenta que estoy usando un URI de datos pequeño para evitar
Cross-Origin
problemas si incluyo una imagen externa o una respuesta más grande de lo permitido si trato de usar un URI de datos más largo.🕵️ Estos colores se ven raros, ¿no?
Si mueve el cursor alrededor de los bordes de la forma de asterisco, verá que a veces
avgSolidColor
es rojo, pero el píxel que está muestreando parece blanco. Esto se debe a que, aunque elR
componente de ese píxel puede ser alto, el canal alfa es bajo, por lo que el color es en realidad un tono rojo casi transparente, pero loavgSolidColor
ignora.Por otro lado,
avgAlphaColor
luce rosa. Bueno, eso en realidad no es cierto, solo se ve rosa porque ahora estamos usando el canal alfa, lo que lo hace semitransparente y nos permite ver el fondo de la página, que en este caso es blanco.🎨 Color ponderado alfa
Entonces, ¿qué podemos hacer para solucionar este problema? Bueno, resulta que solo necesitamos usar el canal alfa y su inverso como pesos para calcular los componentes de nuestra nueva muestra, en este caso fusionándolo con el blanco, ya que ese es el color que usamos como fondo.
Eso significa que si un píxel es
R, G, B, A
, dóndeA
está en el intervalo[0, 1]
, calcularemos la inversa del canal alfaiA
, y los componentes de la muestra ponderada como:const iA = 1 - A; const wR = (R * A + 255 * iA) | 0; const wG = (G * A + 255 * iA) | 0; const wB = (B * A + 255 * iA) | 0;
Tenga en cuenta que cuanto más transparente es un píxel (
A
más cerca de 0), más claro es el color.fuente
Puedes probar el muestreador de colores . Es una forma sencilla de elegir el color de un lienzo. Ver demo .
fuente
Tengo un ejemplo práctico muy simple de cómo obtener el color de los píxeles del lienzo.
Primero algo de HTML básico:
<canvas id="myCanvas" width="400" height="250" style="background:red;" onmouseover="echoColor(event)"> </canvas>
Luego JS para dibujar algo en el Canvas y obtener color:
var c = document.getElementById("myCanvas"); var ctx = c.getContext("2d"); ctx.fillStyle = "black"; ctx.fillRect(10, 10, 50, 50); function echoColor(e){ var imgData = ctx.getImageData(e.pageX, e.pageX, 1, 1); red = imgData.data[0]; green = imgData.data[1]; blue = imgData.data[2]; alpha = imgData.data[3]; console.log(red + " " + green + " " + blue + " " + alpha); }
Aquí hay un ejemplo de trabajo , solo mire la consola.
fuente
La respuesta de @Wayne Burkett es buena. Si también quisiera extraer el valor alfa para obtener un color rgba, podríamos hacer esto:
var r = p[0], g = p[1], b = p[2], a = p[3] / 255; var rgba = "rgb(" + r + "," + g + "," + b + "," + a + ")";
Dividí el valor alfa por 255 porque el objeto ImageData lo almacena como un número entero entre 0-255, pero la mayoría de las aplicaciones (por ejemplo,
CanvasRenderingContext2D.fillRect()
) requieren que los colores estén en formato CSS válido, donde el valor alfa está entre 0 y 1.(Recuerde también que si extrae un color transparente y luego lo vuelve a dibujar en el lienzo, se superpondrá al color que haya anteriormente. Por lo tanto, si dibujó el color
rgba(0,0,0,0.1)
sobre el mismo lugar 10 veces, sería negro).fuente