Extract colors
by Marc Malignan
HTML
<div id="main">
<form id="form">
<div class="form-row withLabel">
<label for="detail">Detail</label>
<input id="detail" type="number" value="1000" />
<span class="helper">Number of pixels to analyze</span>
</div>
<div class="form-row withLabel">
<label for="nbColors">Nb colors</label>
<input id="nbColors" type="number" value="5" />
<span class="helper">Number of results</span>
</div>
<div class="form-row">
<input id="upload" type='file'>
<img id="image" src="" />
</div>
<button type="submit" disabled>Analyze</button>
</form>
<div id="results"></div>
</div>
CSS
* {
box-sizing: border-box;
}
body,
html {
margin: 0;
height: 100%;
}
body {
background: linear-gradient(#333, #222);
}
#main {
position: absolute;
left: 50%;
top: 50%;
width: calc((70px * 5) + (5px * 10));
transform: translate(-50%, -50%);
color: #eee;
text-align: center;
font-family: Arial, sans-serif;
}
#form,
.form-row {
margin-bottom: 10px;
}
.form-row.withLabel {
display: flex;
align-items: center;
}
.form-row.withLabel>label {
flex-shrink: 0;
width: calc(100% / 3);
padding-right: 10px;
text-align: right;
}
.form-row input[type=number] {
flex-shrink: 0;
width: 70px;
}
.form-row .helper {
padding-left: 10px;
font-size: 0.8rem;
opacity: 0.5;
text-align: left;
}
#image {
display: none;
width: 100%;
max-width: 200px;
margin: 10px auto 0;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
}
#results {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin: 0 auto;
}
.result {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 70px;
height: 70px;
margin: 5px;
border-radius: 50%;
font-family: monospace;
font-size: 0.9rem;
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.3);
}
JavaScript
const imgEl = document.getElementById('image');
const uploadEl = document.getElementById('upload');
const formEl = document.getElementById('form');
const resultsEl = document.getElementById('results');
const detailEl = document.getElementById('detail');
const nbColorsEl = document.getElementById('nbColors');
const createCanvas = img => {
const canvas = document.createElement('canvas');
canvas.width = img.naturalWidth;
canvas.height = img.naturalHeight;
canvas.getContext('2d').drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight);
return canvas;
}
const getColors = (canvas, detail) => {
const colors = [];
let total = 0;
const nbPixels = canvas.width * canvas.height;
const analyzeSpacing = detail < nbPixels ? Math.floor(nbPixels / detail) : 1;
let index = 0;
for (let y = 0; y < canvas.height; y++) {
for (let x = 0; x < canvas.width; x++) {
index++;
if (index % analyzeSpacing === 0) {
const pixel = canvas.getContext('2d').getImageData(x, y, 1, 1).data;
const rgb = pixel.slice(0, 3).join();
const colorIndex = colors.findIndex(color => color.rgb === rgb);
if (colorIndex > -1) {
colors[colorIndex].nb++;
} else {
colors.push({
rgb,
nb: 1
});
}
total++;
}
}
}
return colors.sort((colorA, colorB) => colorB.nb - colorA.nb);
}
const findClosestColor = (color, colors) => {
const colorRgb = color.rgb.split(',');
return [...colors].reduce((prev, curr) => {
const prevRgb = prev.rgb.split(',');
const currRgb = curr.rgb.split(',');
return (
Math.abs(currRgb[0] - colorRgb[0]) < Math.abs(prevRgb[0] - colorRgb[0]) &&
Math.abs(currRgb[1] - colorRgb[1]) < Math.abs(prevRgb[1] - colorRgb[1]) &&
Math.abs(currRgb[2] - colorRgb[2]) < Math.abs(prevRgb[2] - colorRgb[2])
) ? curr : prev;
});
}
const approximateColors = (colors, nbColors) => {
if (colors.length <= nbColors) return...