PerfectVector
著者: Irene Kim2 分で読める

SVGのモーションパス:独自アイコンを曲線に沿って動かす

animateMotionとmpathでSVGを曲線に沿って動かします。アイコンの位置ずれを直し、自動回転を選び、動きを減らす設定に対応した静止版を用意します。

目次

SVG要素を経路に沿って動かすにはanimateMotionを使い、その中のmpathで既存のパスを参照します。rotate="auto"は、移動する画像を経路の方向に向けます。パス上を移動する点は画像のローカル原点なので、読み込んだアイコンには意図した基準点を設定する必要があります。

独自の矢印、エンブレム、小さなイラストを、形を変えずに移動させたい場合に役立ちます。線が自分で描かれるように見せるにはストロークアニメーションを使います。ある輪郭を別の輪郭へ変えるにはパスのモーフィングを使います。それぞれ異なる表現を実現する手法です。

経路と画像を分ける

経路はアイコンの移動先を定義し、アイコンは読者に見える内容を定義します。MDNのanimateMotionリファレンスではモーション要素を説明し、mpathリファレンスでは参照によってパスを再利用する方法を示しています。

次の独自の例には、青緑の矢印と金色の点があります。矢印はX軸の正方向を向いているため、自動回転の効果を確認しやすくなっています。画像は、大きな書き出しキャンバスの遠い位置ではなく、(0, 0)の周囲に描いています。

motion.htmlとして保存してブラウザーで開いてください。最初は静止したエンブレムが表示されます。Playを押すと4秒間で1回移動し、Show staticを押すと開始時の静止画に戻ります。

<!doctype html>
<meta charset="utf-8">
<title>Arrow following a curve</title>
<style>
  svg { display: block; width: min(100%, 800px); }
  .moving { display: none; }
  .playing .moving { display: inline; }
  .playing .still { display: none; }
</style>
 
<svg id="scene" viewBox="0 0 480 240"
     xmlns="http://www.w3.org/2000/svg"
     role="img" aria-label="An arrow emblem follows a curved route">
  <defs>
    <path id="route" d="M60 180 C160 20 300 20 420 180" />
    <g id="emblem">
      <path fill="#147d78"
            d="M-18-12 H4 V-22 L30 0 L4 22 V12 H-18 Z" />
      <circle cx="-7" cy="0" r="4" fill="#e5a340" />
    </g>
  </defs>
 
  <use href="#route" fill="none" stroke="#a9bab6" stroke-width="2" />
  <use class="still" href="#emblem" transform="translate(60 180)" />
  <g class="moving">
    <use href="#emblem" />
    <animateMotion id="motion" dur="4s" begin="indefinite"
                   fill="freeze" rotate="auto">
      <mpath href="#route" />
    </animateMotion>
  </g>
</svg>
 
<button id="play" type="button">Play</button>
<button id="stop" type="button">Show static</button>
<script>
  const scene = document.querySelector("#scene");
  const motion = document.querySelector("#motion");
  const preference = matchMedia("(prefers-reduced-motion: reduce)");
 
  function showStatic() {
    scene.pauseAnimations();
    scene.classList.remove("playing");
  }
 
  document.querySelector("#play").addEventListener("click", () => {
    if (preference.matches) return;
    scene.pauseAnimations();
    scene.setCurrentTime(0);
    scene.classList.add("playing");
    motion.beginElement();
    scene.unpauseAnimations();
  });
  document.querySelector("#stop").addEventListener("click", showStatic);
  preference.addEventListener("change", showStatic);
  showStatic();
</script>

見えている灰色の曲線は、経路を別のuseで表示したものです。パスを見せない場合はそのuseを削除しますが、アニメーションから参照できるよう、defs内の定義は残します。モーション要素は、矢印と点を一緒に動かすグループ内に置きます。

fill="freeze"はアニメーションの最終状態を保持します。矢印の青緑の塗りとは関係ありません。この例は1回だけ実行するため、無限ループよりも最終位置を確認しやすくなっています。

曲線の経路に沿って向きを変える青緑の矢印の開始点、頂点付近、終点の図
モーションパス上の3つの姿勢の図解です。自動回転はその位置の経路方向に従い、アイコンの形は維持されます。

経路から離れて動くアイコンを直す

モーションは要素の座標系に適用されます。ロゴの見た目の中心を自動で見つけるわけではありません。読み込んだパスが(200, 120)付近の座標にある場合、それを含むグループを経路上で動かしても、グループ内のずれは残ります。

画像のどの点を経路に合わせるか選び、動くグループ内でその点を(0, 0)へ移動します。基準点が(200, 120)なら、構造は次のようになります。

<g class="moving">
  <g transform="translate(-200 -120)">
    <!-- Place the original artwork paths here. -->
  </g>
  <animateMotion dur="4s" rotate="auto">
    <mpath href="#route" />
  </animateMotion>
</g>

内側の移動変換で画像を準備し、外側のグループにモーションを適用します。拡大縮小や補正回転を追加するときも、それぞれの役割を分けてください。SVGのアニメーション仕様では、モーションを追加の変換として定義しているため、既存の変換も最終配置に影響します。

バッジなら中心が適切な基準点かもしれません。車両なら車輪の接地点、ポインターなら先端を選びたい場合があります。境界ボックスの中心は、最初の目安にすぎません。経路上の複数の位置で、意図した接点を確認してください。

その場で回転する場合にも、回転中心の問題があります。SVGのtransform-originガイドでは、移動経路を追加せずにその問題を説明しています。

画像を回転させるか選ぶ

