初心者向けのjQuery入門講座|デザイナー向けのJavaScriptライブラリ

このエントリーをはてなブックマークに追加
索引
1章:jQuery入門
2章:jQuery基礎
3章:jQuery発展
番外編:研究

cssアニメとの連携(3)

概要

transformを利用したアニメ

前回、ハードウェアアクセラレーションが効くtransitionとtransformを利用したcssアニメを学びました。このtransformは移動先(座標)を指定するのではなく移動量を指定するため、目的の座標に移動させるには少し工夫が必要となります。

ここではtransformを利用して目的の座標へ移動するサンプルを完成させ、さらに移動が完了したら処理を実行できるようにしたいと思います。

追記:2014.6/29

iOS6ではtransitionにもベンダープリフィックスの-webkit-が必要ということが判明したので、記事とサンプル内のソースを変更しました。

解説

最初に配置された場所が基準

私が最初にtransfromを利用したアニメを作成した時にハマってしまった部分があります。間違いを繰り返さないために、まずは失敗例を確認します。サンプル(cssAnime3/01.html)のソースを開いて、body内の構成とcssの設定は前回のサンプル(cssAnime2/03b.html)から変更がないことを確認してください。

jQueryは大きく変更されています。transformは移動量を指定しなければならないので、以下の様な式で目的座標と現在の座標から移動量を算出しなければなりません。

座標から移動量を算出する式
移動量 = 目的座標 - 現在の座標
例えば現在の座標が200pxで目的座標が300pxなら、移動量は100pxとなります。

クリックした座標(目的座標)は前回説明したようにpageXプロパティpageYプロパティを利用します。そして、現在の要素位置はpositionメソッドで取得できます(下表2行目)。あとは上記の式に合わせて移動量moveXとmoveYを算出(3,4行目)して5行目でtranslateの値として利用するようにしました。

$(document).click(function(eo){
	var myPos = $("div").position();
	var moveX = eo.pageX - myPos.left;
	var moveY = eo.pageY - myPos.top;
	$("div").css("-webkit-transform","translate("+moveX+"px,"+moveY+"px)");
});

考え方は間違っていないと思うのですが実際にdocument上をクリックしても、その位置にdiv要素が移動しません。そこでもっとシンプルなサンプルで検証することにしました。

サンプル(cssAnime3/01b.html)を開いてjQueryが以下の様に変更されているのを確認してください。このサンプルではdiv要素をクリックするとX方向に100px移動するだけのサンプルです。

$("div").click(function(eo){
	$("div").css("-webkit-transform","translate(100px,0px)");
});

私の最初の予想ではdiv要素をクリックする度に右に100px移動すると考えていたのですが、実際にクリックすると最初の1回は移動しますが、2回目以降は移動しません。この結果から、基準となる座標は「現在の座標」ではなく「最初に配置された座標」ということが分かりました。つまり移動量を算出する式は以下の様に修正しなければなりません。

座標から移動量を算出する式
移動量 = 目的座標 - 最初に配置した座標
何回場所を移動しても、基となる座標は最初に配置した座標です。

ということでcssAnime3/01.htmlの様にクリックされる度に現在の座標を取得するのではなく、最初に配置された座標を利用するように変更します。サンプル(cssAnime3/02.html)を開いて、jQueryが以下の様に変更されているのを確認してください。

positionメソッドを利用して座標を取得する部分を、クリックのイベントハンドラの外に出しただけです。これでクリックする毎に現在の座標を取得するのではなく、最初に配置した座標を利用することになります。

var myPos = $("div").position();
$(document).click(function(eo){
	var moveX = eo.pageX - myPos.left;
	var moveY = eo.pageY - myPos.top;
	$("div").css("-webkit-transform","translate("+moveX+"px,"+moveY+"px)");
});

結果としてクリックされた場所に移動するようになりました。アプリの作成では移動が完了したら、何かしらの処理を実行したいケースが多くあります。ですので次はコールバックの設定について説明します。

アニメが終わったら処理を実行する(1)

animateメソッドは引数にfunctionを設定することで、アニメが完了したら実行する処理を簡単に設定できます。transitionを利用したアニメではこのようなコールバックを指定することはできませんが、transitionendというイベントが利用できるので、これをjQueryで利用して対応します。transitionendについては以下のサイトでも説明されています。 →参考:MDN「CSS transition の使用

ただし新しいブラウザでも対応していないためベンダープリフィックスが必要となり、
イベント名はwebkitTransitionEndとなります(サンプルはsafariかchoromeで確認してください)。

サンプル(cssAnime3/03.html)を開いてjQueryが以下の様に変更されているのを確認してください。メソッドチェーンでイベントハンドラメソッドのonを利用し、webkitTransitionEndのイベントが発生したらcssメソッドで背景色を変更(4行目)するようにしています。

var moveX = eo.pageX - myPos.left;
var moveY = eo.pageY - myPos.top;
$("div").css("-webkit-transform","translate("+moveX+"px,"+moveY+"px)").on("webkitTransitionEnd",function(){
	$("div").css("background-color","#F9F");
});

実際にdocumentをクリックしてdiv要素を移動させ、移動が終わったらdiv要素の背景色がピンクに変わる事を確認して下さい。

setTimeoutを利用する

前述のサンプルはPCでは問題ないようなのですが、Android端末でイベントが発生しないバグがあります。私がこのアプリを作成した際にもたくさん発生し、Androidでは実質webkitTransitionEndは利用できないと判断しました。日本語圏では報告例がないのですが、英語では報告例が幾つかあります。
→参考:google検索「android webkitTransitionEnd not fire

