iridescent
Version:
🎨 Color conversion, extraction & utility library
181 lines (160 loc) • 5.26 kB
JavaScript
// Normalizes arguments into object with r,g,b properties.
// Accepts various formats
// - three arguments (r, g, b)
// - single array ([r, g, b])
// - single object {r, g, b}
export function normalizeRgb(...args) {
if (args.length === 3)
var [r, g, b] = args
else if (Array.isArray(args[0]))
var [r, g, b] = args[0]
else if (typeof args[0] === 'string')
var {r, g, b} = hexToRgb(args[0])
else
var {r, g, b} = args[0]
return {r, g, b}
}
// Returns foreground color (white or black) for given color.
export function foreground(...args) {
var r, g, b
if (isDark(...args))
r = g = b = 255
else
r = g = b = 0
return {r, g, b}
}
export function brightness(...args) {
var {r, g, b} = normalizeRgb(...args)
return (r * 299 + g * 587 + b * 114) / 1000
}
// per ITU-R BT.709
export function luminance(...args) {
var {r, g, b} = normalizeRgb(...args)
return (0.2126 * r + 0.7152 * g + 0.0722 * b)
}
// Detects if color is dark and should be used on light backgrounds.
export function isDark(...args) {
return luminance(...args) < 140
}
// Detects if color is light and should be used on dark backgrounds.
export function isLight(...args) {
return !isDark(...args)
}
// Accepts color in any format and simply returns true if it's black (#000; 0,0,0; etc...)
export function isBlack(...args) {
var {r, g, b} = normalizeRgb(...args)
return r === 0 && g === 0 && b === 0
}
// Accepts color in any format and simply returns true if it's white (#FFF; 255,255,255; etc...)
export function isWhite(...args) {
var {r, g, b} = normalizeRgb(...args)
return r === 255 && g === 255 && b === 255
}
export function difference(...args) {
if (args.length === 2) {
var {r: r1, g: g1, b: b1} = normalizeRgb(args[0])
var {r: r2, g: g2, b: b2} = normalizeRgb(args[1])
} else if (args.length === 6) {
var [r1, g1, b1, r2, g2, b2] = args
} else {
throw new Error('Invalid args')
}
var sumOfSquares = 0
sumOfSquares += Math.pow(r1 - r2, 2)
sumOfSquares += Math.pow(g1 - g2, 2)
sumOfSquares += Math.pow(b1 - b2, 2)
return Math.sqrt(sumOfSquares)
}
///////////////////////////////////////////////////////////////////////////
/////////////////////////////// CONVERSIONS ///////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Converts RGB color to HEX format (including # symbol at starts).
export function rgbToHex(...args) {
var {r, g, b} = normalizeRgb(...args)
var int = (r << 16) + (g << 8) + b
return '#' + int.toString(16).padStart(6, '0')
}
export function hexToRgb(string) {
if (string.startsWith('#'))
string = string.slice(1)
if (string.length === 3) {
var r = parseInt(string.substr(0,1).repeat(2), 16)
var g = parseInt(string.substr(1,1).repeat(2), 16)
var b = parseInt(string.substr(2,1).repeat(2), 16)
} else {
var r = parseInt(string.substr(0,2), 16)
var g = parseInt(string.substr(2,2), 16)
var b = parseInt(string.substr(4,2), 16)
}
return {r, g, b}
}
export function rgbToHsl(...args) {
var {r, g, b} = normalizeRgb(...args)
r /= 255
g /= 255
b /= 255
var max = Math.max(r, g, b)
var min = Math.min(r, g, b)
var h
var s
var l = (max + min) / 2
if (max == min) {
h = s = 0 // achromatic
} else {
var d = max - min
s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break
case g: h = (b - r) / d + 2; break
case b: h = (r - g) / d + 4; break
}
h /= 6
}
return [h, s, l]
}
///////////////////////////////////////////////////////////////////////////
//////////////////////////// COLOR EXTRACTION /////////////////////////////
///////////////////////////////////////////////////////////////////////////
// Extracts color from given canvas by summing and averaging all pixels
// Precision modifies how many pixels are used.
// 1 = every pixel, 2 = every other pixel, 10 = only every tenth pixel (10x speedup, 10% accuracy).
export function accentAverage(canvasOrImg, precision = 1) {
var data = getCanvasData(canvasOrImg)
var output = {r: 0, g: 0, b: 0}
var count = 0
var step = 4 * precision
for (var i = 0; i < data.length; i += step) {
let alpha = data[i + 3]
// ignore transparent alpha
if (data[i+3] === 0) continue
let r = data[i]
let g = data[i + 1]
let b = data[i + 2]
// ignore black
if (r === 0 && g === 0 && b === 0) continue
// ignore white
if (r === 255 && g === 255 && b === 255) continue
++count
output.r += r
output.g += g
output.b += b
}
// ~~ used to floor values
output.r = ~~(output.r / count)
output.g = ~~(output.g / count)
output.b = ~~(output.b / count)
return output
}
export function getCanvasData(canvasOrImg) {
if (canvasOrImg instanceof Image) {
var img = canvasOrImg
var canvas = document.createElement('canvas')
var context = canvas.getContext('2d')
context.drawImage(img, 0, 0, img.naturalWidth, img.naturalHeight)
} else {
var canvas = canvasOrImg
var context = canvas.getContext('2d')
}
var {data} = context.getImageData(0, 0, canvas.width, canvas.height)
return data
}