Design Repot|デザインレポート

荒ぶるトランスフォームアニメーション―css transformの罠。

は基本的にJavaScriptを演出のために使用する。(はっきりいって正しい使い方ではない) その為、スクリプトに関する造詣はアニメーションや視覚的変化を生む為の知識に特化している。 ゆえにこの「Individual Research」でも概ね演出面での使用に関する内容を書き綴ることになると思うのでそのつもりで。 今回は「css transformを用いたアニメーショントゥイーンでの挙動について」 rotate()後の変形に際し、独特な挙動を発見したのでここに記しておく。

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

このようにwidth、heightの値を変更した際は、その場で拡大されるのではなく、
  • 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);
});

同時

ここでleftのposition移動が活きてくる。
  • 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の倍数で回転角度を調整すること
※Y方向ならtop(bottom)かtranslateY、
X方向ならleft(right)とtop(bottom)かtranslat(X,Y)で

裏話

今回の検証は、元々はウチ(Glitter Style)のWebサイトでスクロール量に応じて『バーが回転しながら位置を変えていく』というスクロールイベントを作成した際に、「何故か回転の中心がズレる」「position移動を行っていないのに、ズレた位置へ移動する」という現象があり、こうした挙動を発見した。
実のところ、当初は途中でtransform-originも切り替えようと考えていたが、上記のようにあまりに破天荒な動きをする為、断念したという・・。

Comments

Add a Comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

認証コード * Time limit is exhausted. Please reload CAPTCHA.

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください