10
2

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 1 year has passed since last update.

NIJIBOXAdvent Calendar 2023

Day 20

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

Last updated at Posted at 2023-12-26

はじめに

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

SVGをアニメーションさせる事例

よくあるのはこういった線をなぞって塗りつぶすもの

See the Pen sample by shoco_cor (@shoko232) on CodePen.

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

animation

まず使用するプロパティはanimationです。
animationは一括指定プロパティなので以下の構成要素を持っています。

// アニメーションの開始時間
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;

この中から必要なものを記述していきます。今回はnamedurationtiming-functionを主に使っていきます。

  • name@keyframesに記述した名称を記載し(詳細は後述します)
  • durationでひとつのアニメーションが完了するまでの時間を設定
  • timing-functiondurationの時間中の再生速度を設定

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

@keyframes

ここで@keyframesについて見ていきましょう。
from-toで始まりと終わりを設定する、もしくは0%~100%のなかで作動するタイミングを細かく指定することができます。

// 最初と最後の状態を設定する
@keyframes test1 {
    from {
        fill: transparent;
    }
    to {
        fill: yellow;
    }
}

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

SVGで使えるfillstroke

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

fill

fillはオブジェクトの内部の色を設定します。
fill-opacityで透過の設定ができます。

See the Pen fill by shoco_cor (@shoko232) on CodePen.

stroke

strokeはオブジェクトを囲む線の色を設定します。

See the Pen stroke by shoco_cor (@shoko232) on CodePen.

このふたつを組み合わせることで星をなぞってから塗りつぶすアニメーションが完成します。

See the Pen sample by shoco_cor (@shoko232) on CodePen.

strokeはオブジェクトの外側に線が付くのでSVGのデータの状態によっては描画範囲からはみ出してしまい見切れてしまいます。

See the Pen stroke-over by shoco_cor (@shoko232) on CodePen.

また、strokeには他にもプロパティがあります。

// 線を破線にする
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()を使用してパスの全長を計算した値を使用しています。

fillstrokeの注意点

見本では塗りつぶした星を使用していますが、線画の星の場合はfillstrokeの挙動が変わります。

See the Pen fillとstroke by shoco_cor (@shoko232) on CodePen.

SVGで使えるtransform

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

translateで位置を変更し、scaleでサイズを変え、rotateで角度を変更すればかなりの動きは再現することができます。

See the Pen transform by shoco_cor (@shoko232) on CodePen.

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

まとめ

様々なプロパティを見てきましたが、これまでのプロパティでかなりの動きは実装できそうですよね!
わたしはクリックしたときに動くようにしたかったので実装してみました。
星の線上でクリックしてみてください!
(クリックの判定がシビアですみません><)

See the Pen Untitled by shoco_cor (@shoko232) on CodePen.

あとがき

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

参考

10
2
1

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?