MENU

SCSSでbackgroundにsvgを指定するときに便利なbase64エンコード関数を作る

IE9linear-gradient を使いたいとき、IE独自の filter では実現不可能なものがあります。color stop がないため、他のブラウザと比べて表現できる範囲がせまいのです。そこで、IE9のみSVGで代用するためにSVGコードからbase64エンコードして埋め込むmixinを作ったので紹介します。

アルゴリズム

アルゴリズムはこちらを参考にさせていただきました。非常に分かりやすかったです。

base64ってなんぞ??理解のために実装してみた

base64Encode関数


@function base64Encode($string)
{
  // 文字から2進数に変換するテーブル
  $character_to_binary_number_table:
  (
    ' ': '00100000',  '!': '00100001',  '"': '00100010',
    '#': '00100011',  '$': '00100100',  '%': '00100101',
    '&': '00100110',  '\'': '00100111', '(': '00101000',
    ')': '00101001',  '*': '00101010',  '+': '00101011',
    ',': '00101100',  '-': '00101101',  '.': '00101110',
    '/': '00101111',  '0': '00110000',  '1': '00110001',
    '2': '00110010',  '3': '00110011',  '4': '00110100',
    '5': '00110101',  '6': '00110110',  '7': '00110111',
    '8': '00111000',  '9': '00111001',  ':': '00111010',
    ';': '00111011',  '<': '00111100',  '=': '00111101',
    '>': '00111110',  '?': '00111111',  '@': '01000000',
    'A': '01000001',  'B': '01000010',  'C': '01000011',
    'D': '01000100',  'E': '01000101',  'F': '01000110',
    'G': '01000111',  'H': '01001000',  'I': '01001001',
    'J': '01001010',  'K': '01001011',  'L': '01001100',
    'M': '01001101',  'N': '01001110',  'O': '01001111',
    'P': '01010000',  'Q': '01010001',  'R': '01010010',
    'S': '01010011',  'T': '01010100',  'U': '01010101',
    'V': '01010110',  'W': '01010111',  'X': '01011000',
    'Y': '01011001',  'Z': '01011010',  '[': '01011011',
    '\\': '01011100', ']': '01011101',  '^': '01011110',
    '_': '01011111',  '`': '01100000',  'a': '01100001',
    'b': '01100010',  'c': '01100011',  'd': '01100100',
    'e': '01100101',  'f': '01100110',  'g': '01100111',
    'h': '01101000',  'i': '01101001',  'j': '01101010',
    'k': '01101011',  'l': '01101100',  'm': '01101101',
    'n': '01101110',  'o': '01101111',  'p': '01110000',
    'q': '01110001',  'r': '01110010',  's': '01110011',
    't': '01110100',  'u': '01110101',  'v': '01110110',
    'w': '01110111',  'x': '01111000',  'y': '01111001',
    'z': '01111010',  '{': '01111011',  '|': '01111100',
    '}': '01111101',  '~': '01111110'
  );

  // 2進数からbase64の文字に変換するテーブル
  $binary_number_to_base64_table:
  (
    '000000': 'A',  '000001': 'B',  '000010': 'C',  '000011': 'D',
    '000100': 'E',  '000101': 'F',  '000110': 'G',  '000111': 'H',
    '001000': 'I',  '001001': 'J',  '001010': 'K',  '001011': 'L',
    '001100': 'M',  '001101': 'N',  '001110': 'O',  '001111': 'P',
    '010000': 'Q',  '010001': 'R',  '010010': 'S',  '010011': 'T',
    '010100': 'U',  '010101': 'V',  '010110': 'W',  '010111': 'X',
    '011000': 'Y',  '011001': 'Z',  '011010': 'a',  '011011': 'b',
    '011100': 'c',  '011101': 'd',  '011110': 'e',  '011111': 'f',
    '100000': 'g',  '100001': 'h',  '100010': 'i',  '100011': 'j',
    '100100': 'k',  '100101': 'l',  '100110': 'm',  '100111': 'n',
    '101000': 'o',  '101001': 'p',  '101010': 'q',  '101011': 'r',
    '101100': 's',  '101101': 't',  '101110': 'u',  '101111': 'v',
    '110000': 'w',  '110001': 'x',  '110010': 'y',  '110011': 'z',
    '110100': '0',  '110101': '1',  '110110': '2',  '110111': '3',
    '111000': '4',  '111001': '5',  '111010': '6',  '111011': '7',
    '111100': '8',  '111101': '9',  '111110': '+',  '111111': '/'
  );

  // 文字列を2進数へ変換したときに代入する変数
  $binary_number: '';

  // 文字列の長さだけループ
  @for $i from 1 through str-length($string)
  {
    // 1文字ずつ取り出す
    $character: str-slice($string, $i, $i);
    // 2進数へ変換 
    $binary_number: $binary_number + map-get($character_to_binary_number_table, $character);
  }

  // 2進数を6ビットずつ分割した配列
  $hexadecimal_number: ();
  // 2進数を6ビットずつ分割した配列の長さ
  $hexadecimal_length: ceil(str-length($binary_number)/6-1);

  @for $i from 0 through $hexadecimal_length
  {
    // 6ビットに区切った文字列
    $tmp: str-slice($binary_number, 2*($i*3)+1, (2*($i*3)+1+5));

    // 配列の最後のビット数が6とは限らないのでチェック
    @if $i == $hexadecimal_length
    {
      // 6ビットに区切った文字列の長さ
      $tmp_length: str-length($tmp);

      // 6ビットでないとき
      @if $tmp_length != 6
      {
        // 0で埋めて6ビットにする
        @for $j from $tmp_length+1 through 6
        {
          $tmp: $tmp + '0';
        }
      }
    }
    // 2進数を6ビットずつ分割
    $hexadecimal_number: append($hexadecimal_number, $tmp);
  }

  // base64に変換した文字列
  $base64_string: '';

  @for $i from 1 to length($hexadecimal_number)+1
  {
    // 変換テーブルを使ってbase64エンコード
    $base64_string: $base64_string + map-get($binary_number_to_base64_table, nth($hexadecimal_number, $i));
  }

  // base64の最終結果
  $base64: '';
  // 4ビットずつ分割した配列の長さ
  $fourdecimal_length: ceil(str-length($base64_string)/4-1);

  @for $i from 0 through $fourdecimal_length
  {
    // 4ビットに区切った文字列
    $tmp: str-slice($base64_string, 2*($i*2)+1, (2*($i*2)+1+3));

    // 配列の最後のビット数が4とは限らないのでチェック
    @if $i == $fourdecimal_length
    {
      // 4ビットに区切った文字列の長さ
      $tmp_length: str-length($tmp);

      // 4ビットでないとき
      @if $tmp_length != 4
      {
        // =で埋めて4ビットにする
        @for $j from $tmp_length+1 through 4
        {
          $tmp: $tmp + '=';
        }
      }
    }

    $base64: $base64 + $tmp;
  }
  
  @return $base64;
}

使い方


html 
{
  background: url('data:image/svg+xml;base64,' + base64Encode(''));
}

こんな感じで直接SVGコードを指定できます。base64Encode() 関数に渡すときは改行は消して1行のSVGコードにしておきましょう。