tech.sinayaka.com

SafariでのSVGモーフィング

2024-05-09
2025-07-13
6分
1016語
Web技術 svgアニメーション

ダークモード実装時、iPhoneで確認したときにアイコンがモーフィングせずに「じわー」っとトランジション したので気になって調べました。

Safariではpathを入れ替えたアニメーションはできない

Javascriptでpathのdを入れ替えることで綺麗にモーフィングしてくれます。 ただしパス同士のセグメント数(パスコマンド)が一致している必要があります。

sun_moon_morphing
sun_moon_morphing

👆これをSafariで確認するとディゾルブしていました👇

sun_moon_dissolve
sun_moon_dissolve

いろいろ試したところ2つのことがわかりました。

  • Safariではpathのd属性を入れ替えてもアニメーション(モーフィング)されない
  • arcコマンドで半径が0ではパスが表示されない

セグメント数をそろえるために、消したい要素は座標を指定して(上記の例では中央に)大きさを0にしていたのですが、 そもそもそういった作法をSafariは許してはくれませんでした。

animateタグ

調べていると、同じようにd属性を入れ替えている人の嘆きや、GSAPなどのライブラリの紹介等しかなく、 何となく自分で行きついた答えを紹介しておきます。
それはanimateタグをJavascript駆動することです。

animateの属性には以下のものがあります。

基本属性説明
attributeNameアニメーション対象の属性(例:x, y, opacity, d など)
attributeType属性のタイプ。CSSXMLauto(省略可)
値と変化の制御説明
fromアニメーション開始値
toアニメーション終了値
by開始値からの相対変化値
values; 区切りで複数の値を指定(例:values="10;20;10"
keyTimesvalues に対応したタイミング(0〜1)を指定(例:keyTimes="0;0.5;1"
keySplinescalcMode="spline" 時のイージングをベジェ曲線で指定(例:.42 0 .58 1
calcMode補間の方法。discrete(不連続)、linear(線形)、pacedspline など
時間制御説明
begin開始タイミング(例:2sclickindefinite
durアニメーションの継続時間(例:3s
endアニメーションを終了するタイミング(あまり使われない)
repeatCount繰り返し回数(例:indefinite, 1, 3
repeatDur繰り返し全体の期間(例:10s
fill終了後の状態:remove(初期値)か freeze(終了状態を保持)
イベント連携とID関連説明
idアニメーション要素のID
begin="otherID.end"他アニメーションの終了をトリガーに開始
begin="click"対象要素がクリックされたら開始
begin="indefinite"JSの beginElement() で手動再生するために使用
restartalways, whenNotActive, never などでリスタート可否を指定

svgのpathでモーフィングアニメーション

pathで初期状態のd値を設定しておき、begin="indefinite" fill="freeze"で Javascript駆動と終了状態の保持を設定したうえで、それぞれto属性に変化後のセグメント情報を入力しておきます。

Javascriptでd属性やto属性をどうのこうのしようとしてもSafariは許してくれません。 初期状態の値と変化後の値、そして戻るときの値(初期状態と同じ)を入力しておかなくてはいけないので ムダが嫌いな効率人間には厳しい鍛錬となります。

そして変化後のセグメント情報のarcコマンドでは、半径を0.001として存在を認めてあげることも忘れてはなりません。 「一寸の虫にも五分の魂」については思うところがあり、 小さな虫けらにはしょせん小さな魂しか入っていないという印象を受けて育ちました。

<svg width="24" height="24" viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg">
  <path d="M 64 9.3 A 7.3 7.3 0 0 0 56.7 16.6 ... Z" >
    <animate id="to_dark" attributeName="d" fill="freeze"
      begin="indefinite" dur="1s" to="M 64,64 a 0.001,0.001 0 0 0 0,0 ... Z"/>
    <animate id="to_light" attributeName="d" fill="freeze"
      begin="indefinite" dur="1s" to="M 64 9.3 A 7.3 7.3 0 0 0 56.7 16.6 ... Z"/>
  </path>
</svg>
<script>
function toggleDark(to_dark) {
  if (to_dark) document.getElementById('to_dark').beginElement();
  else document.getElementById('to_light').beginElement();
}
</script>

終わりに

今回の騒動でsvgについて少しは理解が深まった気がします。対応ブラウザのことが気になりますが、 見えている世界しか見えないので気にしないようにします。




Copyright 2025
サイトマップ