ChatGPT解决这个技术问题 Extra ChatGPT

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

W
Woold

这是一个完整的、独立的示例。首先,使用以下 HTML:

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

然后在画布上放置一些具有随机背景颜色的方块:

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

在这里查看它的实际效果:

https://bl.ocks.org/wayneburkett/ca41a5245a9f48766b7bc881448f9203

// 设置一些随机颜色的样本方块 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 + "
" + hex) ; });函数 findPos(obj) { var curleft = 0, curtop = 0; if (obj.offsetParent) { do { curleft += obj.offsetLeft; curtop += obj.offsetTop; } 而 (obj = obj.offsetParent);返回 { x: curleft, y: curtop }; } 返回未定义; } function rgbToHex(r, g, b) { if (r > 255 || g > 255 || b > 255) throw "无效的颜色分量";返回 ((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)})` }


请您看看这个question,看看是否有解决方案。我将非常感激:)
您使用嵌套的 offsetParent 是一种非常好的方法。我从来没有想过。但是为什么不使用常规的 while 循环而不是 if 然后是 do...while
这个答案今天对我有帮助(2017 年 9 月 1 日)。所以 +1
@AlivetoDie #100!谢谢 :)
C
Caio Vertematti

我知道这是一个老问题,但这里有一个替代方案。我将该图像数据存储在一个数组中,然后在画布上的鼠标移动事件中:

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 要容易得多。


L
Lundin

合并 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>

这是我的完整解决方案。这里我只使用了画布和一张图片,但如果您需要在图片上使用 <map>,也可以。


D
Danziger

如果您需要获取矩形区域的平均颜色,而不是单个像素的颜色,请查看另一个问题:

👉 JavaScript - Get average color from a certain area of an image

无论如何,两者都以非常相似的方式完成:

🔍 从图像或画布中获取单个像素的颜色/值

要获取单个像素的颜色,您首先将该图像绘制到画布上,您已经完成了:

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() 来获取整个图像的值,您可以通过更改其第三个和第四个参数来做到这一点。该函数的签名是:

ImageData ctx.getImageData(sx, sy, sw, sh);

sx:要从中提取 ImageData 的矩形左上角的 x 坐标。

sy:要从中提取 ImageData 的矩形左上角的 y 坐标。

sw:从中提取 ImageData 的矩形的宽度。

sh:从中提取 ImageData 的矩形的高度。

您可以看到它返回一个 ImageData 对象 whatever that is。这里的重要部分是该对象有一个 .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 的组件,即这个假想图像的位置 x = 1, y = 1 上的组件,您会在它的 ImageData' 中找到它的第一个索引 i s 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');常量画笔 = document.getElementById('brush'); const image = document.getElementById('image'); const canvas = document.createElement('canvas');常量上下文 = canvas.getContext('2d');常量宽度 = image.width;常量高度 = image.height;常量 BRUSH_SIZE = Brush.offsetWidth;常量 BRUSH_CENTER = BRUSH_SIZE / 2;常量 MIN_X = image.offsetLeft + 4;常量 MAX_X = MIN_X + 宽度 - 1; const MIN_Y = image.offsetTop + 4;常量 MAX_Y = MIN_Y + 高度 - 1;画布.宽度 = 宽度; canvas.height = 高度; context.drawImage(图像, 0, 0, 宽度, 高度); const imageDataData = context.getImageData(0, 0, width, height).data;函数 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)';});返回; } const imageX = clientX - MIN_X; const imageY = clientY - MIN_Y;常量 i = (imageY * 宽度 + imageX) * 4; // 单个像素 (R, G, B, A) 将在数组中占据 4 个位置: const R = imageDataData[i];常量 G = imageDataData[i + 1];常量 B = imageDataData[i + 2];常量 A = imageDataData[i + 3] / 255;常数 iA = 1 - A; // Alpha 加权颜色: const wR = (R * A + 255 * iA) | 0;常数 wG = (G * A + 255 * iA) | 0;常数 wB = (B * A + 255 * iA) | 0; // 更新 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);样本颜色(MIN_X,MIN_Y);身体{边距:0;高度:100vh;显示:弯曲;弹性方向:行;对齐项目:居中;证明内容:中心;光标:无;字体系列:等宽;溢出:隐藏; } #image { 边框:4px 纯白色;边框半径:2px;盒子阴影:0 0 32px 0 rgba(0, 0, 0, .25);宽度:150px; box-sizing:边框框; } #brush { 位置:绝对;顶部:0;左:0;指针事件:无;宽度:1px;高度:1px;混合混合模式:排除;边界半径:100%; } #brush::before, #brush::after { 内容:'';位置:绝对;背景:洋红色; } #brush::before { 顶部:-16px;左:0;高度:33px;宽度:100%; } #brush::after { 左:-16px;顶部:0;宽度:33px;高度:100%; } #samples { 位置:相对;列表样式:无;填充:0;宽度:250px; } #samples::before { 内容:'';位置:绝对;顶部:0;左:27px;宽度:2px;高度:100%;背景:黑色;边框半径:1px; } #samples > li { 位置:相对;显示:弯曲;弹性方向:列;证明内容:中心;填充左:56px; } #samples > li + li { margin-top: 8px; } .sample { 位置:绝对;最高:50%;左:16px;变换:翻译(0,-50%);显示:块;宽度:24px;高度:24px;边界半径:100%;盒子阴影:0 0 16px 4px rgba(0, 0, 0, .25);右边距:8px; } .sampleLabel { 字体粗细:粗体;边距底部:8px; } .sampleCode { }

  • solidColor
    rgb(0, 0, 0)
  • alphaColor
    rgba(0, 0, 0, 0.00)
  • solidWeighted(带白色)
    rgb(0, 0, 0)

⚠️ 请注意,如果我尝试使用更长的数据 URI,我会使用一个小的数据 URI 来避免 Cross-Origin 问题,如果我包含一个外部图像或一个大于允许的答案。

🕵️ 这些颜色看起来很奇怪,不是吗?

如果您在星号形状的边框周围移动光标,您会看到有时 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),颜色就越浅。


u
user889030

每次调用 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);

D
Damian Pavlica

我有一个从画布获取像素颜色的非常简单的工作示例。

首先是一些基本的 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);  
}

这是一个working example,只需查看控制台即可。


谢谢,这正是我需要的。现在我可以找出饼图的哪一部分被点击了。我将其转换为打字稿。
s
slamborne

快速回答

context.getImageData(x, y, 1, 1).data; 返回一个 rgba 数组。例如[50, 50, 50, 255]

这是@lwburk 的 rgbToHex 函数的一个版本,它将 rgba 数组作为参数。

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

这仅适用于 16 以上的红色值,例如 [10, 42, 67, 255] 生成 #a2a43,它不是有效/格式正确的十六进制颜色代码。
e
emn178

你可以试试color-sampler。这是在画布中选择颜色的一种简单方法。见demo


G
Galen Long

@Wayne Burkett 的 answer 很好。如果您还想提取 alpha 值以获得 rgba 颜色,我们可以这样做:

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

我将 alpha 值除以 255,因为 ImageData 对象将其存储为 0 - 255 之间的整数,但大多数应用程序(例如 CanvasRenderingContext2D.fillRect())要求颜色采用有效的 CSS 格式,其中 alpha 值介于 0 和 1 之间.

(还请记住,如果您提取透明颜色,然后将其绘制回画布上,它将覆盖先前存在的任何颜色。因此,如果您在同一点上绘制颜色 rgba(0,0,0,0.1) 10 次,它将是黑色的。)