Fare ile tuvalden piksel rengi alın


85

RGB değeri pikselini farenin altında almak mümkün müdür? Bunun tam bir örneği var mı? Şimdiye kadar sahip olduğum şeyler:

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

Yanıtlar:


150

İşte eksiksiz, bağımsız bir örnek. İlk önce aşağıdaki HTML'yi kullanın:

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

Ardından tuval üzerine rastgele arka plan renkleriyle bazı kareler koyun:

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

Ve fareyle üzerine gelindiğinde her rengi yazdırın:

$('#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);
});

Yukarıdaki kod, jQuery'nin ve aşağıdaki yardımcı program işlevlerinin varlığını varsayar:

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

Burada iş başında görün:


Lütfen bu soruya bakıp bunun için bir çözüm olup olmadığına bakar mısınız ? Bunu çok takdir
edeceğim

İç içe geçmiş offsetParents kullanımınız, bunun için gerçekten güzel bir yoldur. Bunu hiç düşünmedim. Ama neden whileönce ifve sonra a yerine normal bir döngü kullanmıyorsunuz do...while?
Jonathan Lam

bu cevap bugün bana yardımcı oluyor (1-Eylül-2017). so +1
Alive to Die

@AlivetoDie # 100! Teşekkürler :)
Wayne

12

Bunun eski bir soru olduğunu biliyorum, ama işte bir alternatif. Bu görüntü verilerini bir dizide ve ardından tuval üzerinde fare hareketi olayında saklardım:

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]

Her seferinde imageData'yı almaktan çok daha kolay.


7

Burada StackOverflow'da (yukarıdaki makale dahil) ve diğer sitelerde bulunan çeşitli referansları birleştirerek, bunu javascript ve JQuery kullanarak yaptım:

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

Bu benim tam çözümüm. Burada sadece tuval ve bir resim kullandım, ancak <map>resmin üzerine kullanmanız gerekirse , bu da mümkün.


5

getImageData'yı her seferinde çağırmak süreci yavaşlatır ... işleri hızlandırmak için görüntü verilerini depolamanızı öneririm ve sonra kolayca ve hızlı bir şekilde pix değeri elde edebilirsiniz, bu nedenle daha iyi performans için böyle bir şey yapın

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

Çok yararlı +1
Wayne

4

Tek bir pikselin rengi yerine dikdörtgen bir alanın ortalama rengini almanız gerekiyorsa, lütfen şu diğer soruya bir göz atın:

👉 JavaScript - Bir görüntünün belirli bir alanından ortalama renk alın

Her neyse, ikisi de çok benzer şekilde yapılır:

🔍 Tek Bir Pikselin Rengini / Değerini Bir Görüntüden veya Kanvastan Alma

Tek bir pikselin rengini elde etmek için, önce o görüntüyü daha önce yapmış olduğunuz bir tuvale çizmelisiniz:

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

Ve sonra aşağıdaki gibi tek bir pikselin değerini alın:

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

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

🚀 Tüm Görüntü Verilerini Tek Seferde Toplayarak Hızlandırın

Üçüncü ve dördüncü parametrelerini değiştirerek yaptığınız tüm görüntünün değerlerini almak için aynı CanvasRenderingContext2D.getImageData () öğesini kullanmanız gerekir . Bu işlevin imzası:

ImageData ctx.getImageData(sx, sy, sw, sh);
  • sx: ImageData'nın çıkarılacağı dikdörtgenin sol üst köşesinin x koordinatı.
  • sy: ImageData'nın çıkarılacağı dikdörtgenin sol üst köşesinin y koordinatı.
  • sw: ImageData'nın çıkarılacağı dikdörtgenin genişliği.
  • sh: ImageData'nın çıkarılacağı dikdörtgenin yüksekliği.

Her ne ise, bir ImageDatanesne döndürdüğünü görebilirsiniz . Buradaki önemli kısım, o nesnenin bir.data tüm piksel değerlerimizi içeren özelliğe sahip olmasıdır.

Ancak, .dataözelliğin 1 boyutlu olduğunu Uint8ClampedArray, yani pikselin tüm bileşenlerinin düzleştirilmiş olduğu anlamına gelir, bu nedenle şuna benzer bir şey elde edersiniz:

