A Pen by Josh Iverson on CodePen.
Created
April 25, 2022 18:21
-
-
Save jiverson/f62fb5e910de0abb5dbb52ca0441939b to your computer and use it in GitHub Desktop.
Sorting colors example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<h1>Sorting colors</h1> | |
<p>Example from <a href="https://tomekdev.com/posts/sorting-colors-in-js">https://tomekdev.com/posts/sorting-colors-in-js</a>. Go there to learn more about sorting colors.</p> | |
<section class="box"> | |
<h2>Colors unsorted</h2> | |
<ul id="unsorted"></ul> | |
</section> | |
<section class="box"> | |
<h2>Colors sorted</h2> | |
<ul id="sorted"></ul> | |
</section> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import colorUtil from "https://cdn.skypack.dev/[email protected]"; | |
const colors = [ | |
'#ffffff', | |
'#009cd4', | |
'#505e75', | |
'rgba(0,0,0,0.1)', | |
'#606a79', | |
'rgba(0,0,0,0.2)', | |
'#1e2837', | |
'#e3e6e9', | |
'#969eac', | |
'#f02913', | |
'#eeeeee', | |
'#555e74', | |
'#000000', | |
'#edeef1', | |
'#02bd00', | |
'#eae7e6', | |
'#e3e3e3', | |
'#f4f5f6', | |
'#e6e6e6', | |
'rgba(240,41,19,0.05)', | |
'#efeeed', | |
'#209bd0', | |
'#1f2837', | |
'#999999', | |
'rgba(0,156,212,0.05)', | |
'rgba(63,78,90,0.11)', | |
'rgba(80,94,117,0.1)', | |
'#868686', | |
'#e8e4e1', | |
'#fb7c00', | |
'#485f79', | |
'#e5f1f6', | |
'#6e4888', | |
'#b9b9b9', | |
'#e0e2e8', | |
'rgba(232,228,225,0.5)', | |
'#f0f0f0', | |
'#eaebee', | |
'#656d78', | |
'rgba(96,106,121,0.8)', | |
'#ee0b0b', | |
'#b3b3b3', | |
'rgba(83,83,83,0.2)', | |
'#f9f9f9', | |
'rgba(80,94,117,0.5)', | |
'rgba(255,255,255,0.1)', | |
'#e9e9e9', | |
'#f9f8f8', | |
'#ff3ea8', | |
'rgba(136,183,213,0)', | |
'#dddddd', | |
'#e0e0e0', | |
'#c0c0c0', | |
'#eef7fa', | |
'#f5f4f3', | |
'rgba(96,106,121,0.7)', | |
'#adacac', | |
'#e1e4e7', | |
'#dadada', | |
'#8891a7', | |
'rgba(0,0,0,0.05)', | |
'#fcfcfc', | |
'#dcdcdc', | |
'#535e73', | |
'rgba(80,94,117,0.3)', | |
'#9e9e9e', | |
'#d4cfcf', | |
'#f8d200', | |
'rgba(194,225,245,0)', | |
'#ffff00', | |
'#928f8f', | |
'rgba(0,0,0,0.5)', | |
'rgba(0,156,212,0.2)', | |
'#0295f7', | |
'#5d99d0', | |
'rgba(96,106,121,0.2)', | |
'rgba(255,255,255,0.44)', | |
'#dee0e2', | |
'#c0c9d1', | |
'#48cd35', | |
'#5897fb', | |
'#e4e4e4', | |
'#333333', | |
'#f7f6f6', | |
'#acb1b5', | |
'#e8e8e8', | |
'#ff5f57', | |
'#ffbe2f', | |
'#28ca42', | |
'#c9c9c9', | |
'#cccccc', | |
'#f3f4f5', | |
'#e90e11', | |
'#8b5ca9', | |
'#a9a9a9', | |
'#f2fafd', | |
'#73468b', | |
'#6b7897', | |
'rgba(81,95,118,0.5)', | |
'rgba(136,136,136,0.47)', | |
'#dfdfdf', | |
'rgba(158,158,158,0.2)', | |
'rgba(2,189,0,0.2)', | |
'#e2f2f7', | |
'rgba(251,124,0,0.2)', | |
'#616e82', | |
'#1189ca', | |
'#171e2a', | |
'rgba(244,245,246,0.5)', | |
'rgba(0,0,0,0.3)', | |
'#e5f5fa', | |
'#ffbc49', | |
'#b8b8b8', | |
'#c8c8c8', | |
'#e5f5fb', | |
'rgba(150,158,172,0.1)', | |
'#949ead', | |
'#59d2fb', | |
'#00a9e7', | |
'#f7f7f7', | |
'#d9dce2', | |
'#ecebeb', | |
'rgba(0,0,0,0.13)', | |
'rgba(101,116,139,0.07)', | |
'#a8aeb9', | |
'rgba(30,40,55,0.5)', | |
'#09aae8', | |
'#713996', | |
'#fbfbfb', | |
'#5ad1fc', | |
'#4a4a4a', | |
'#e9edf0', | |
'#7ec0ee', | |
'#f4d309', | |
]; | |
function renderColors(colors, listName) { | |
let list = document.createDocumentFragment(); | |
for (let i = 0, len = colors.length; i < len; i++) { | |
let el = document.createElement('li'); | |
el.style.backgroundColor = colors[i]; | |
list.appendChild(el); | |
} | |
document.querySelector(listName).appendChild(list); | |
} | |
renderColors(colors, '#unsorted'); | |
// Sorting | |
function blendRgbaWithWhite(rgba) { | |
const color = colorUtil.color(rgba); | |
const a = color.rgb.a / 255; | |
const r = Math.floor(color.rgb.r * a + 0xff * (1 - a)); | |
const g = Math.floor(color.rgb.g * a + 0xff * (1 - a)); | |
const b = Math.floor(color.rgb.b * a + 0xff * (1 - a)); | |
return '#' + ((r << 16) | (g << 8) | b).toString(16); | |
} | |
function colorDistance(color1, color2) { | |
const x = | |
Math.pow(color1[0] - color2[0], 2) + | |
Math.pow(color1[1] - color2[1], 2) + | |
Math.pow(color1[2] - color2[2], 2); | |
return Math.sqrt(x); | |
} | |
const clusters = [ | |
{ name: 'red', leadColor: [255, 0, 0], colors: [] }, | |
{ name: 'orange', leadColor: [255, 128, 0], colors: [] }, | |
{ name: 'yellow', leadColor: [255, 255, 0], colors: [] }, | |
{ name: 'chartreuse', leadColor: [128, 255, 0], colors: [] }, | |
{ name: 'green', leadColor: [0, 255, 0], colors: [] }, | |
{ name: 'spring green', leadColor: [0, 255, 128], colors: [] }, | |
{ name: 'cyan', leadColor: [0, 255, 255], colors: [] }, | |
{ name: 'azure', leadColor: [0, 127, 255], colors: [] }, | |
{ name: 'blue', leadColor: [0, 0, 255], colors: [] }, | |
{ name: 'violet', leadColor: [127, 0, 255], colors: [] }, | |
{ name: 'magenta', leadColor: [255, 0, 255], colors: [] }, | |
{ name: 'rose', leadColor: [255, 0, 128], colors: [] }, | |
{ name: 'black', leadColor: [0, 0, 0], colors: [] }, | |
{ name: 'grey', leadColor: [235, 235, 235], colors: [] }, | |
{ name: 'white', leadColor: [255, 255, 255], colors: [] }, | |
]; | |
function oneDimensionSorting(colors, dim) { | |
return colors | |
.sort((colorA, colorB) => { | |
if (colorA.hsl[dim] < colorB.hsl[dim]) { | |
return -1; | |
} else if (colorA.hsl[dim] > colorB.hsl[dim]) { | |
return 1; | |
} else { | |
return 0; | |
} | |
}); | |
} | |
function sortWithClusters(colorsToSort) { | |
const mappedColors = colorsToSort | |
.map((color) => { | |
const isRgba = color.includes('rgba'); | |
if (isRgba) { | |
return blendRgbaWithWhite(color); | |
} else { | |
return color; | |
} | |
}) | |
.map(colorUtil.color); | |
mappedColors.forEach((color) => { | |
let minDistance; | |
let minDistanceClusterIndex; | |
clusters.forEach((cluster, clusterIndex) => { | |
const colorRgbArr = [color.rgb.r, color.rgb.g, color.rgb.b]; | |
const distance = colorDistance(colorRgbArr, cluster.leadColor); | |
if (typeof minDistance === 'undefined' || minDistance > distance) { | |
minDistance = distance; | |
minDistanceClusterIndex = clusterIndex; | |
} | |
}); | |
clusters[minDistanceClusterIndex].colors.push(color); | |
}); | |
clusters.forEach((cluster) => { | |
const dim = ['white', 'grey', 'black'].includes(cluster.name) ? 'l' : 's'; | |
cluster.colors = oneDimensionSorting(cluster.colors, dim) | |
}); | |
return clusters; | |
} | |
const sortedClusters = sortWithClusters(colors); | |
const sortedColors = sortedClusters.reduce((acc, curr) => { | |
const colors = curr.colors.map((color) => color.hex); | |
return [...acc, ...colors]; | |
}, []); | |
renderColors(sortedColors, '#sorted'); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ul { | |
list-style-type: none; | |
margin: 0; | |
overflow: hidden; | |
padding: 0.5rem 0; | |
} | |
li { | |
display: block; | |
float: left; | |
width: 30px; | |
height: 30px; | |
border: 1px solid #ccc; | |
border-radius: 0 0 6px 0; | |
font-size: 10px; | |
margin: 4px; | |
word-break: break-word; | |
padding: 4px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment