Blame view

node_modules/stylus/lib/functions/contrast.js 2.33 KB
ce4c83ff   wxy   初始提交
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
  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;
  }