2018/10/29

box-shadowを使ってCSSだけでドット絵を描き、アニメーションさせる

CSSだけでドット絵(Pixel Art)を描く・アニメーションさせる方法を紹介する。ただ、バニラCSSだけでも大丈夫なのだが、よりメンテナブルにするためSassで描く方法も併せて紹介する。

ちなみに下図のようなドット絵がつくれる。

マリオとMinecraftの土ブロックは、CSSアニメーションをつかって動かしている。もちろんJavaScriptは使っていない。

box-shadowプロパティについて


ドット絵を描くためには、CSSのbox-shadowプロパティを使う。
そもそもbox-shadowプロパティは、要素にドロップシャドウ効果(影をつける)を与えるのが本来の使い方なので、まずは基本的な使い方からまとめる。

いくつかの構文がある。

  • box-shadow: offset-x offset-y color
  • box-shadow: offset-x offset-y blur-radius color
  • box-shadow: offset-x offset-y blur-radius spread-radius color
  • box-shadow: inset offset-x offset-y color


offset-x, offset-yは影の位置を指定するために使う。要素の左上を原点とし、そこからX軸、Y軸にどれだけ移動させるかを指定する。
colorはその名のとおり、影の色を指定する。
blur-radiusは、ブラー効果(ぼかし)の半径を指定する。border-radiusと同じ感じだ。
spread-raduisは、ぼかしの大きさ(拡大/縮小)を指定する。
insetキーワードは、ドロップシャドウ効果をボックスの内側に描画するときに指定する。

また、これらの値はカンマ区切りで複数指定できる。

言葉で説明してもわかりづらいと思うので、Playgroundをつくってみた。
実際に触ってみると以下のような感じで動くので、どの値がどのように影響しているかわかっていただけると思う。



基礎: ドット絵を描くために


box-shadowの基礎はわかったので、つぎにドット絵を描く。

1辺が100pxの正方形を、box-shadowを使って表示する。
<div class="container">
    <div class="box"></div>
</div>

<style>
* {
  /* 見た目をわかりやすくするためにborderを使ったため(なくてもOK) */
  box-sizing: border-box;
}
.container {
  /* box-shadowで表示した部分は要素のwidth,heightに含まれないので調整 */
  width: 200px;
  height: 200px;
}

.box {
  /* 基となる要素のサイズ */
  width: 100px;
  height: 100px;
  border: 2px solid #777;

  /* 基となる要素の右下に同じサイズのボックスを表示する */
  box-shadow: 100px 100px rgba(7,7,7,.3);
}
</style>
上図のようにbox-shadowを使うことで、もととなる要素と同じ大きさの影が描ける。サンプルコードでは100px四方の影を(100px, 100px)の位置に表示しているだけだが、これをカンマ区切りで複数指定することでドット絵が描けるようになる。



発展: box-shadowプロパティを使ってドット絵を描く


次にbox-shadowプロパティに複数の値を指定して、ドット絵を描いていく。

完成予想図

このドット絵は5x5で描いている。
まずは、左のカラーパターンから。
<div class="container">
  <div class="pixel one"></div>
</div>

<style>
.container {
  /* ドット絵のサイズ */
  width: 100px;
  height: 100px;
}

.pixel {
  /* 疑似要素の位置を調整するため */
  position: relative;
}
.pixel::before {
  content: "";

  /* ドットの大きさ(例:20px x 20px) */
  width: 20px;
  height: 20px;
  /* box-shadowで色付けするため疑似要素は透明にする */
  background-color: transparent;

  /* 左上を(0,0)のポジションにするため疑似要素の位置をずらす */
  position: absolute;
  top: -20px;
  left: -20px;
}

.pixel.one::before {
  box-shadow:
     /* 列 行 色 */
     /* 1列目 */
     20px   20px #FB0600,
     20px   40px #FC322F,
     20px   60px #FC6663,
     20px   80px #FD9999,
     20px  100px #FECCCB, 
     /* 2列目 */
     40px   20px #60169F,
     40px   40px #7A23B0,
     40px   60px #964DC2,
     40px   80px #B681D9,
     40px  100px #D8BEED, 
     /* 3列目 */
     60px   20px #1388BC,
     60px   40px #269DC9,
     60px   60px #55B3D7,
     60px   80px #88CAE2,
     60px  100px #BFE3EF, 
     /* 4列目 */
     80px   20px #ACD902,
     80px   40px #BDE02D,
     80px   60px #CDEA5E,
     80px   80px #DBEF8E,
     80px  100px #F4FBC8, 
     /* 5列目 */
    100px  20px #FB8F02,
    100px  40px #FDA533,
    100px  60px #FDBB64,
    100px  80px #FED39A,
    100px 100px #FDE8C9;
}
</style>

まず、bod-shadowで表示した影はボックスのサイズに含まれないので、containerクラスを用いドット絵完成後のサイズを指定している。
次に、box-shadowの影は基となる要素のサイズで決まるので1ドットを20px四方にするため、pixelクラスでwidth,heightに20pxを指定している。

実際のドットはbefore疑似要素で書くのだが、pixelの20px四方のボックス分左上にスペースが空いてしまうため、position: absoluteを使って位置を調整している。
あとはbox-shadowプロパティを用い、1ドットずつ指定して行けばドット絵が完成する。


