<svg>と<path>とCSSで作るアニメーションの可能性

## はじめに
普段何気なくSVGファイルを使用していますが、だいたいはimgタグや疑似要素に入れたりなんかして...
そういえばsvgタグってどんなことができるのか把握できていなかったと思い調べると思ったよりできることがありました。その中でも本記事では、`<svg>`と`<path>`とCSSでできるアニメーションをいくつかご紹介します。

## SVGをアニメーションさせる事例
よくあるのはこういった線をなぞって塗りつぶすもの
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="ExMYmWr" data-user="shoko232" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shoko232/pen/ExMYmWr">
  sample</a> by shoco_cor (<a href="https://codepen.io/shoko232">@shoko232</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## `animation`と`@keyframes`で持続時間と速度を設定する

### animation
まず使用するプロパティは`animation`です。
`animation`は一括指定プロパティなので以下の構成要素を持っています。
```scsscss
// アニメーションの開始時間
animation-delay: 1s;

// アニメーションの再生方向
animation-direction: alternate;

// アニメーションの所要時間
animation-duration: 3s;

// アニメーション実行前後の状態
animation-fill-mode: forwards;

// アニメーションを何セット繰り返すか
animation-iteration-count: 1;

// 使用するキーフレームの名称
animation-name: test;

// アニメーションの実行状況
animation-play-state: running;

// アニメーション実行中の速度
animation-timing-function: ease;
```
この中から必要なものを記述していきます。今回は`name`、`duration`、`timing-function`を主に使っていきます。
* `name`に`@keyframes`に記述した名称を記載し(詳細は後述します)
* `duration`でひとつのアニメーションが完了するまでの時間を設定
* `timing-function`で`duration`の時間中の再生速度を設定

これらを書くことでアニメーションの大枠が作ることができます。
ではもっと詳細な中身を書いていきましょう。
といっても見本に出したSVGの図形に対して線を走らせて、内部を塗りつぶすアニメーションであればかなりシンプルです。

### `@keyframes`
ここで`@keyframes`について見ていきましょう。
`from`-`to`で始まりと終わりを設定する、もしくは0%~100%のなかで作動するタイミングを細かく指定することができます。
```scss
// 最初と最後の状態を設定する
@keyframes test1 {
    from {
        fill: transparent;
    }
    to {
        fill: yellow;
    }
}

// もしくは％で間を細かく設定することもできる
@keyframes test2 {
    0% {
        fill: transparent;
    }
    50% {
        fill: lightyellow;
    }
    100% {
        fill: yellow;
    }
}
```

## SVGで使える`fill`と`stroke`
`fill`と`stroke`はSVGで描画に使われる`<rect>`や`<circle>`、`<path>`などのタグにインラインCSSで記述する属性ですが、今回はCSSにプロパティを記述してアニメーションさせる場合に焦点を当てています。

### `fill`
`fill`はオブジェクトの内部の色を設定します。
`fill-opacity`で透過の設定ができます。
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="zYbOxoE" data-user="shoko232" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shoko232/pen/zYbOxoE">
  fill</a> by shoco_cor (<a href="https://codepen.io/shoko232">@shoko232</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

### `stroke`
`stroke`はオブジェクトを囲む線の色を設定します。
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="dyrbPWg" data-user="shoko232" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shoko232/pen/dyrbPWg">
  stroke</a> by shoco_cor (<a href="https://codepen.io/shoko232">@shoko232</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

このふたつを組み合わせることで星をなぞってから塗りつぶすアニメーションが完成します。
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="ExMYmWr" data-user="shoko232" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shoko232/pen/ExMYmWr">
  sample</a> by shoco_cor (<a href="https://codepen.io/shoko232">@shoko232</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

:::note warn
`stroke`はオブジェクトの外側に線が付くのでSVGのデータの状態によっては描画範囲からはみ出してしまい見切れてしまいます。
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="xxBKryq" data-user="shoko232" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shoko232/pen/xxBKryq">
  stroke-over</a> by shoco_cor (<a href="https://codepen.io/shoko232">@shoko232</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
:::

また、`stroke`には他にもプロパティがあります。
```scss
// 線を破線にする
stroke-dasharray: 2 1;

// 線の描画開始位置
stroke-dashoffset: 50;

// 線の透過
stroke-opacity: 1;

// 線の幅
stroke-width: 2px;

// 以下はパスや基本的な図形に使える
// 線の端の形状
stroke-linecap: round;

// 線の連結部の形状
stroke-linejoin: miter;

// 線の連結部の尖り具合
stroke-miterlimit: 1;
```
見本にも使用している`stroke-dasharry`は破線の間隔を設定できるのですが、今回はパスの全長を設定しています。そして`stroke-dashoffset`で描画の開始位置を設定できるので、アニメーションで初期位置と終了位置をそれぞれ開始と終了に記載することで線を描画するアニメーションを作ることができます。

また、今回の星型はFigmaで図形を書いてSVGで取得し、`getTotalLength()`を使用してパスの全長を計算した値を使用しています。

### `fill`と`stroke`の注意点
見本では塗りつぶした星を使用していますが、線画の星の場合は`fill`と`stroke`の挙動が変わります。
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="BabBJMo" data-user="shoko232" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shoko232/pen/BabBJMo">
  fillとstroke</a> by shoco_cor (<a href="https://codepen.io/shoko232">@shoko232</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## SVGで使える`transform`
`transform`は一般的なWeb制作でも使用する場面は多いと思いますので、`transform`の詳細は割愛しますが、これもSVGのアニメーションで使うことができます。

`translate`で位置を変更し、`scale`でサイズを変え、`rotate`で角度を変更すればかなりの動きは再現することができます。
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="OJqLzbe" data-user="shoko232" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shoko232/pen/OJqLzbe">
  transform</a> by shoco_cor (<a href="https://codepen.io/shoko232">@shoko232</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

:::note warn
`rotate`で回転させる場合は、`transform-origin`で回転の中心点を調整しないと初期値が左上になっています。
:::

## まとめ
様々なプロパティを見てきましたが、これまでのプロパティでかなりの動きは実装できそうですよね！
わたしはクリックしたときに動くようにしたかったので実装してみました。
星の線上でクリックしてみてください！
（クリックの判定がシビアですみません><）
<p class="codepen" data-height="300" data-default-tab="result" data-slug-hash="LYaPQYa" data-user="shoko232" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/shoko232/pen/LYaPQYa">
  Untitled</a> by shoco_cor (<a href="https://codepen.io/shoko232">@shoko232</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## あとがき
実は最近身近で既存のSVGファイルのアイコンに後からアニメーションを追加してほしいという依頼があったので、チームで試行錯誤する機会がありました。
みんなでいろいろと調べていく内にこれって実は可能性無限大では…？？と思ったのが本記事を書くきっかけでした。SVGアニメーションを作るジェネレーターもありますが、どこまでなら人力で再現できるのか試したくなりました。想像以上にできる幅が広かったので、かなりターゲットを絞り込んで記事にしてしまいました。しかし、実際に自分でどこまで記事にするか試行錯誤する時間はとても楽しかったので、自分の好奇心をうまくアウトプットに繋げることができてよかったです。

## 参考
* [プラグインなしで作るSVGアニメーション（パスに沿って描写されていくアニメーション）](https://qiita.com/kft/items/3102196d2355f575f91e)
* [MDN animation](https://developer.mozilla.org/ja/docs/Web/CSS/animation)
* [MDN SVG教本](https://developer.mozilla.org/ja/docs/Web/SVG/Tutorial)
* [SVG逆引きリファレンス](https://shanabrian.com/web/svg/)
* [MDN getTotalLength()](https://developer.mozilla.org/en-US/docs/Web/API/SVGGeometryElement/getTotalLength)