![]()
こんにちは、ウィルスタイルの梁川です。
入社から早くも5ヶ月が経ち、毎日多くの学びがある日々を送っています。
既存サイトの保守・更新や下層ページのデザイン、ディレクションの補助など、さまざまな業務を経験させていただき、時間が経つのもあっという間です。
どの業務も楽しんでいますが、個人的な関心としてWeb上でのアニメーションを探求するのが好きです。
今回は、スクロールに合わせて線が描かれるSVGアニメーションの作り方をご紹介したいと思います。
視線誘導やストーリー性を作るうえで、とても効果的な表現です。
実際の挙動や全体のコードは、以下のURLからご確認いただけます。
Table of contents
HTML・CSS
まずは土台となるコードです。いくつかこだわっているポイントだけピックアップします。
<div class="circle__item"> <svg class="circle__shape" data-circle aria-hidden="true" ...> <path stroke="#000" d="..." /> </svg> </div>
HTMLでは、JavaScriptのフック用に data-circle というデータ属性を使用しました。クラス名(スタイル用)と分けることで、デザイン変更に強い設計にしています。
また、装飾用の画像なので aria-hidden="true" を付与し、スクリーンリーダーへの配慮も行っています。
.circle__item {
margin-top: -180px;
}
CSSでは、margin-top にマイナスの値を指定して円の要素を重ねて、連鎖する波紋のような奥行きを表現しました。
線が描かれる仕組み
実装に入る前に、そもそもなぜ線が伸びていくように見えるのか、その原理を整理しておきましょう。
これは、線を伸ばしているのではなく、点線の隙間を移動させているというイメージです。
鍵となるのは、SVGの2つのプロパティです。
- stroke-dasharray:点線の「実線」と「隙間」の間隔
- stroke-dashoffset:線の開始位置のズレ
アニメーションの原理
通常、点線は「実線・隙間・実線…」の繰り返しですが、「実線の長さ」と「隙間の長さ」を、パスの全長と同じ長さに設定することで、今回のようなアニメーションが実装できます。
- 隠す(初期状態)
stroke-dasharrayで「全長分の実線」と「全長分の隙間」を作ります。
そしてstroke-dashoffsetを使って、開始位置を「全長分」ずらします。
そうすると、画面上では「隙間」の部分だけが表示され、線が見えなくなります。 - 描く(アニメーション)
スクロールに合わせて、ずらしていたstroke-dashoffsetを0に戻していきます。
すると、画面外に押し出されていた「実線」の部分が入ってきて、線が描かれているように見せることができます。
JavaScriptでの実装
仕組みがわかったところで、今回のメインであるJavaScriptの実装です。
Step 1:要素の取得とループ処理
const circles = document.querySelectorAll("[data-circle]");
if (circles.length > 0) {
circles.forEach((el) => {
const path = el.querySelector("path");
if (!path) return; // エラー回避
});
}
まず querySelectorAll で対象となるSVGをすべて取得します。
その後、forEach を使って1つずつ処理を回していきます。こうすることで、ページ内に複数のアニメーションがあっても、それぞれ個別に動作させることができます。
if (!path) return; は、万が一SVG内にパスが含まれていなかった場合にエラーで止まるのを防ぐための記述です。
Step 2:パスの長さを自動取得
// パスの正確な長さを取得 const length = path.getTotalLength();
ここが今回の最大のポイントで、 getTotalLength() メソッドを使うと、パスの正確な幾何学的な長さを取得できます。
これにより、「SVGを差し替えたら線が足りなくなる」といった問題を解消できます。
Step 3:初期状態のセット(線を隠す)
gsap.set(path, {
strokeDasharray: length,
strokeDashoffset: length
});
取得した length を使って、CSSプロパティをセットします。
gsap.set で初期状態を指定します。
ここで「線の長さ」と「ズレ」をどちらも全長と同じにすることで、画面上では線が完全に隠れた状態になります。
Step 4:スクロールアニメーションの設定
gsap.to(path, {
strokeDashoffset: 0, // 0に戻して線を描く
ease: "none",
scrollTrigger: {
trigger: el,
start: "top center",
end: "center center",
scrub: 1,
}
});
最後に gsap.to でアニメーションを実行します。
strokeDashoffset を 0 に戻すことで、隠れていた実線部分が画面内に引き戻され、線が描かれていきます。
- ease: “none”
デフォルトの「動き出しがゆっくり」などのイージングを解除し、スクロールと等速で描かれるようにします。 - scrub: 1
スクロールに追従させます。trueではなく数値を指定することで、ピタッと止まらず「1秒遅れて追いつく」ような余韻が生まれ、リッチな操作感になります。
完成アニメーション
完成したものがこちらです。
正確な長さを取得しているため、最後まで気持ちよく線が描かれています。
円形以外のSVGにも差し替えてみました。 コードの変更は一切なく、HTMLのSVGタグを変えただけで、このように複雑な波線もきれいに描画されます。
おわりに
いかがだったでしょうか。
今回は「getTotalLength」を活用して、メンテナンス性の高いSVGラインアニメーションを実装しました。
一度仕組みを作ってしまえば、ロゴや手書き文字など、あらゆるSVGアニメーションに応用が効きます。ぜひプロジェクトに取り入れてみてください。