目的モーション設定画像の準備
矢印を曲線の方向に向けるrotate="auto"前方をX軸の正方向に向けます
バッジを直立したまま移動させるrotate="0"ローカル画像を希望する固定方向にしておきます
経路方向と反対に向けるrotate="auto-reverse"使用前に元画像の前方を確認します

これらの値はMDNのモーションの回転リファレンスで説明されています。auto-reverseは向きを変えるだけで、経路をたどる順序は逆にしません。

元画像が上向きの場合、autoは上が前方だとは認識しません。内側の画像グループを回転してX軸の正方向に向け、外側のグループにモーションを適用してください。アイコンのローカル方向を補正するためだけに、経路自体を回転させることは避けます。

経路の鋭い角では、方向が急に変わることがあります。滑らかな旋回を見せたい場合は、曲線の経路から始めるとよいでしょう。また、経路の周囲に余裕を残してください。アイコンはパスの中心線からはみ出すため、SVGビューポートの端で切れる場合があります。

開始点、中間、終点を確認する

同じ経路とエンブレムをChromeで開き、小さなテストツールでSVGのタイムラインを0、2、4秒に一時停止しました。アイコンは左下から始まり、頂点付近を通って右下で終わりました。autoでは曲線に沿って向きが変わりました。回転を0にすると、中間の位置は同じまま、矢印は水平を保ちました。

これは1つのブラウザーで、この素材を確認した結果です。デザインツールへのアニメーション読み込みのテストでも、あらゆるブラウザーの組み込み方での動作保証でもありません。

自分の画像では、次を確認します。

  1. 経路の開始点で接点を確認します。タイミングを調整する前に、元画像のずれを直します。
  2. 曲がる付近で一時停止し、エンブレムの意図した部分が線に沿うか確認します。
  3. 自動回転と固定回転を比較します。特に文字を含む場合は、画像に合わせて選びます。
  4. 最後の姿勢で切れがないか確認し、再生をやり直したとき正しく始まるか確認します。
  5. 実際の納品先でテストします。この例は、JavaScriptの操作ボタンを備えたHTMLページ内のインラインSVGです。

MDNが説明するように、animateMotionは既定で移動距離に応じた一定速度の動きを使います。そのため、時間の半分はおおむね移動距離の半分に相当し、必ずしも制御点間の曲線パラメーターの中間ではありません。今回の非対称な3次曲線では、2秒時点の姿勢は幾何学的な頂点より少し右でした。

役立つ静止版を用意する

この例はエンブレムの静止したuseから始まり、Playボタンを操作して初めて動きます。JavaScriptが動作しなければ、静止した画像が見えたままです。動きを減らす設定が有効なら、イベントハンドラーはその状態を維持します。

prefers-reduced-motionメディア機能により、ページはユーザーの動きの設定に対応できます。ここではJavaScriptがSVGの移動を始める前に設定を読み、設定が変わると静止版に戻します。

テストツールの明示的な「動きを減らす」プレビュー切り替えでも、静止表示の分岐を確認しました。アニメーションのグループが隠れ、開始位置にエンブレムが表示されました。この切り替えは代替表示のロジックを実行したもので、コンピューターのアクセシビリティ設定は変更していません。

可能な限り、意味はアニメーションの外に置いてください。状態の把握や重要な説明を読むために、移動するエンブレムを見続ける必要がないようにします。すでに説明的なテキストの横にある装飾アイコンでは、同じ内容を二重に読み上げないよう、アクセシビリティ用のラベルを調整します。

動かす前に独自のモチーフを準備する

独自のモチーフがPNGやJPGしかない場合は、動きを作る前にPerfectVectorで編集可能なSVGを復元できます。復元した輪郭と小さな開口部を確認し、一緒に移動する部分をグループ化して、基準点を選びます。PerfectVectorは元の形状を提供し、経路、タイミング、動作は別途作成します。

既存のSVGなら、そのままグループ化と基準点の準備に進めます。このデモのような単純な矢印なら、形状を直接描く方法も実用的です。画像のベクター化の概要では、ラスター画像のトレースが役立つ場面を説明しています。

ラスターしかない独自のマークでは、PerfectVectorで画像を試し、輪郭と細部を確認してから、SVGを経路の開始点、中間、終点でテストしてください。アニメーション版とともに静止版を保持します。

よくある質問

アイコンがパス上ではなく横を移動するのはなぜですか? 画像がローカル原点からずれている可能性があります。経路に沿わせたい点を選び、動くグループ内でその点をゼロへ移動してください。開始点と曲がる位置で結果を確認します。

自動回転でアイコンの形は変わりますか? いいえ。モーションは画像の配置と方向を変えますが、パス形状はモーフィングしません。この例の自動回転では、元画像の前方をX軸の正方向に向けておく必要があります。

見えているガイド線を消せますか? はい。経路の定義とIDを残し、ガイド線を描画する別の要素を削除してください。モーションは引き続きmpathを通じてパスを参照できます。

参考資料

  1. MDN animateMotion — モーションパス、回転値、既定の距離に応じた移動を説明しています。
  2. MDN mpath — モーション要素内で既存の経路を参照する方法を説明しています。
  3. SVGのアニメーション仕様 — モーション変換と画像座標の関係を定義しています。
  4. MDN rotate属性 — 固定、自動、逆向きの移動姿勢を説明しています。
  5. MDNの動きを減らす設定 — ユーザーの動きの設定を検出する方法を説明しています。

ブログのその他の記事

編集しやすい
よりきれいなSVGから始めましょう