SVGのtransform-originにハマった話(IE,safari,マルチブラウザ対応)

Blog

こんばんは。コーダーのシマです。
先日たずさわった案件で、SVGにcssアニメーションを付けました。

私、普段のメインブラウザChromeなのですが、いざ実装して確認してみると、
IE/edgeで。。。transformが効いてない!
safari(9)で。。。なんかずれてる!
という悲しい事態に。

というわけで、備忘録兼ねて、実装時のTIPSを共有したいと思います。

準備

まずはSVGの素材を用意しましょう。
今回はICOON MONOさんより、エビの素材をお借りしました。
(初期表示がナナメになっているのでサンプルとしては不向きだったなと、書きながら気づきました。爆)

.svg形式でDLしたら、イラレで開きましょう。(私のMacのイラレが古いのはスルーしてください)
ナナメだとわかりづらいので、回転させて上を向かせちゃいましょう。(←)
動かす分の余白を考慮して、キャンバスサイズ512pxに対してエビの体長は480pxにしました。
キャンバスの中央に配置したら準備OKです。

レイヤーのグループ化

レイヤーがバラけているので、動かしたい単位でグループ化していきます。
選択ツールの状態で、「Shift+クリック」で複数選択し「Cmd+G」でグループとしてまとめることが出来ます。
動かしたい単位でグループを分けたら、グループ/レイヤーにidとなる名前をつけていきます。
アンダーバーは文字化けするので、ハイフンを使いましょう。

SVGを貼り付ける

グループ化した.svgファイルをテキストエディタで開いて、
必要な箇所だけになるよう(短く・見やすくするよう)コードを整形したら、htmlに貼り付けます。
無事表示されたら、動かす準備完了です!

※ イラレで名前をつけたグループは通常idで出力されますが、サンプルではclassに置き換えています。
※ 512pxをまま表示すると大きすぎるので、wrapperに128pxを設定しています。

See the Pen shrimp by Miki Shima(EVOWORX) (@shima_evo) on CodePen.

アニメーション

アニメーション自体は、簡単なcssのkeyframeアニメーションを使ってつけていきます。
例えば右手(.handR)の回転は、下記のようにtransform:rotate()の値を変化させているだけです。
.shrimp.-animeがつくと、アニメがつく想定です。

@keyframes rightHand {
    0% {
        transform: rotate(0deg);
    }
    50% {
        transform: rotate(5deg);
    }
    100% {
        transform: rotate(0deg);
    }
}

.shrimp.-anime .handR {
    transform-origin: 58.7645% 50.5281%; //ここがポイント
    animation-name: rightHand;
    animation-duration: 1.2s;
    animation-timing-function: linear;
    animation-iteration-count: infinite;
}

このようにtransformで動きをつける場合、回転軸(transform-origin)の設定がポイントになります。

<ポイント>transform-originの設定

通常のhtml要素の場合、transform-originの初期値は (50%,50%)ですが、SVGの中身(<path><g>)の場合、transform-originの初期値はキャンバス内の(0,0)になっているため、transformをかけただけでは意図した動きになってくれません。(こちらの記事が参考になります)

そこで、回転軸としたい座標を、キャンバス内での相対位置に変換してあげましょう。
(簡単に言うと、ここを基準にしたい!という座標を、キャンバスサイズで割れば良い)

座標の取得には再びイラレを使います。
回転軸にしたいパーツを選択して、「基準点」パネルから座標を確認することが出来ます。

腕を動かすとき、人間でいうと肩から動くのが自然ですよね。
ということで、胴体から出ている関節パーツの、左下を基準点として回転軸を設定していきたいと思います。

# 右手の回転軸の求め方
X軸: 300.874px / 512px(キャンバスwidth) * 100% = 58.764453125%
Y軸: 258.704px / 512px(キャンバスheight) * 100% = 50.528125%

ずれると厄介なので、ここは嫌がらず小数点も含めて計算してあげます。
この値をtransform-originに設定すると、意図した動きになりました!

See the Pen shrimp_anime by Miki Shima(EVOWORX) (@shima_evo) on CodePen.

ブラウザによる不具合

さて、今皆さまはこちらのブログを何でご覧いただいているでしょうか。
Chromeとfirefoxの方は、エビの右ハサミが、関節(人間でいうところの肩)から動いているように見えるかと思います。

しかし、IE/edgeの方はおそらくアニメ自体動いておらず、
古いsafariで見ている方は、なんだかエビがワキワキしているような動きに見える方がいるかと思います。
調べてみた結果、これらは下記が原因でないかという推測に至りました。

  • IE/edgeは、SVG要素ならtransformを扱えるものの、SVGの中身の<path><g>にはtransformを適用できない
  • 最新環境でないsafariでは、tranform-originの設定がうまく出来ない

※iOS11のsafariでも同様の現象になった方がいたようで、こちら参考にtransform-boxの値をセットしたら解決するかと思ったのですが、古いsafari(9)だと-webkit-prefixをつけても、そもそもプロパティ自体認識されませんでした。。

解決策

SVGで扱えるようにしてあげれば、とりあえず全ブラウザカバー出来る

さて、全ブラウザでtransform-originを適用するにはどうしたらよいものか、、至った結論は、<g>タグでかこっているものを、SVGに分割する」です。

動かしたいパーツを、グループではなく単独のSVGとして書き出して、「複数グループで構成される1つのSVG」から、「複数SVGで構成される1つの要素」に変えてあげます。(レイヤーを重ねるイメージ)

SVGを分割する

下記の手順で構造をかえていきます。

①先ほど整形したsvgデータをグループ分複製し、書き出したいパーツのみ残して保存していきます。group名として指定していたidは、(最上位の)SVG自体のidになるよう設定します。
  ▪キャンバスサイズは各SVGすべて同じにしておくと、位置揃えがラクです。
  ▪座標をとるためにSVG化するので、なかにgroupがあっても問題ありません。
②親となるラッパーを用意し、width:表示サイズ; height:表示サイズ; position:relative;を設定します。
③子となるSVGたちは、width:100%; height:100%; にして position:absolute;top:0; left:0; にします。(親コンテナのサイズが変わっても大丈夫)
④先ほど設定したアニメと同様の方法で、transform-originを設定します。

①右手だけのSVGを作成

これで、無事全ブラウザでtransform-originがきいて、動くようになりました!とりあえず一安心。

See the Pen shrimp_svg by Miki Shima(EVOWORX) (@shima_evo) on CodePen.

注意

SVG化した場合でも、IEではfillなどSVGの中身に対してのcssでの状態変化は適用できないようですので、お気をつけ下さい。

繊細・複雑なアニメーションをしたい場合には、jsやライブラリを使うほうが扱いやすいのが現状かもしれませんが、
(TweenMaxはブラウザ間のtransform-origin問題を吸収してくれるよ〜っていう記事
html+cssで、ちょっとしたSVGアニメーションを実装したいな〜という際には、こちらのTIPS、お役に立つ場面あれば幸いです。

ではでは!???