Diyelim ki böyle bir 2x2 resminiz var:

 RED PIXEL |       GREEN PIXEL
BLUE PIXEL | TRANSPARENT PIXEL

Sonra, onları şu şekilde alacaksınız:

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

Çağrı olarak getImageDatayavaş bir işlemdir, tüm resmi (veri almak için sadece bir kez çağırabilir sw= görüntü genişliği, sh= görüntü yükseklik).

Eğer bileşenlerini erişmek istiyorsanız Sonra, yukarıdaki örnekte, TRANSPARENT PIXELolduğunu, pozisyonda bir x = 1, y = 1bu hayali görüntünün, onun ilk indeksi bulur ionun içinde ImageData'ın dataolarak mülkiyet:

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

✨ Eylemde Görelim

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>

⚠️ Not Cross-OriginHarici bir görüntü veya daha uzun bir veri URI'si kullanmaya çalıştığımda izin verilenden daha büyük bir yanıt eklersem sorunları önlemek için küçük bir veri URI kullanıyorum.

🕵️ Bu renkler tuhaf görünüyor, değil mi?

İmleci yıldız şeklinin sınırları etrafında hareket ettirirseniz, bazen avgSolidColorkırmızı olduğunu, ancak örneklediğiniz pikselin beyaz göründüğünü göreceksiniz. Bunun nedeni R, o pikselin bileşeni yüksek olsa bile alfa kanalının düşük olması, dolayısıyla rengin aslında kırmızının neredeyse saydam bir tonu olması, ancak bunu avgSolidColorgörmezden gelmesidir.

Öte yandan avgAlphaColorpembe görünüyor. Aslında bu doğru değil, sadece pembe görünüyor çünkü şimdi alfa kanalını kullanıyoruz, bu onu yarı saydam yapar ve sayfanın arka planını görmemizi sağlar, bu durumda bu durumda beyazdır.

🎨 Alfa ağırlıklı renk

O halde bunu düzeltmek için ne yapabiliriz? Görünüşe göre, yeni örneğimizin bileşenlerini hesaplamak için ağırlık olarak alfa kanalını ve tersini kullanmamız gerekiyor, bu durumda onu beyazla birleştiriyoruz, çünkü arka plan olarak kullandığımız renk bu.

Bu, bir piksel ise R, G, B, A, Aaralığın neresinde [0, 1]olursa, alfa kanalının tersini iAve ağırlıklı örneğin bileşenlerini şu şekilde hesaplayacağımız anlamına gelir :

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;

Bir piksel ne kadar şeffafsa ( A0'a yakınsa) rengin o kadar açık olduğuna dikkat edin.


3

Hızlı cevap

context.getImageData(x, y, 1, 1).data;bir rgba dizisi döndürür. Örneğin[50, 50, 50, 255]


Burada, rgba dizisini bağımsız değişken olarak alan @ lwburk'un rgbToHex işlevinin bir sürümü var.

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

Bu yalnızca 16'nın üzerindeki kırmızı değerlerle çalışır, örneğin geçerli / iyi biçimlendirilmiş onaltılık renk kodu olmayan [10, 42, 67, 255]üretir #a2a43.
Ti Hausmann


1

Tuvalden piksel rengi elde etmek için çok basit bir çalışma örneğim var.

Önce bazı temel HTML:

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

Ardından JS'nin tuval üzerine bir şeyler çizmesi ve renk elde etmesi için:

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

İşte çalışan bir örnek , sadece konsola bakın.


0

@Wayne Burkett'in cevabı güzel. Bir rgba rengi elde etmek için alfa değerini de çıkarmak isterseniz, bunu yapabiliriz:

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

Alfa değerini 255'e böldüm çünkü ImageData nesnesi bunu 0 - 255 arasında bir tam sayı olarak saklar, ancak çoğu uygulama (örneğin CanvasRenderingContext2D.fillRect()) renklerin alfa değerinin 0 ile 1 arasında olduğu geçerli CSS formatında olmasını gerektirir.

(Ayrıca, şeffaf bir rengi çıkarırsanız ve sonra onu tuval üzerine geri çekerseniz, daha önce orada olan rengi kaplayacağını unutmayın. Yani rengi rgba(0,0,0,0.1)aynı noktanın üzerine 10 kez çizerseniz , siyah olur.)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.