荒ぶるトランスフォームアニメーション―css transformの罠。
Point
- 要素をtransform:rotate()で回転させた後に拡大等の変形を加えたい
- 要素の中身の大きさを変えたくないので、要素のサイズ変更はtransform:scale以外で拡大
- jQueryアニメーションでtransformを使用する為に「transit.js」を使用
仕様:100px×100pxの正方形の要素(paddingとtop:10px,left:100pxのポジション指定有り)に対し
rotate(90deg)→height:300px→width:500px→left:0とプロパティを1秒毎に加えていくトゥイーン。
css
.box{ width:80px; height:80px; padding:10px; background-color:#d7003a; position:absolute; top:10px; left:100px; }
JavaScript(jQuery)
$(function(){ $('#box').animate({rotate:'90deg'},1000) .animate({height:'300px'},1000) .animate({width:'500px'},1000) .animate({left:'0px'},1000); });
ソースは上記。
クラスでスタイルを指定し、イベント用にスタイル指定の無いIDを付与。
アニメーションは単純にメソッドチェーンで繋ぐだけの設定で分かりやすいように縦に羅列。
Demo1:width、heightで拡大
transit.jsだけの特殊な挙動ではないことの裏付けとして、下にcss animationで同様の変形トゥイーンを並べて同時に動かす。
※easeingの違いにより、微妙なタイミングのズレは有り。
@keyframes test{ 25%{transform:rotate(90deg);height:80px;} 50%{height:300px;width:80px;} 75%{width:500px;left:100px} 100%{transform:rotate(90deg);left:0;height:300px;width:500px;} }
transit.js
CSS animation
- heightでは画面下方向へ
- widthでは画面右上方向へ
※transform:rotateの値が正負問わず同様の動きをする
※サイズ変形の大きさと移動距離は比例する
また、position設定のtop、bottom、left、rightは「回転後の要素から見て」ではなく、あくまでも通常と同じように働く。 (この辺はtransformの仕様上当然と言えば当然か・・。)
ちなみに
この現象はpaddingで行っても同様になる。
Demo2:width,heightの拡大をpaddingで置き換えた場合
Demo1のwidth,heightで拡大した分とpaddingでの拡大した最終的なサイズを同じに。
$(function(){ $('#box').animate({rotate:'90deg'},1000) .animate({padding:'100px 10px'},1000) .animate({padding:'100px 200px'},1000) .animate({left:'0px'},1000); });
@keyframes test{ 25%{transform:rotate(90deg);height:80px;} 50%{padding:100px 10px;} 75%{padding:100px 200px;left:100px;} 100%{transform:rotate(90deg);left:0;padding:100px 200px;} }
transit.js
CSS animation
また、
縮小時は移動方向が逆になる。
Demo3:width,heightを縮小した場合
Demo2の通り、paddingで置き換えた場合もまったく同じ結果になるので割愛。
$(function(){ $('#box').animate({rotate:'90deg'},1000) .animate({height:'30px'},1000) .animate({width:'20px'},1000) .animate({left:'0px'},1000); });
@keyframes test{ 25%{transform:rotate(90deg);height:80px;} 50%{height:30px;width:80px;} 75%{width:20px;left:100px} 100%{transform:rotate(90deg);left:0;height:30px;width:20px;} }
transit.js
CSS animation
- Y方向の変形時、X軸(拡大ならば下/縮小ならば上)へ
- X方向の変形時、XY軸の対角線上(拡大ならば右上/縮小なら左下)へ
- 変形の大きさと比例した量、ズレる
ただ、
角度が0,180,360度など、180の倍数時(正負問わず)には起こらない。
Demo4:rotateを180deg(180度回転)に設定した場合
$(function(){ $('#box').animate({rotate:'180deg'},1000) .animate({height:'300px'},1000) .animate({width:'500px'},1000) .animate({left:'0px'},1000); });
@keyframes test{ 25%{transform:rotate(180deg);height:80px;} 50%{height:300px;width:80px;} 75%{width:500px;left:100px} 100%{transform:rotate(180deg);left:0;height:300px;width:500px;} }
transit.js
CSS animation
常に要素の「左上起点」で変形が起こる。
本来はこちらが正しい仕様(のハズ・・)。
※こちらもrotateの値が正負問わず同様の結果になる。
そして・・
transform-originに変更を加えると、更にカオスな状況になる。
Demo5:荒ぶるトランスフォームアニメーション
最初のデモと同様の仕様でtransform-originを100% 100%(要素の右下起点)に変更。
$(function(){ $('#box').animate({rotate:'90deg'},1000) .animate({height:'300px'},1000) .animate({width:'500px'},1000) .animate({left:'0px'},1000); });
@keyframes test{ 25%{transform:rotate(90deg);height:80px;} 50%{height:300px;width:80px;} 75%{width:500px;left:100px} 100%{transform:rotate(90deg);left:0;height:300px;width:500px;} }
transit.js
CSS animation
「荒ぶる」・・というに相応しいほど画面内を要素が動きまわるようになる・・。
変形をすべて同時に行うと「最終的な結果」になる。
Demo6
$(function(){ $('#box').animate({rotate:'90deg',height:'300px',width:'500px',left:'0px'},1000); });
同時
- heightで「下に下がった分」とwidthで「右上に上がった分」でY軸のズレが相殺
- X軸のズレ分をleftのposition移動で相殺
となり、初期位置から(ほぼ)動いていないように見える。
leftやtop(right,bottom)の値を調節すれば色々できる。
- 変形を要素の中心起点で起こっているようにしたければ、left、topをもう少しマイナスしてやればいい
- 変形中に右へ移動していくようにしたければleftを初期値のままor移動させたい分足し引きして調整
scaleを用いればこうした現象に遭遇することはないが、
要素の中身を拡大縮小させたくない(ex. 画像の可視範囲を変えたいだけ、テキストのサイズは変えたくないetc..)際は
width,height,padding等での変形にすることになる。
その際はこのように、下記の注意点がある。
- Y方向への拡大時は下、X方向への拡大時は右上への座標ズレが生じる
- Y方向への縮小時は上、X方向への縮小時は左下への座標ズレが生じる
- 要素の位置を初期位置から動かしたくない場合は、
ズレを引き起こす変形と「同タイミング」で「同距離」「反対方向」へ動かす(※) - ズレを引き起こしたくない場合は、180の倍数で回転角度を調整すること
X方向ならleft(right)とtop(bottom)かtranslat(X,Y)で
裏話
今回の検証は、元々はウチ(Glitter Style)のWebサイトでスクロール量に応じて『バーが回転しながら位置を変えていく』というスクロールイベントを作成した際に、「何故か回転の中心がズレる」「position移動を行っていないのに、ズレた位置へ移動する」という現象があり、こうした挙動を発見した。
実のところ、当初は途中でtransform-originも切り替えようと考えていたが、上記のようにあまりに破天荒な動きをする為、断念したという・・。
Comments