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;
🚀 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 ImageData
nesne 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 getImageData
yavaş 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 PIXEL
olduğunu, pozisyonda bir x = 1, y = 1
bu hayali görüntünün, onun ilk indeksi bulur i
onun içinde ImageData
'ın data
olarak 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;
const R = imageDataData[i];
const G = imageDataData[i + 1];
const B = imageDataData[i + 2];
const A = imageDataData[i + 3] / 255;
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;
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-Origin
Harici 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 avgSolidColor
kı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 avgSolidColor
görmezden gelmesidir.
Öte yandan avgAlphaColor
pembe 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
, A
aralığın neresinde [0, 1]
olursa, alfa kanalının tersini iA
ve 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 ( A
0'a yakınsa) rengin o kadar açık olduğuna dikkat edin.