ブラウザ側の問題なようなので、webkitTransitionEndは利用せずにsetTimeoutを利用して問題を回避するようにしました。サンプル(cssAnime3/04.html)を開いて、jQueryが以下の様に変更されているのを確認してください。setTimeoutを利用して背景色を変更する処理を実行するようにしました。また時間はcssアニメに合わせて500ミリ秒にしています。

$(document).click(function(eo){
	var moveX = eo.pageX - myPos.left;
	var moveY = eo.pageY - myPos.top;
	$("div").css("-webkit-transform","translate("+moveX+"px,"+moveY+"px)");
	setTimeout('$("div").css("background-color","#F9F");',500);
});

実際にdocumentをクリックして、div要素が移動した後に背景色がピンクになることを確認して下さい。しかし、このサンプルは分かりやすいようにdiv要素が1つしかありません。実際にアプリ作成する時は複数の要素を管理しなければなりません。なので次項では複数のdiv要素があるときに、どのようにsetTimeoutを利用するかを説明します。

アニメが終わったら処理を実行する(2)

まずはsetTimeoutを利用せずに複数のdiv要素をアニメさせるサンプルを確認します。
サンプル(cssAnime3/05.html)を開いてbody内にdiv要素が5つ有ることを確認して下さい。 続いてcssは以下の様に設定されています。配置に関する設定が異なるだけでアニメの設定は変更有りません。

div{
	position:relative;
	width:50px;
	height:50px;
	margin:10px;
	background-color:#6CF;
	-webkit-transition-property:-webkit-transform;
	-webkit-transition-duration:0.5s;
}

jQueryは以下の様に記述され、div要素がクリックされたらクリックされたdiv要素(this)をアニメさせるようにしています。アニメはシンプルにX座標を500px移動させるだけです。

$("div").click(function(eo){
	$(this).css("-webkit-transform","translate(500px,0px)");
});

実際にdiv要素をクリックして、クリックした要素だけがアニメで移動することを確認して下さい。

次はsetTimeoutを追加するのですが、setTimeout内ではthisを利用することはできません。サンプル(cssAnime3/06.html)を開いてjQueryが以下の様に変更されているのを確認してください。

$("div").click(function(eo){
	$(this).css("-webkit-transform","translate(500px,0px)");
	setTimeout(function(){
		$(this).css("background-color","#F9F");
	},500);
});

考え方としては正しいのですが、setTimeout内でthisは利用できないのでアニメが完了しても色は変わりません。一番簡単な解決法としてはthisを別の変数に代入しておき、それを利用することです。

サンプル(cssAnime3/06b.html)を開いてjQueryが以下の様に変更されているのを確認してください。まず2行目でthisの要素を変数myObjに代入し、それを5行目のsetTimeout内の処理で利用するだけです。実際にdiv要素をクリックしてアニメの後に色が変わることを確認して下さい。

$("div").click(function(eo){
	var myObj = $(this);
	myObj.css("-webkit-transform","translate(500px,0px)");
	setTimeout(function(){
		myObj.css("background-color","#F9F");
	},500);
});

setTimeoutを利用する場合のデメリットとして、アニメの長さを変更する度にsetTimeoutの時間指定も変更しなければいけない点です。なので、最後にアニメの時間もcssで設定するのではなく、javaScriptで設定するようにします。

サンプル(cssAnime3/07.html)を開いてcssでアニメの長さ(transition-duration)が設定されていないことを確認して下さい。

div{
	position:relative;
	width:50px;
	height:50px;
	margin:10px;
	background-color:#6CF;
	-webkit-transition-property:-webkit-transform;
}

続いてjQueryを確認します。まず1行目でアニメの長さ(ミリ秒)を変数speedに代入します。そして2行目でcssメソッドの値として変数speedを利用してアニメの長さを設定します。最後にsetTimeoutの時間指定の部分に利用します(8行目)。

var speed = 1000;
$("div").css("-webkit-transition","-webkit-transform "+speed+"ms");
$("div").click(function(eo){
	var myObj = $(this);
	myObj.css("-webkit-transform","translate(500px,0px)");
	setTimeout(function(){
		myObj.css("background-color","#F9F");
	},speed);
});

こうすることでアニメの長さを変更する際にcssアニメとsetTimeoutの値が同時に変わるため修正のミスが少なくなります。これでcssアニメの長さとjQueryの連携の説明を終わります。cssアニメは利用できるブラウザが限られているため、現段階ではスマートフォンアプリなどでしか活用する機会は無いと思います。しかし時代が進めばcssアニメも一般的になり、活用する機会も増えるのではないでしょうか?

メモ

dataを利用した要素管理

複数のdiv要素のサンプルで、setTimeoutだけでなく座標の管理も行うようなサンプルを作成しました。少々複雑になったのでメモとして記載します。興味のある方はソースを研究してみて下さい。
→サンプル(cssAnime3/test01.html

このサンプルではクリーム色の部分(id属性cardArea)をクリックするとカード(class属性card)をクリックした位置に生成します。そしてカードをクリックすると原点(0,0)にcssアニメで移動します。

ポイントは、div要素を生成する際に座標をdataメソッドを利用して記憶し、移動する際にdataに保存した座標を利用して移動量を計算する点です。生成したdiv要素自身に座標位置を記憶させることで管理を簡単にしているのです。