はじめに
Webデザインにおいて、視覚的なインパクトを与えるアニメーションは非常に効果的です。その中でもSVGを使用したストロークアニメーションは、軽量でスムーズな動きが特徴で、ユーザー体験を向上させることができます。本記事では、SVGのストロークアニメーションをCSSだけで作る方法とJavaScriptでパス長を正確に取得する方法を、そのままコピーして動くコード付きで解説します。
ストロークアニメーションとは?
SVGストロークアニメーションとは、stroke-dasharray と stroke-dashoffset の2つのプロパティを使い、線が手で描かれていくように見せるSVGの演出技法です。パスを「破線」として扱い、その破線をずらして隠した状態から元に戻すことで、線が徐々に現れるエフェクトを作ります。CSSだけでも、JavaScriptでパスの全長を取得しても実装できます。
See the Pen Untitled by masakazuimai (@masakazuimai) on CodePen.
仕組み:stroke-dasharray と stroke-dashoffset
仕組みはシンプルです。まず stroke-dasharray にパスの全長以上の値を指定して、線全体を「1本の長い破線」として扱います。次に同じ値を stroke-dashoffset に入れると、破線が丸ごとずれて線が消えた状態になります。あとはこのオフセットを 0 に向けてアニメーションさせれば、線が描かれていくように見えます。
.path {
stroke-dasharray: 1000; /* 線全体を1本の長い破線として扱う */
stroke-dashoffset: 1000; /* 破線を全部ずらして“消した”状態にする */
animation: draw 3s ease forwards;
}
@keyframes draw {
to { stroke-dashoffset: 0; } /* 0に戻す=線が描かれていく */
}CSSのみで実装する場合に必要な「パスの全長」は、ブラウザの開発者ツールのコンソールで次の1行を実行するとすぐ確認できます。ここで得た値を stroke-dasharray に使えば、無駄な概算を避けられます。
// DevToolsのコンソールで実行するとパスの全長が分かる
document.querySelector('.draw-path').getTotalLength();関係するプロパティを整理すると次のとおりです。
| プロパティ | 役割 |
|---|---|
stroke-dasharray | 線を破線パターンにする。全長以上の1値を指定して「まだ描かれていない」状態を作る |
stroke-dashoffset | 破線の開始位置をずらす。全長→0でアニメーションさせると「描かれていく」動きになる |
stroke-linecap | 線の端の形状を決める(butt / round / square) |
vector-effect: non-scaling-stroke | 拡大縮小しても線幅を一定に保つ(レスポンシブで有効) |
CSSだけで実装する(コピペ可)
もっとも手軽なのはCSSだけで完結させる方法です。SVGのパスにクラスを付け、stroke-dasharray と stroke-dashoffset にパスの全長以上の固定値を入れてアニメーションさせます。まずはHTML(SVG)です。
<svg viewBox="0 0 200 100" width="200" height="100">
<path d="M10 50 Q 60 10, 110 50 T 190 50"
fill="none" stroke="#4f46e5" stroke-width="4"
stroke-linecap="round" class="draw-path" />
</svg>続いてCSSです。このまま貼り付ければ、ページ読み込み時に線が描かれます。
.draw-path {
stroke-dasharray: 300; /* パスの全長以上の固定値を指定 */
stroke-dashoffset: 300; /* 同じ値でいったん線を隠す */
animation: draw 2.5s ease-out forwards;
}
@keyframes draw {
to { stroke-dashoffset: 0; }
}注意点は stroke-dasharray の値です。パスの実際の全長より小さいと線の途中で止まり、大きすぎると描き始めに間(ま)ができます。パスを変えるたびに数値を調整する必要があるのがCSSのみの弱点で、これを自動化したいときは次のJavaScriptの方法が便利です。
JavaScriptでパス長を正確に取得する
パスの全長を手で測らずに済ませるには、getTotalLength() を使います。パスの正確な長さを取得して stroke-dasharray と stroke-dashoffset に設定するので、パスを変更してもコードを直す必要がありません。
const path = document.getElementById('logo');
const length = path.getTotalLength(); // パスの全長を正確に取得
path.style.strokeDasharray = length;
path.style.strokeDashoffset = length;
path.getBoundingClientRect(); // 再描画を強制(オフセット反映のため)
path.style.transition = 'stroke-dashoffset 2.5s ease-out';
path.style.strokeDashoffset = '0'; // 0に向けて線を描画CSSのみとJavaScript制御は、それぞれ向き不向きがあります。用途で選びましょう。
| 観点 | CSSのみ | JavaScript(getTotalLength) |
|---|---|---|
| パス長の指定 | 全長以上を手動で概算 | 自動で正確に取得 |
| パス変更への追従 | 都度dasharrayを再調整 | コードがそのまま追従 |
| 発火タイミング | 読み込み時・hoverなど | スクロール連動・任意のイベント |
| 複数パスの順次描画 | delayを手計算 | ループで自動化できる |
| 向いている場面 | 単純な1パスの装飾 | ロゴ署名・スクロール演出・複数パス |
このアニメーションの特徴
このストロークアニメーションは以下のような特徴を持っています:
- 軽量
SVGはベクター形式で記述されるため、画像形式よりも軽量で高解像度を維持できます。 - 滑らかな動き
CSSトランジションを使用することで、ストロークが描画される様子をスムーズに表現しています。 - カスタマイズ性
色や線の幅、アニメーション速度を簡単に調整できるため、様々なデザインに応用可能です。
どんな場面で使える?
このようなアニメーションは、以下のようなシーンで効果を発揮します:
- ローディング画面
ページやコンテンツが読み込まれる間に表示されると、待機時間が短く感じられます。 - アイコンの強調
特定の操作を促すボタンやアイコンの周囲に動きを与えることで、視覚的な注意を引きつけることができます。 - インタラクティブな要素
ホバーやクリック時にアニメーションを発動することで、ユーザーとのインタラクションを強化します。
実務Tips(ベストプラクティス集)
複雑すぎるパスを避ける
SVGパスが極端に複雑だと、アニメーションがカクついたり描画負荷が高くなります。必要に応じてIllustratorやFigmaでアンカーポイントを減らし、パスを最適化しましょう。
カクつきは will-change で抑える
アニメーション対象に will-change を指定すると、ブラウザが描画を最適化しやすくなり、カクつきを軽減できます。多用は禁物ですが、主役のパスに限定して使うのが効果的です。
.draw-path {
will-change: stroke-dashoffset; /* 描画を最適化してカクつきを抑える */
}複数のパスを順番に描画する
ロゴや文字など複数のパスを順番に描きたいときは、各パスの全長を取得し、animation-delay を少しずつずらします。JavaScriptならループで自動化できます。
document.querySelectorAll('.draw-path').forEach((p, i) => {
const len = p.getTotalLength();
p.style.strokeDasharray = len;
p.style.strokeDashoffset = len;
p.style.animation = `draw 2s ease forwards ${i * 0.4}s`; // 0.4秒ずつ遅らせる
});レスポンシブは vector-effect で線幅を保つ
SVGは拡大縮小に強いですが、viewBox を必ず設定し、固定幅を避けて width="100%" を使うと崩れにくくなります。さらに vector-effect: non-scaling-stroke を指定すると、拡大しても線幅が太くならず一定に保てます。
prefers-reduced-motion でアクセシビリティに配慮する
動きを抑えたいユーザー向けに、prefers-reduced-motion でアニメーションを無効化し、完成形を即座に表示する分岐を入れておくと親切です。
@media (prefers-reduced-motion: reduce) {
.draw-path {
animation: none; /* アニメーションを無効化 */
stroke-dashoffset: 0; /* 完成形を即表示 */
}
}フォールバックを用意する
ごく一部の環境ではSVGアニメーションが意図通り動かないことがあります。重要な情報を線画だけに頼らせず、静的なSVG/PNGでも内容が伝わるようにしておくと安全です。
うまく描画されないときのチェックリスト
ストロークアニメーションが思いどおりに動かないときは、原因の多くが次の5つに集約されます。上から順に確認すると早く解決できます。
- 線が途中で止まる →
stroke-dasharrayがパスの全長より小さい。全長以上の値(迷ったらgetTotalLength()の値)を入れる。 - 塗りつぶされて線画に見えない →
pathにfill="none"が無い。塗りが残ると描画演出が見えない。 - アニメーション後に線が消える →
animationにforwardsが無い。最終状態を保持するにはforwardsを指定する。 - そもそも線が見えない →
strokeとstroke-widthが未指定。色と太さを必ず指定する。 - 描き始めに不自然な間ができる →
stroke-dasharrayが全長より大きすぎる。全長に近い値へ調整する。
応用と次の一手
ここまでの仕組みは、ロゴの「署名のように描かれる」演出、ローディングインジケーター、アイコンの強調など、幅広く応用できます。まずは1パスのCSS実装で動きを掴み、複数パスやスクロール連動が必要になったらJavaScript制御へ広げる、という順番がおすすめです。
次の一手として、動いているコードをそのままAIに渡して改造する方法があります。色・速度・対象要素だけを差分で指示すれば、ゼロから生成させるより速く確実です。詳しくは CSSアニメーションはAIに「コードを渡して」作る で解説しています。
よくある質問
Q. ストロークアニメーションに必須のプロパティは?
A. 基本は stroke-dasharray と stroke-dashoffset の2つです。これらをアニメーションさせることで「線が描かれる演出」を作れます。
Q. パスの長さはどうやって取得しますか?
A. JavaScriptの getTotalLength() メソッドで正確に取得できます。取得した値を stroke-dasharray と stroke-dashoffset に設定するのが定番です。
Q. CSSだけで実装できますか?
A. はい、単純なアニメーションならCSSだけで可能です。stroke-dasharray にパスの全長以上の固定値を入れれば動きます。複雑な制御や複数パスの調整にはJavaScriptが便利です。
Q. レスポンシブ対応にする方法は?
A. viewBox を設定し、固定幅を避けて width="100%" を指定します。線幅を一定に保ちたいときは vector-effect: non-scaling-stroke も有効です。
Q. アニメーションがカクつくのはなぜ?
A. パスのアンカーポイントが多すぎる場合や描画処理が重いとカクつきます。パスの簡略化、イージング調整、will-change の指定を検討してください。
Q. ロゴアニメーションにも使えますか?
A. はい、特にロゴやアイコンに「描かれる」演出をつけるとブランディング効果が高まります。複数パスを順番に描画すると、より手書きらしい仕上がりになります。
参考リンク
本記事のプロパティ・メソッドの仕様は、MDN Web Docsの公式ドキュメントに基づいています。
- MDN Web Docs「stroke-dasharray」
- MDN Web Docs「SVGGeometryElement.getTotalLength()」
まとめ
SVGストロークアニメーションは、stroke-dasharray と stroke-dashoffset の2つだけで「線が描かれる」演出を作れる、軽量で応用範囲の広い手法です。まずはCSSだけのコピペ実装で動きを確認し、パスの変更に追従させたい・複数パスを順番に描きたいときはJavaScriptの getTotalLength() へ広げましょう。will-change や prefers-reduced-motion まで押さえれば、実務でそのまま使える品質になります。
