こんにちは、中村です。
SVGについて、あまり勉強してこなかったので、
今回は気になっていた写真の切り抜きとアニメーションを試したいと思います。
DEMO
作ったのはこちら。
See the Pen SVGの画像の切り抜きとsnap.svgを使ったアニメーション/a> by Nakamura (@takayo-nakamura) on CodePen.
星の形に写真を切り抜く
クリッピングパスを使って写真を切り抜いています。 CSSのプロパティにもclip-pathはありますが、IEが対応していないため、 今回はSVGのclip-path属性を使って切り抜きました。
クリッピング用パス
SVGのパスの記述を<clipPath id="clip">
で囲み、idを付けます。
このSVG自体は表示させたくないため、CSSで非表示にします。
display:none;
では効果がなくなってしまうため、 position: absolute;
にしつつ、幅と高さをなくすことで非表示にします。
<!-- クリッピング用パス --> <svg xmlns="http://www.w3.org/2000/svg" class="clipPath"> <clipPath id="clip"> <polygon points="111.06 0 145.38 69.54 222.12 80.69 166.59 134.82 179.7 211.25 111.06 175.17 42.42 211.25 55.53 134.82 0 80.69 76.74 69.54 111.06 0" /> </clipPath> </svg>
// クリッピング用パス .clipPath { position: absolute; top: 0; left: 0; width: 0; height: 0; }
写真をSVGで表示する
<svg id="svgPhoto" class="star_photo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 222 211"> <image xlink:href="https://picsum.photos/500/500" width="100%" height="100%" preserveAspectRatio="xMidYMid slice" clip-path="url(#clip)" /> </svg>
clip-path属性には先ほど記述したクリッピング用パスのidを指定します。
clip-path="url(#clip)"
これで写真の切り抜きができました。
アニメーションを加える
次にアニメーションも加えたいと思います。
星の淵に沿って線が描画され、その後に写真が表示されるようにしたいと思います。
写真を表示したSVGの上に、同じ形のSVGを重ねます。
<svg id="svgCover" class="star_cover" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 222 211"> <polygon points="111.06 0 145.38 69.54 222.12 80.69 166.59 134.82 179.7 211.25 111.06 175.17 42.42 211.25 55.53 134.82 0 80.69 76.74 69.54 111.06 0" /> </svg>
線の描画はCSSでstrokeを使います。
塗りはfillです。
線の描画アニメーションの仕組みは、破線を使って行います。
破線の間隔を星一周分にし、開始位置の数値を合わせると、線のない状態になります。
開始位置を0に向けて下げていくことで、描画されているように見えます。
stroke-dasharray: 1000px; // 破線の間隔 stroke-dashoffset: 1000px; // svgパスの始まりの位置
CSSなのでanimationでも動かすことが可能ですが、 IEはanimationでstroke-dashoffsetの値を変えることができないようなので、snap.svgというjavascriptのプラグインを使って動かします。
プラグイン:Snap.svg - Home
snap.svgを使う
snap.svgのjsを読み込みます。
あとはアニメーションさせたい要素を取得し、それに対してアニメーションを加えていきます。
写真を先に非表示にしているのは、そうしないと薄ら見えてしまうからです。
<script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
const svgStar = Snap('#svgStar'); // 2つのsvgの親要素を取得 const svgPhoto = svgStar.select('#svgPhoto'); //その中の写真のsvgを取得 const svgCover = svgStar.select('#svgCover'); //その中の上に重ねるsvgを取得 svgPhoto.animate({ opacity: 0 }, 0); //写真のsvgを非表示にする svgCover.animate({ 'stroke-dashoffset': 0 }, 1800, function () { //上に重ねるsvgのパスの開始位置を、1800msかけて0にする svgPhoto.animate({ opacity: 1 }, 0); //写真のsvgを表示する svgCover.animate({ 'fill-opacity': 0 }, 800);//上に重ねるsvgの塗りを800msかけて非表示にする });
最後に
最初は星ではなくてsvgのtextで文字で作っていたのですが、
stroke-dashoffsetのアニメーションがうまく動かなかったので諦めました。
またどこかで試してみたいと思います。