2018/12/21

任意の背景色に対して読みやすい文字色を取得するSass functionを実装する

@ktsnさんが興味深い記事を公開されていた。
WCAG(Web Content Accessibility Guidelines)で定義されているコントラスト比の計算方法を用い、任意の背景色に対して読みやすい文字色を選択する方法だ。

詳しくは@ktsnさんの記事を読んでほしい。
この記事ではJavaScriptを用いて文字色を選択していたのだが、Sassの関数として定義できればかなり便利なのでは?と思い実装してみた。

背景色に対して読みやすい文字色を取得する関数


Sassで実装するにあたり躓いた点は、Sassには数学関数がほとんどないことだ。
幸いコントラスト比を求める計算式はそこまで難しくないので四則演算だけで行けるのだが、小数を含む「べき乗」の計算が問題だ。

ググってみるとmathsassというSass用の数学関数ライブラリがあったので、そこから必要な部分だけ借用させていただいた。
//-------------------------------------
// べき乗するための関数群
// https://github.com/terkel/mathsass
//-------------------------------------
$LN2:   0.6931471805599453;
$SQRT2: 1.4142135623730951;

@function frexp ($x) {
    $exp: 0;
    @if $x < 0 {
        $x: $x * -1;
    }
    @if $x < 0.5 {
        @while $x < 0.5 {
            $x: $x * 2;
            $exp: $exp - 1;
        }
    } @else if $x >= 1 {
        @while $x >= 1 {
            $x: $x / 2;
            $exp: $exp + 1;
        }
    }
    @return $x, $exp;
}

@function ldexp ($x, $exp) {
    $b: if($exp >= 0, 2, 1 / 2);
    @if $exp < 0 {
        $exp: $exp * -1;
    }
    @while $exp > 0 {
        @if $exp % 2 == 1 {
            $x: $x * $b;
        }
        $b: $b * $b;
        $exp: floor($exp * 0.5);
    }
    @return $x;
}

@function log ($x, $b: null) {
    @if $b != null {
        @return log($x) / log($b);
    }

    @if $x <= 0 {
        @return 0 / 0;
    }
    $k: nth(frexp($x / $SQRT2), 2);
    $x: $x / ldexp(1, $k);

    @return $LN2 * $k + _log($x);
}

@function _log ($x) {
    $x: ($x - 1) / ($x + 1);
    $x2: $x * $x;
    $i: 1;
    $s: $x;
    $sp: null;
    @while $sp != $s {
        $x: $x * $x2;
        $i: $i + 2;
        $sp: $s;
        $s: $s + $x / $i;
    }
    @return 2 * $s;
}

@function pow ($base, $exp) {
    @if $exp == floor($exp) {
        $r: 1;
        $s: 0;
        @if $exp < 0 {
            $exp: $exp * -1;
            $s: 1;
        }
        @while $exp > 0 {
            @if $exp % 2 == 1 {
                $r: $r * $base;
            }
            $exp: floor($exp * 0.5);
            $base: $base * $base;
        }
        @return if($s != 0, 1 / $r, $r);
    } @else if $base == 0 and $exp > 0 {
        @return 0;
    } @else {
        $expint: floor($exp);
        $r1: pow($base, $expint);
        $r2: _exp(log($base) * ($exp - $expint));
        @return $r1 * $r2;
    }
}

@function _exp ($x) {
    $ret: 0;
    $i: 1;
    @for $n from 0 to 24 {
        $ret: $ret + $i;
        $i: $i * $x / ($n + 1);
    }
    @return $ret;
}


//-------------------------------------
// 背景色に対して読みやすい文字色を選択する
//-------------------------------------
$white: #fff;
$black: #000;

@function getLuminance($color) {
  $i: $color / 255;
  @if $i <= 0.03828 {
    @return $i / 12.92;
  } @else {
    $_i: ($i + 0.0055) / 1.055;
    @return pow($_i, 2.4);
  }
}
@function getTextColor($background-color) {
  $r: getLuminance(red($background-color));
  $g: getLuminance(green($background-color));
  $b: getLuminance(blue($background-color));
  $l: 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;

  $Lw: 1;
  $Lb: 0;

  $Cw: ($Lw + 0.05) / ($l + 0.05);
  $Cb: ($l + 0.05) / ($Lb + 0.05);

  @if ($Cw < $Cb) {
    @return $black;
  } @else {
    @return $white;
  }
}


.item {
  $colors: (
    black, silver, gray, white,
    maroon, red, purple, fuchsia,
    green, lime, olive, yellow,
    navy, blue, teal, aqua
  );
  @each $color in $colors {
    &.#{$color} {
      background-color: $color;
      // 背景色に対して文字色を取得する
      color: getTextColor($color);
    }
  }
  
  display: inline-block;
  padding: 0.5rem 1rem;
  margin: 0.2rem;
  width: 100px;
  text-align: center;
}

前半部分が小数を含むべき乗計算用の関数で、後半が実際に文字色を取得する部分だ。Sassをコンパイルするときに文字色が静的に決まるのでブラウザへの負荷もない。
かなり使える関数なので、ぜひ使ってみていただきたい。



参考サイト




以上

written by @bc_rikko

0 件のコメント :

コメントを投稿