1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677 |
- 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
- */
- 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;
- }
- contrast.params = ['top', 'bottom'];
- module.exports = contrast;
|