ちなみに図の右の顔みたいなドット絵は以下のような実装になる。
.pixel.two::before {
  box-shadow:
    20px   20px #704b16,
    40px   20px #704b16,
    60px   20px #704b16,
    80px   20px #704b16,
    100px  20px #704b16,
    20px   40px #704b16,
    40px   40px #fdb778,
    60px   40px #fdb778,
    80px   40px #fdb778,
    100px  40px #704b16,
    20px   60px #fdb778,
    40px   60px #333333,
    60px   60px #fdb778,
    80px   60px #333333,
    100px  60px #fdb778,
    20px   80px #fdb778,
    40px   80px #fdb778,
    60px   80px #fdb778,
    80px   80px #fdb778,
    100px  80px #fdb778,
    20px  100px #fdb778,
    40px  100px #c70300,
    60px  100px #c70300,
    80px  100px #c70300,
    100px 100px #fdb778;
}


応用: Sassを使ってメンテナブルなドット絵を描く


いくつかサンプルコードを書いたが、これをメンテできる自信は私にはない。5x5のドット絵ですら25個の値を書かなければならない。一般的な16x16にもなると256個もの値を書かなければならない。

そこでSass(今回はSCSS)を使って、メンテしやすいドット絵を実装する。
Sassの環境構築は以下の記事を参照にしてほしい。

Sassのmixin(functionでも可)を使っていい感じのスタイルを生成する方法だ。
@mixin pixelize($matrix, $size, $colors) {
  $ret: "";

  @for $i from 1 through length($matrix) {
    $row: nth($matrix, $i);

    @for $j from 1 through length($row) {
      $dot: nth($row, $j);

      @if $dot != 0 {
        @if $ret != "" {
          $ret: $ret + ",";
        }

        $color: nth($colors, $dot);
        $ret: $ret + ($j * $size) + " " + ($i * $size) + " " + $color;
      }
    }
  }

  box-shadow: unquote($ret + ";");
}

$heart-colors: (#333, #f11416, #831200);
$heart: (
  (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0),
  (0,0,1,1,1,0,0,0,0,0,1,1,1,0,0,0),
  (0,1,2,2,2,1,0,0,0,1,2,2,3,1,0,0),
  (1,2,0,0,2,2,1,0,1,2,2,2,2,3,1,0),
  (1,2,0,2,2,2,2,1,2,2,2,2,2,3,1,0),
  (1,2,2,2,2,2,2,2,2,2,2,2,2,3,1,0),
  (1,2,2,2,2,2,2,2,2,2,2,2,2,3,1,0),
  (1,2,2,2,2,2,2,2,2,2,2,2,2,3,1,0),
  (0,1,2,2,2,2,2,2,2,2,2,2,3,1,0,0),
  (0,0,1,2,2,2,2,2,2,2,2,3,1,0,0,0),
  (0,0,0,1,2,2,2,2,2,2,3,1,0,0,0,0),
  (0,0,0,0,1,2,2,2,2,3,1,0,0,0,0,0),
  (0,0,0,0,0,1,2,2,3,1,0,0,0,0,0,0),
  (0,0,0,0,0,0,1,3,1,0,0,0,0,0,0,0),
  (0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0),
  (0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
);

.icon {
  width: 20px;
  height: 20px;
  @include pixelize($heart, 20px, $heart-colors);
}

pixelizeというmixinを定義し、そこにドット絵のマトリクス($heart)、ドットのサイズ(20px)、色配列($hearts-colors)を渡し、box-shadowプロパティを生成している。
ドット絵のマトリクスは0〜nの数字で表し、0は透明、1〜nは色配列($heart-colors)に対応付けしている。

ブログ上だとわかりづらいかもしれないが、エディタで表示するとドット絵のマトリクスがハイライトされ、どんな形になっているか一目瞭然になる。

バニラCSSと比べて、メンテしやすくなったと思う。


それでもしんどくなったら、CSSドット絵ジェネレータを作ったので使ってみてほしい。



番外: ドット絵でアニメーションを描く


実はCSSだけでドット絵のアニメーションも描くことができる。

通常はiconクラスとかに直接box-shadowプロパティを指定して描くのだが、アニメーションをさせるときはCSSアニメーションを使用する。
.mario {
  width: 8px;
  height: 8px;

  animation:
    jump 1s infinite,
    sprite 1s infinite;
}

/* ジャンプモーション(上下移動) */
@keyframes jump {
  from, 25%, 75%, to {
    transform: translateY(0);
  }
  50% {
    transform: translateY(calc(8px * -8));
  }
}

/* 通常モードとジャンプモードのドット絵 */
@keyframes sprite {
  /* animation-timing-function: steps(n)より細かく指定したかったのでパーセントで指定 */
  from, 24%, 76%, to {
    box-shadow: /* 通常モードのドット絵 */
  }
  25%, 75% {
    box-shadow: /* ジャンプモードのドット絵 */
  }
}

CSSアニメーションでbox-shadowの値を変更する + 要素自体を上下に動かすことであたかもジャンプしているように見える。
詳しい実装方法などは、以下のリポジトリにサンプルがあるのでそちらを参照してほしい。


以上

written by @bc_rikko

0 件のコメント :

コメントを投稿