You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
75 lines
2.3 KiB
75 lines
2.3 KiB
var utils = require('../utils') |
|
, nodes = require('../nodes') |
|
, blend = require('./blend') |
|
, luminosity = require('./luminosity'); |
|
|
|
/** |
|
* Returns the contrast ratio object between `top` and `bottom` colors, |
|
* based on http://leaverou.github.io/contrast-ratio/ |
|
* and https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js#L108 |
|
* |
|
* Examples: |
|
* |
|
* contrast(#000, #fff).ratio |
|
* => 21 |
|
* |
|
* contrast(#000, rgba(#FFF, 0.5)) |
|
* => { "ratio": "13.15;", "error": "7.85", "min": "5.3", "max": "21" } |
|
* |
|
* @param {RGBA|HSLA} top |
|
* @param {RGBA|HSLA} [bottom=#fff] |
|
* @return {Object} |
|
* @api public |
|
*/ |
|
|
|
module.exports = function contrast(top, bottom){ |
|
if ('rgba' != top.nodeName && 'hsla' != top.nodeName) { |
|
return new nodes.Literal('contrast(' + (top.isNull ? '' : top.toString()) + ')'); |
|
} |
|
var result = new nodes.Object(); |
|
top = top.rgba; |
|
bottom = bottom || new nodes.RGBA(255, 255, 255, 1); |
|
utils.assertColor(bottom); |
|
bottom = bottom.rgba; |
|
function contrast(top, bottom) { |
|
if (1 > top.a) { |
|
top = blend(top, bottom); |
|
} |
|
var l1 = luminosity(bottom).val + 0.05 |
|
, l2 = luminosity(top).val + 0.05 |
|
, ratio = l1 / l2; |
|
|
|
if (l2 > l1) { |
|
ratio = 1 / ratio; |
|
} |
|
return Math.round(ratio * 10) / 10; |
|
} |
|
|
|
if (1 <= bottom.a) { |
|
var resultRatio = new nodes.Unit(contrast(top, bottom)); |
|
result.set('ratio', resultRatio); |
|
result.set('error', new nodes.Unit(0)); |
|
result.set('min', resultRatio); |
|
result.set('max', resultRatio); |
|
} else { |
|
var onBlack = contrast(top, blend(bottom, new nodes.RGBA(0, 0, 0, 1))) |
|
, onWhite = contrast(top, blend(bottom, new nodes.RGBA(255, 255, 255, 1))) |
|
, max = Math.max(onBlack, onWhite); |
|
function processChannel(topChannel, bottomChannel) { |
|
return Math.min(Math.max(0, (topChannel - bottomChannel * bottom.a) / (1 - bottom.a)), 255); |
|
} |
|
var closest = new nodes.RGBA( |
|
processChannel(top.r, bottom.r), |
|
processChannel(top.g, bottom.g), |
|
processChannel(top.b, bottom.b), |
|
1 |
|
); |
|
var min = contrast(top, blend(bottom, closest)); |
|
|
|
result.set('ratio', new nodes.Unit(Math.round((min + max) * 50) / 100)); |
|
result.set('error', new nodes.Unit(Math.round((max - min) * 50) / 100)); |
|
result.set('min', new nodes.Unit(min)); |
|
result.set('max', new nodes.Unit(max)); |
|
} |
|
return result; |
|
}
|
|
|