フルスクリーンでsvgマスクアニメーションをしたい

#マスクアニメーションとは

`<svg>`などのpathを利用して画像にマスクをかけるアニメーションです。

今回参考に使用するのはこちらのサイトのアニメーションです。

https://wreath-ent.co.jp/web-design/play-with-svg-masks-and-animations-using-vivus-js/#part2

![anim.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257183/d03b8f59-c1b4-575c-2886-05fbf110bff9.gif)

##↑のマスクアニメーションをフルスクリーンにして使いたい

サイトの実装では画像の比率を保つように実装されているので、
これを改良してなんとか画面全体に広がるようにします。

https://qiita.com/ryounagaoka/items/a98f59347ed758743b8d

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="bGoEeEg" data-user="kiikikikkkiikikkiikikiki" 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/kiikikikkkiikikkiikikiki/pen/bGoEeEg">
  Untitled</a> by kiikikikkkiikikkiikikiki (<a href="https://codepen.io/kiikikikkkiikikkiikikiki">@kiikikikkkiikikkiikikiki</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

##cssのobject-fitでフルスクリーンにできるか

画像を全体に広げるには`object-fit: cover;`を使用すると簡単に実現できますが、

このアニメーションの実装の場合、
`<svg>`の`<image>`と[`<mask>`](https://developer.mozilla.org/ja/docs/Web/SVG/Element/mask)を使用してマスクアニメーションを実現されているので、**残念ながら`object-fit`は使用できません**。
`object-fit`が使えるのは`<img>と<video>`のみです。

（因みに`<img>`とcssの`mask-image`ではこのアニメーションの実装はできません）

https://developer.mozilla.org/ja/docs/Web/CSS/object-fit

https://developer.mozilla.org/ja/docs/Web/SVG/Element/image

一応、`<svg>`の`<image>`でも使えないか探すと、[Opera](https://dev.opera.com/articles/ja/css3-object-fit-object-position/)の記事で使えるみたいな内容のヒットしますが、Chromeでは動作不可なのを確認しています。

↓サンプル

<details><summary>`<image>`を`object-fit: cover;`</summary>
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="jOGWMRN" data-user="kiikikikkkiikikkiikikiki" 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/kiikikikkkiikikkiikikiki/pen/jOGWMRN">
  Untitled</a> by kiikikikkkiikikkiikikiki (<a href="https://codepen.io/kiikikikkkiikikkiikikiki">@kiikikikkkiikikkiikikiki</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
</details>

<details><summary>`<img>`を`object-fit: cover;`</summary>
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="ExwPgBW" data-user="kiikikikkkiikikkiikikiki" 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/kiikikikkkiikikkiikikiki/pen/ExwPgBW">
  object-fit</a> by kiikikikkkiikikkiikikiki (<a href="https://codepen.io/kiikikikkkiikikkiikikiki">@kiikikikkkiikikkiikikiki</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>
</details>

##`<svg>`のpreserveAspectRatioを使ってフルスクリーンにする

`preserveAspectRatio`は名前の通り、`<svg>`等の比率を変更する設定です。
これとcssを組み合わせることで`object-fit: cover;`ような全画面表示にすることができます。

https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/preserveAspectRatio

```html
<svg ... preserveAspectRatio="x[Min/Max/Mid]Y[Min/Max/Mid] [meet/slice]">
```

下記サイトに設定についてわかりやすい画像があったので、拝借しています。

https://triple-underscore.github.io/svg-coords-ja.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257183/f9c7f02c-9fa9-3a1d-b40b-c74a525ef687.png)

- `x[Min/Max/Mid]Y[Min/Max/Mid]`は画像の位置を設定します。
- `meet`はアスペクト比を維持した状態で表示領域に納めようとします。
- `slice`の場合はアスペクト比を維持した状態で表示領域をはみ出した部分はカットとします。

では、`object-fit: cover;`のようにするには？
↓
`preserveAspectRatio="xMidYMid slice"`にします。
これで画像をセンター配置にしつつ、はみ出した部分をカットすることができます。

実際のコードです↓

```html
<div class="container">
  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 500"  preserveAspectRatio="xMidYMid slice" class="pc">
    <image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://imgur.com/QXLsXId.png"></image>
  </svg>
  <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 750 1000"  preserveAspectRatio="xMidYMid slice" class="sp">
    <image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://imgur.com/EbKMG5L.png"></image>
  </svg>
</div>
```

:::note warn
viewBoxの設定は、必ず配置する画像の解像度を設定してください。
今回の例では解像度に合わせて「PCが1000×500、SPが750×1000」で設定しています。
:::

次にcssの設定です。
`<svg>`を画面一杯に引き伸ばし、中の`<image>`をそれに合わせる形で設定します。

```scss
.container {
  position: relative;
  width: 100%;
  height: 100vh;
  
  svg {
    width: 100%;
    height: 100vh;
  }

  image {
    width: 100%;
    height: 100%;
  }
}

.pc {
  display: block;
}
.sp {
  display: none;
}
@media (max-width: 768px) {
  .pc {
    display: none;
  }
  .sp {
    display: block;
  }
}
```

実際に動かすとこんな感じです。
レスポンシブっぽくPC/SPで表示を分けています。

<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="BawjRxy" data-user="kiikikikkkiikikkiikikiki" 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/kiikikikkkiikikkiikikiki/pen/BawjRxy">
  Untitled</a> by kiikikikkkiikikkiikikiki (<a href="https://codepen.io/kiikikikkkiikikkiikikiki">@kiikikikkkiikikkiikikiki</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

フルサイズでみる場合はこちらからどうぞ

https://codepen.io/kiikikikkkiikikkiikikiki/full/BawjRxy

##フルスクリーンにできたので`<svg>`にマスクアニメーションをつける
今のままだと画像をフルスクリーンにしただけなので、
参考サイト通りマスクアニメーションもつけます。

###マスク用のsvg用意
今回はイラレで1000×500と750×1000をベースにパスを引いてsvgで書き出しました。

:::note warn
pathは必ずstrokeで描画し、塗りつぶしがないようにしてください。
塗りつぶされたpathはアニメーションすることはできません。
:::

![スクリーンショット 2021-12-07 14.25.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257183/1f1bcfa5-a0d4-72d8-8aa6-2747320e73d2.png)

作成したsvgが上手く動くか心配な場合は以下から確認してみてください。
https://maxwellito.github.io/vivus-instant/

作成後は、

- `<mask>`の中に作成したsvgのpathだけを入れ、`id="clipmask"`を設定
- `<image>`に対しは`mask="url(#clipmask)"`を設定
- `<svg>`には`id="任意のID"`を設定します。（vivus.jsで使います）

<details><summary>htmlのサンプルコード</summary><div>

```html
<div class="container">
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1000 500"  preserveAspectRatio="xMidYMid slice" id="vivusPC" class="pc">
      <image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://imgur.com/QXLsXId.png" mask="url(#clipmask)">
      </image>
      <mask id="clipmask" maskUnits="objectBoundingBox">
        <g transform="scale(1.1)translate(-188,-66)">
            <polyline class="cls-1" points="40 192.5 199 47.5 40 292.62 307.06 47.5 40 392.46 411.97 47.5 40 497.37 528.5 40 40 602.29 635.34 40 82.5 669.97 748.71 40 193.5 670 846.86 40 315.52 670 943.31 40 432.28 670 1049.92 40 544.96 670 1148.06 40 653.95 670 1148.06 177.56 760.56 670 1148.06 319.7 894.24 670 1148.06 468.61 1041.46 670"/>
        </g>
      </mask>
    </svg>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 750 1000"  preserveAspectRatio="xMidYMid slice" id="vivusSP" class="sp">
        <image xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="https://i.imgur.com/EbKMG5L.png" mask="url(#clipmask2)">
        </image>
        <mask id="clipmask2" maskUnits="objectBoundingBox">
          <g transform="scale(1.1)translate(-188,-66)">
            <polyline class="cls-2" points="45 219.5 252 56.5 55.5 349 379.39 56.5 45 494.62 523.5 45 45 638.48 642.8 45 45 778.29 768.43 45 45 907.97 892.03 45 45 1037.65 892.03 186.63 45 1163.27 892.5 324 166.64 1163.27 892.5 458.14 294.29 1163.27 892.5 577.69 419.92 1163.27 892.5 725.61 555.67 1163.27 892.5 867.44 685.35 1163.27 892.5 1009.28 829.22 1163.27"/>
          </g>
        </mask>
    </svg>
</div>
```
</div></details>

次にスタイルを設定します。

<details><summary>scssのサンプルコード</summary><div>

```scss
.container {
  position: relative;
  width: 100%;
  height: 100vh;
  
  svg {
    width: 100%;
    height: 100vh;
  }

  image {
    width: 100%;
    height: 100%;
  }
}

.cls-1 {
  fill: none;
  stroke: #fff;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 80px;
}

.cls-2 {
  fill: none;
  stroke: #fff;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-width: 90px;
}

.pc {
  display: block;
}
.sp {
  display: none;
}
@media (max-width: 768px) {
  .pc {
    display: none;
  }
  .sp {
    display: block;
  }
}
```
</div></details>

svgのスタイルの`.cls-1`と`.cls-2`の記述に関しては、
線の設定によって変わってくると思うので、各自書き出したsvgを元に変えてください。

###vivus.js導入
https://github.com/maxwellito/vivus
 
■ [NPM](https://www.npmjs.com/package/vivus)

```
npm install vivus
```

■ [Bower](http://bower.io/)

```
bower install vivus
```

- [jsDelivr CDN](http://www.jsdelivr.com/#!vivus): - 
- //cdn.jsdelivr.net/npm/vivus@latest/dist/vivus.min.js
- [CDNJS CDN](https://cdnjs.com/libraries/vivus)
- [WebJars](http://www.webjars.org/)

インストール（読み込み）後は以下のjsを記述します。

<details><summary>jsのサンプルコード</summary><div>

```js
new Vivus('svgに設定したID', { 
    type: 'oneByOne',
    start: 'inViewport',
    duration: 200,
    forceRender: false,
    animTimingFunction: Vivus.LINEAR,
});
```

</div></details>

###完成？

実際に動かしてみるとマスクアニメーションで画像が出現するようになりますが、
マスクが全画面をカバーしてくれていないせいで隙間が見えてしまっています。
（マスクをベースより大きく作っているためです）

![スクリーンショット 2021-12-07 15.03.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257183/e5b77cc9-483e-c0e6-b129-e0f914e78ba7.png)

これを解決するために、マスクの位置とサイズを`transform`でマスクの位置と大きさを調整します。

https://developer.mozilla.org/ja/docs/Web/SVG/Attribute/transform

```html
<mask id="clipmask" maskUnits="objectBoundingBox">
    <g transform="scale(1.1)translate(-188,-66)">
        <polyline class="cls-1" points="40 192.5 199 47.5 40 292.62 307.06 47.5 40...
    </g>
```

マスクのpathを`<g>`タグで囲い、
`transform="scale(1.1)translate(-188,-66)"`を設定します。
ここの値はマスクの大きさなどで変わってくるので、各自調整してみてください。

###完成
<p class="codepen" data-height="300" data-default-tab="css,result" data-slug-hash="LYzGwEp" data-user="kiikikikkkiikikkiikikiki" 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/kiikikikkkiikikkiikikiki/pen/LYzGwEp">
  mask</a> by kiikikikkkiikikkiikikiki (<a href="https://codepen.io/kiikikikkkiikikkiikikiki">@kiikikikkkiikikkiikikiki</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

全画面で見る場合はこちらをどうぞ。
https://codepen.io/kiikikikkkiikikkiikikiki/full/LYzGwEp

vivusは再生の操作が可能なので、
演出に合わせて操作すると良いと思います。

:::note alert
safariでは全画面マスク処理が重い確認しています。使用する際はターゲットブラウザに気をつけてください。
:::

参考:
https://developer.mozilla.org/
https://wreath-ent.co.jp/web-design/play-with-svg-masks-and-animations-using-vivus-js/#part2
https://fuuno.net/ani/ani35/ani35.html
https://triple-underscore.github.io/svg-coords-ja.html