簡體   English   中英

在 mousemove 上從 canvas 獲取像素顏色

[英]Get pixel color from canvas, on mousemove

是否可以在鼠標下獲取RGB值像素? 有沒有完整的例子? 這是我到目前為止所擁有的:

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]);
      };
    }

這是一個完整的、獨立的示例。 首先,使用以下 HTML:

<canvas id="example" width="200" height="60"></canvas>
<div id="status"></div>

然后在具有隨機背景 colors 的 canvas 上放置一些方塊:

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);
});

上面的代碼假設存在 jQuery 和以下實用函數:

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)})`
}

在這里查看它的實際效果:

 // 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>

我知道這是一個老問題,但這里有一個替代方案。 我將該圖像數據存儲在一個數組中,然后,在 canvas 上的鼠標移動事件中:

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]

比每次獲取 imageData 要容易得多。

合並 StackOverflow(包括上面的文章)和其他站點中的各種參考資料,我使用 javascript 和 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>

這是我的完整解決方案。 這里我只使用了 canvas 和一張圖片,但是如果你需要在圖片上使用<map>也是可以的。

每次調用 getImageData 都會減慢進程...為了加快速度,我建議存儲圖像數據,然后您可以輕松快速地獲得 pix 值,所以為了更好的性能做這樣的事情

// 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);

如果您需要獲取矩形區域的平均顏色,而不是單個像素的顏色,請查看另一個問題:

JavaScript - 從圖像的某個區域獲取平均顏色

無論如何,兩者都以非常相似的方式完成:

從圖像或 Canvas 中獲取單個像素的顏色/值

要獲得單個像素的顏色,您首先將該圖像繪制到 canvas,您已經完成了:

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);

然后像這樣獲取單個像素的值:

const data = context.getImageData(X, Y, 1, 1).data;

// RED   = data[0]
// GREEN = data[1]
// BLUE  = data[2]
// ALPHA = data[3]

通過一次獲取所有 ImageData 來加速變薄

您需要使用相同的CanvasRenderingContext2D.getImageData()來獲取整個圖像的值,您可以通過更改其第三個和第四個參數來做到這一點。 該 function 的簽名是:

ImageData ctx.getImageData(sx, sy, sw, sh);
  • sx :從中提取 ImageData 的矩形左上角的 x 坐標。
  • sy : 矩形左上角的 y 坐標,從中提取 ImageData。
  • sw :從中提取 ImageData 的矩形的寬度。
  • sh :從中提取 ImageData 的矩形的高度。

你可以看到它返回一個ImageData object,不管它是什么 這里重要的部分是 object 有一個.data屬性,其中包含我們所有的像素值。

但是,請注意.data屬性是一維Uint8ClampedArray ,這意味着所有像素的組件都已展平,因此您會得到如下所示的內容:

假設您有一個像這樣的 2x2 圖像:

 RED PIXEL |       GREEN PIXEL
BLUE PIXEL | TRANSPARENT PIXEL

然后,你會像這樣得到它們:

[ 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 | 

由於調用getImageData是一個緩慢的操作,你可以只調用一次來獲取所有圖像的數據( sw =圖像寬度, sh =圖像高度)。

然后,在上面的例子中,如果你想訪問TRANSPARENT PIXEL的組件,也就是 position x = 1, y = 1這個假想圖像的組件,你會在它的ImageData中找到它的第一個索引i data屬性為:

const i = (y * imageData.width + x) * 4;

✨ 讓我們看看它的實際應用

 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>

⚠️ 請注意,如果我嘗試使用更長的數據 URI,如果我包含外部圖像或大於允許的答案,我將使用小數據 URI 來避免Cross-Origin問題。

️ 這些 colors 看起來很奇怪,不是嗎?

如果您將 cursor 移動到星號形狀的邊界周圍,您會看到有時avgSolidColor是紅色的,但您采樣的像素看起來是白色的。 這是因為即使該像素的R分量可能很高,但 alpha 通道很低,所以顏色實際上是幾乎透明的紅色陰影,但avgSolidColor忽略了這一點。

另一方面, avgAlphaColor看起來是粉紅色的。 好吧,這實際上不是真的,它只是看起來是粉紅色的,因為我們現在使用的是 alpha 通道,這使它成為半透明的,並允許我們看到頁面的背景,在這種情況下是白色的。

Alpha 加權顏色

那么,我們能做些什么來解決這個問題呢? 好吧,事實證明,我們只需要使用 alpha 通道及其倒數作為權重來計算新樣本的分量,在這種情況下,將它與白色合並,因為這是我們用作背景的顏色。

這意味着如果一個像素是R, G, B, A ,其中A在區間[0, 1]中,我們將計算 alpha 通道的倒數iA和加權樣本的分量:

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;

請注意像素越透明( A越接近 0),顏色越亮。

快速回答

context.getImageData(x, y, 1, 1).data; 返回一個 rgba 數組。 例如[50, 50, 50, 255]


這是@lwburk 的 rgbToHex function 的一個版本,它將 rgba 數組作為參數。

function rgbToHex(rgb){
  return '#' + ((rgb[0] << 16) | (rgb[1] << 8) | rgb[2]).toString(16);
};

我有一個從 canvas 獲取像素顏色的非常簡單的工作示例。

首先是一些基本的 HTML:

<canvas id="myCanvas" width="400" height="250" style="background:red;" onmouseover="echoColor(event)">
</canvas>

然后JS在Canvas上畫點東西,並得到顏色:

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);  
}

這是一個工作示例,只需查看控制台即可。

你可以試試color-sampler 這是在 canvas 中挑選顏色的簡單方法。 演示

@Wayne Burkett 的回答很好。 如果您還想提取 alpha 值以獲得 rgba 顏色,我們可以這樣做:

var r = p[0], g = p[1], b = p[2], a = p[3] / 255;
var rgba = "rgb(" + r + "," + g + "," + b + "," + a + ")";

I divided the alpha value by 255 because the ImageData object stores it as an integer between 0 - 255, but most applications (for example, CanvasRenderingContext2D.fillRect() ) require colors to be in valid CSS format, where the alpha value is between 0和 1。

(還請記住,如果您提取透明顏色,然后將其繪制回 canvas,它將覆蓋以前的任何顏色。因此,如果您在同一點上繪制顏色rgba(0,0,0,0.1) 10 次,它將是黑色的。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM