Якщо вам потрібно отримати середній колір прямокутної області, а не колір одного пікселя, подивіться на це інше питання:
👉 JavaScript - Отримайте середній колір з певної області зображення
У будь-якому випадку, і те, і інше робиться дуже подібним чином:
Отримання кольору / значення одного пікселя із зображення чи полотна
Щоб отримати колір одного пікселя, ви спочатку намалюєте це зображення на полотні, що ви вже зробили:
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;
Пришвидшення скорочується, отримуючи всі ImageData одночасно
Вам потрібно використовувати той самий CanvasRenderingContext2D.getImageData (), щоб отримати значення всього зображення, що ви робите, змінюючи його третій та четвертий параметри. Підпис цієї функції:
ImageData ctx.getImageData(sx, sy, sw, sh);
sx: Координата x верхнього лівого кута прямокутника, з якого буде витягнуто ImageData.
sy: Координата y верхнього лівого кута прямокутника, з якого буде витягнуто ImageData.
sw: Ширина прямокутника, з якого буде витягнуто ImageData.
sh: Висота прямокутника, з якого буде витягнуто ImageData.
Ви бачите, що він повертає ImageDataоб’єкт, яким би він не був . Важливим тут є те, що цей об’єкт має .dataвластивість, що містить усі наші піксельні значення.
Однак зауважте, що .dataвластивість є одновимірноюUint8ClampedArray , це означає, що всі компоненти пікселя згладжені, тож ви отримуєте щось таке, що виглядає так:
Скажімо, у вас є зображення 2х2, таке:
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цього уявного чином, ви знайдете свій перший індекс iв його ImageData«сек 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;
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>
Зверніть увагу: я використовую невеликий URI даних, щоб уникнути Cross-Origin проблем, якщо я зовнішнє зображення або відповідь, яка перевищує допустиму, якщо я намагаюся використовувати довший URI даних.
🕵️ Ці кольори виглядають дивно, чи не так?
Якщо навести курсор навколо меж фігури зірочки, ви побачите, що інколи avgSolidColorчервоний, але піксель, який вибираєте, виглядає білим. Це тому, що, хоча Rкомпонент для цього пікселя може бути високим, альфа-канал низький, тому колір насправді є майже прозорим червоним відтінком, але avgSolidColorігнорує це.
З іншого боку, avgAlphaColorвиглядає рожевим. Ну, це насправді неправда, це просто виглядає рожевим, тому що зараз ми використовуємо альфа-канал, що робить його напівпрозорим і дозволяє побачити фон сторінки, який у цьому випадку є білим.
Color Альфа-зважений колір
Тоді, що ми можемо зробити, щоб це виправити? Ну, виявляється, нам просто потрібно використовувати альфа-канал та його обернене значення як ваги для обчислення компонентів нашої нової вибірки, в даному випадку об’єднавши його з білим, оскільки це колір, який ми використовуємо як фон.
Це означає, що якщо піксель знаходиться R, G, B, A, де Aзнаходиться інтервал [0, 1], ми обчислимо обернену до альфа-каналу 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), тим світліший колір.