Created
October 18, 2018 06:36
-
-
Save aristath/8071b84e333d57089a361ec3956b58e8 to your computer and use it in GitHub Desktop.
This file contains 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
var Colour = function( rgba ) { | |
var rgbaString = ''; | |
if ( 'transparent' === rgba ) { | |
rgba = [ 0, 0, 0, 0 ]; | |
} else if ( 'string' === typeof rgba ) { | |
rgbaString = rgba; | |
rgba = rgbaString.match( /rgba?\(([\d.]+), ([\d.]+), ([\d.]+)(?:, ([\d.]+))?\)/ ); | |
if ( rgba ) { | |
rgba.shift(); | |
} else { | |
throw new Error( 'Invalid string: ' + rgbaString ); | |
} | |
} | |
rgba[3] = ( undefined === rgba[3] ) ? 1 : rgba[3]; | |
rgba = rgba.map( function( a ) { | |
return floor( a, 3 ); | |
} ); | |
this.rgba = rgba; | |
}; | |
Colour.prototype = { | |
get rgb() { | |
return this.rgba.slice( 0, 3 ); | |
}, | |
get alpha() { | |
return this.rgba[3]; | |
}, | |
set alpha( alpha ) { | |
this.rgba[3] = alpha; | |
}, | |
get luminance() { | |
// Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef | |
var rgba = this.rgba.slice(), | |
rgb, | |
i; | |
for ( i = 0; 3 > i; i++ ) { | |
rgb = rgba[i]; | |
rgb /= 255; | |
rgb = 0.03928 > rgb ? rgb / 12.92 : Math.pow( ( rgb + 0.055 ) / 1.055, 2.4 ); | |
rgba[i] = rgb; | |
} | |
return 0.2126 * rgba[0] + 0.7152 * rgba[1] + 0.0722 * rgba[2]; | |
}, | |
get inverse() { | |
return new Colour( [ | |
255 - this.rgba[0], | |
255 - this.rgba[1], | |
255 - this.rgba[2], | |
this.alpha | |
] ); | |
}, | |
toString: function() { | |
return 'rgb' + ( 1 > this.alpha ? 'a' : '' ) + '(' + this.rgba.slice( 0, 1 >= this.alpha ? 3 : 4 ).join( ', ' ) + ')'; | |
}, | |
clone: function() { | |
return new Colour( this.rgba ); | |
}, | |
overlayOn: function( color ) { | |
var overlaid = this.clone(), | |
alpha = this.alpha, | |
i; | |
if ( 1 <= alpha ) { | |
return overlaid; | |
} | |
for ( i = 0; 3 > i; i++ ) { | |
overlaid.rgba[ i ] = overlaid.rgba[ i ] * alpha + color.rgba[ i ] * color.rgba[3] * ( 1 - alpha ); | |
} | |
overlaid.rgba[3] = alpha + color.rgba[3] * ( 1 - alpha ); | |
return overlaid; | |
}, | |
contrast: function( color ) { | |
// Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef | |
var alpha = this.alpha, | |
l1, | |
l2, | |
ratio, | |
onBlack, | |
onWhite, | |
contrastOnBlack, | |
contrastOnWhite, | |
max, | |
min, | |
closest; | |
if ( 1 <= alpha ) { | |
if ( 1 > color.alpha ) { | |
color = color.overlayOn( this ); | |
} | |
l1 = this.luminance + 0.05; | |
l2 = color.luminance + 0.05; | |
ratio = l1 / l2; | |
if ( l2 > l1 ) { | |
ratio = 1 / ratio; | |
} | |
ratio = floor( ratio, 2 ); | |
return { | |
ratio: ratio, | |
error: 0, | |
min: ratio, | |
max: ratio | |
}; | |
} | |
// If we’re here, it means we have a semi-transparent background | |
// The text color may or may not be semi-transparent, but that doesn't matter | |
onBlack = this.overlayOn( Colour.BLACK ); | |
onWhite = this.overlayOn( Colour.WHITE ); | |
contrastOnBlack = onBlack.contrast( color ).ratio; | |
contrastOnWhite = onWhite.contrast( color ).ratio; | |
max = Math.max( contrastOnBlack, contrastOnWhite ); | |
// This is here for backwards compatibility and not used to calculate | |
// `min`. Note that there may be other colors with a closer luminance to | |
// `color` if they have a different hue than `this`. | |
closest = this.rgb.map( function( c, i ) { | |
return Math.min( Math.max( 0, ( color.rgb[i] - c * alpha ) / ( 1 - alpha ) ), 255 ); | |
} ); | |
closest = new Colour( closest ); | |
min = 1; | |
if ( onBlack.luminance > color.luminance ) { | |
min = contrastOnBlack; | |
} else if ( onWhite.luminance < color.luminance ) { | |
min = contrastOnWhite; | |
} | |
return { | |
ratio: floor( ( min + max ) / 2, 2 ), | |
error: floor( ( max - min ) / 2, 2 ), | |
min: min, | |
max: max, | |
closest: closest, | |
farthest: onWhite === max ? Colour.WHITE : Colour.BLACK | |
}; | |
} | |
}; | |
Colour.BLACK = new Colour( [ 0, 0, 0 ] ); | |
Colour.GRAY = new Colour( [ 127.5, 127.5, 127.5 ] ); | |
Colour.WHITE = new Colour( [ 255, 255, 255 ] ); | |
// Math.floor with precision | |
function floor( number, decimals ) { | |
var multiplier; | |
decimals = +decimals || 0; | |
multiplier = Math.pow( 10, decimals ); | |
return Math.floor( number * multiplier ) / multiplier; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment