É possível obter o pixel de valor RGB sob o mouse? Existe um exemplo completo disso? Aqui está o que tenho até agora:
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
fonte
fonte
Respostas:
Aqui está um exemplo completo e independente. Primeiro, use o seguinte HTML:
<canvas id="example" width="200" height="60"></canvas> <div id="status"></div>
Em seguida, coloque alguns quadrados na tela com cores de fundo aleatórias:
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 cor ao passar o mouse:
$('#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); });
O código acima pressupõe a presença de jQuery e as seguintes funções de utilitário:
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)})` }
Veja em ação aqui:
Exibir trecho 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>
fonte
offsetParent
s aninhados é uma maneira muito boa de fazer isso. Nunca pensei nisso. Mas por que você não usa umwhile
loop regular em vez de umif
e depois umdo...while
?Eu sei que esta é uma pergunta antiga, mas aqui está uma alternativa. Eu armazenaria os dados da imagem em uma matriz e, em seguida, no evento de movimento do mouse sobre a tela:
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]
Muito mais fácil do que obter o imageData sempre.
fonte
Mesclando várias referências encontradas aqui no StackOverflow (incluindo o artigo acima) e em outros sites, fiz isso usando javascript e 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 é minha solução completa. Aqui usei apenas canvas e uma imagem, mas se precisar usar
<map>
sobre a imagem também é possível.fonte
chamar getImageData todas as vezes tornará o processo mais lento ... para acelerar coisas, eu recomendo armazenar dados de imagem e então você pode obter valor de pix facilmente e rapidamente, então faça algo assim para melhor desempenho
// 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);
fonte
Se você precisar obter a cor média de uma área retangular, em vez da cor de um único pixel, dê uma olhada nesta outra questão:
👉 JavaScript - Obtenha a cor média de uma determinada área de uma imagem
De qualquer forma, ambos são feitos de maneira muito semelhante:
🔍 Obtendo a cor / valor de um único pixel de uma imagem ou tela
Para obter a cor de um único pixel, você deve primeiro desenhar essa imagem em uma tela, o que você já fez:
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);
E então obtenha o valor de um único pixel como este:
const data = context.getImageData(X, Y, 1, 1).data; // RED = data[0] // GREEN = data[1] // BLUE = data[2] // ALPHA = data[3]
🚀 Acelerar afina obtendo todos os ImageData de uma só vez
Você precisa usar este mesmo CanvasRenderingContext2D.getImageData () para obter os valores de toda a imagem, o que você faz alterando seu terceiro e quarto parâmetros. A assinatura dessa função é:
sx
: A coordenada x do canto superior esquerdo do retângulo do qual os ImageData serão extraídos.sy
: A coordenada y do canto superior esquerdo do retângulo do qual o ImageData será extraído.sw
: A largura do retângulo do qual o ImageData será extraído.sh
: A altura do retângulo do qual o ImageData será extraído.Você pode ver que ele retorna um
ImageData
objeto, seja ele qual for . A parte importante aqui é que esse objeto tem um.data
propriedade que contém todos os nossos valores de pixel.No entanto, observe que a
.data
propriedade é de 1 dimensãoUint8ClampedArray
, o que significa que todos os componentes do pixel foram achatados, então você está obtendo algo parecido com isto:Digamos que você tenha uma imagem 2x2 como esta:
Então, você os obterá assim:
[ 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 a chamada
getImageData
é uma operação lenta, você pode chamá-la apenas uma vez para obter os dados de toda a imagem (sw
= largura da imagem,sh
= altura da imagem).Então, no exemplo acima, se você quiser acessar os componentes do
TRANSPARENT PIXEL
, isto é, aquele em posiçãox = 1, y = 1
desta imagem imaginário, você iria encontrar o seu primeiro índicei
em seuImageData
'sdata
propriedade como:const i = (y * imageData.width + x) * 4;
✨ Vamos ver em ação
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>
⚠️ Observação Estou usando um URI de dados pequeno para evitar
Cross-Origin
problemas se incluir uma imagem externa ou uma resposta maior do que o permitido se eu tentar usar um URI de dados mais longo.🕵️ Essas cores parecem estranhas, não é?
Se você mover o cursor ao redor das bordas da forma de asterisco, verá que às vezes
avgSolidColor
é vermelho, mas o pixel da amostra parece branco. Isso porque, embora oR
componente para aquele pixel possa ser alto, o canal alfa é baixo, então a cor é na verdade um tom quase transparente de vermelho, masavgSolidColor
ignora isso.Por outro lado,
avgAlphaColor
parece rosa. Bem, isso não é verdade, só parece rosa porque agora estamos usando o canal alfa, o que o torna semitransparente e nos permite ver o fundo da página, que neste caso é branco.🎨 Cor alfa ponderada
Então, o que podemos fazer para consertar isso? Bem, acontece que só precisamos usar o canal alfa e seu inverso como pesos para calcular os componentes de nossa nova amostra, neste caso mesclando-o com o branco, já que essa é a cor que usamos como fundo.
Isso significa que se um pixel está
R, G, B, A
, ondeA
está no intervalo[0, 1]
, calcularemos o inverso do canal alfaiA
, e os componentes da amostra 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;
Observe como quanto mais transparente é um pixel (
A
mais próximo de 0), mais clara é a cor.fonte
Resposta rápida
context.getImageData(x, y, 1, 1).data;
retorna uma matriz rgba. por exemplo[50, 50, 50, 255]
Aqui está uma versão da função rgbToHex de @lwburk que recebe o array rgba como argumento.
function rgbToHex(rgb){ return '#' + ((rgb[0] << 16) | (rgb[1] << 8) | rgb[2]).toString(16); };
fonte
[10, 42, 67, 255]
produz#a2a43
que não é um código de cor hexadecimal válido / bem formatado.Você pode experimentar o classificador de cores . É uma maneira fácil de escolher a cor em uma tela. Veja a demonstração .
fonte
Eu tenho um exemplo de trabalho muito simples de obter a cor de pixel da tela.
Primeiro, algum HTML básico:
<canvas id="myCanvas" width="400" height="250" style="background:red;" onmouseover="echoColor(event)"> </canvas>
Em seguida, JS para desenhar algo na tela e obter cor:
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); }
Aqui está um exemplo prático , basta olhar para o console.
fonte
A resposta de @Wayne Burkett é boa. Se você quiser também extrair o valor alfa para obter uma cor rgba, poderíamos fazer isso:
var r = p[0], g = p[1], b = p[2], a = p[3] / 255; var rgba = "rgb(" + r + "," + g + "," + b + "," + a + ")";
Dividi o valor alfa por 255 porque o objeto ImageData o armazena como um número inteiro entre 0 e 255, mas a maioria dos aplicativos (por exemplo,
CanvasRenderingContext2D.fillRect()
) exige que as cores estejam em formato CSS válido, em que o valor alfa está entre 0 e 1.(Lembre-se também de que se você extrair uma cor transparente e, em seguida, desenhá-la de volta na tela, ela se sobreporá a qualquer cor que esteja lá anteriormente. Portanto, se você desenhou a cor
rgba(0,0,0,0.1)
sobre o mesmo ponto 10 vezes, seria preto.)fonte