24
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

un-T factory! XAAdvent Calendar 2021

Day 17

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

Last updated at Posted at 2021-12-16

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

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

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

anim.gif

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

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

See the Pen Untitled by kiikikikkkiikikkiikikiki (@kiikikikkkiikikkiikikiki) on CodePen.

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

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

このアニメーションの実装の場合、
<svg><image><mask>を使用してマスクアニメーションを実現されているので、残念ながらobject-fitは使用できません
object-fitが使えるのは<img>と<video>のみです。

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

一応、<svg><image>でも使えないか探すと、Operaの記事で使えるみたいな内容のヒットしますが、Chromeでは動作不可なのを確認しています。

↓サンプル

``を`object-fit: cover;`

See the Pen Untitled by kiikikikkkiikikkiikikiki (@kiikikikkkiikikkiikikiki) on CodePen.

``を`object-fit: cover;`

See the Pen object-fit by kiikikikkkiikikkiikikiki (@kiikikikkkiikikkiikikiki) on CodePen.

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

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

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

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

image.png

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

では、object-fit: cover;のようにするには?

preserveAspectRatio="xMidYMid slice"にします。
これで画像をセンター配置にしつつ、はみ出した部分をカットすることができます。

実際のコードです↓

<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>

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

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

.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で表示を分けています。

See the Pen Untitled by kiikikikkkiikikkiikikiki (@kiikikikkkiikikkiikikiki) on CodePen.

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

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

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

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

スクリーンショット 2021-12-07 14.25.39.png

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

作成後は、

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

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

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;
  }
}

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

###vivus.js導入
https://github.com/maxwellito/vivus

NPM

npm install vivus

Bower

bower install vivus

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

jsのサンプルコード
new Vivus('svgに設定したID', { 
    type: 'oneByOne',
    start: 'inViewport',
    duration: 200,
    forceRender: false,
    animTimingFunction: Vivus.LINEAR,
});

###完成?

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

スクリーンショット 2021-12-07 15.03.57.png

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

<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)"を設定します。
ここの値はマスクの大きさなどで変わってくるので、各自調整してみてください。

###完成

See the Pen mask by kiikikikkkiikikkiikikiki (@kiikikikkkiikikkiikikiki) on CodePen.

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

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

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

24
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?