2014年5月31日土曜日

Javascriptで動くページを作ろう第三回:transitionとJavascriptの組み合わせ

どうも、JP3BGYです。
今回は前回の続きとしてtransitionとJavascriptを組み合わせてみたいと思いますが・・・。
その前に少しばかりJavascriptの説明を。

まず、Javascriptの実行の順番です。
アニメーションをすることにおいて一番重要な部分なのでまあ知っている方も多いでしょうが一応目を通しておいてください。
えー、それ以前にJavascriptがわかんない方は別のページで調べてください。
あんまり詳しく書くと長くなってしまいますから。
まずJavascriptには関数内の命令と関数外の命令があります。
下のような感じです。

var a;
//関数外の命令群
function b(){
 //関数内の命令群
}
//関数外の命令
b();

HTMLが上のタグから順に読み込まれていってJavascriptが読み込まれた瞬間にまず関数外の命令群が動きます。もちろん上から順に実行されます。
上の例では変数aと関数bが宣言されて関数bが一度だけ実行されるようになっています。
また,JavascriptにはHTMLにイベントを組み込むことによってそのイベント発生時に決まった関数を処理できるようになってます。

<html>
<head>
<script>
 var a;
 //関数外の命令群
 function b(){
  //関数内の命令群
 }
</script>
</head>
<body onload="b()">
</body>
</html>

この例ではbodyが読み込まれる前に関数外の命令群が動いて読み込まれたときに関数bが呼び出されていますね。

次にDOMについてです。
DOMとは簡単に言いますとHTMLやXMLの内容をJavascrript等外部のプログラムで使うためのものです。
CSSの値を変更する場合は以下のような感じです。

var sampleelement=document.GetElementById("sample1");
sampleelement.style.width="60px";

上の例ではID名がsample1であるタグのwidthプロパティを60pxに変更しました。
何らかの方法でタグのElementを手に入れてそこに
Elementを代入した変数名.style.変えたいCSSプロパティ="変更後の値を指定";
とまあこんな感じでかえます。
これはCSSプロパティなら何でも使うことができます。

さてここまではほとんど常識の範囲ですが、ここからアニメーションの話にしていきましょう。
前回使ったtransitionは指定されたCSSプロパティが変更されたときにアニメーション化するものなので、Javascriptで変更された場合でも適用されます。
なのでさっそく下にJavascriptで作る簡単なアニメーションの例を書いてみましょう。

<html>
<head>
<style>
#anime{
color:white;
 background-color:blue;
 transition: width 1s ease  , height 1s ease ;
 -moz-transition: width 1s ease  , height 1s ease ;
-webkit-transition: width 1s ease  , height 1s ease ;
-o-transition:  width 1s ease  , height 1s ease ;
-ms-transition: width 1s ease  , height 1s ease ;
}
</style>
</head>
<body>
<div id="anime" onclick="b();">アニメーションですよ</div>
<script>
 var a=document.getElementById("anime");
 var flag=0;
 //関数外の命令群
 function b(){
  switch(flag){
   case 1:
    a.style.width="256px";
    a.style.height="256px";
    break;
   default :
    a.style.width="128px";
    a.style.height="128px";
    break;
  }
  flag=flag^1;
 }
 b();
</script>
</body>
</html>

とまあこんな感じでクリックすると変わるものが出来上がった。

ところで、上の例を見て少し変だと思ったところはありますか?
そう、埋め込みscriptがbody、しかもdivの後ろにあります。
実はdivよりも下にないとうまく実行できません。
なぜでしょう?
これはHTMLの読み込みの順番が関係しています。
HTMLはうえの行の左から順に読み込みそしてすぐに実行します。
難しい言葉で言うとインタプリタ方式というやつです。
で、試しにブラウザの真似をして上から順に見ていくとですね、もしjavascriptがheadの中にあったとするとjavascripが実行されている時点では以下のようになっているのです。

<html>
<head>
<style>
#anime{
color:white;
 background-color:blue;
 transition: width 1s ease  , height 1s ease ;
 -moz-transition: width 1s ease  , height 1s ease ;
-webkit-transition: width 1s ease  , height 1s ease ;
-o-transition:  width 1s ease  , height 1s ease ;
-ms-transition: width 1s ease  , height 1s ease ;
}
</style>
<script>
 var a=document.getElementById("anime");
 var flag=0;
 //関数外の命令群
 function b(){
  switch(flag){
   case 1:
    a.style.width="256px";
    a.style.height="256px";
    break;
   default :
    a.style.width="128px";
    a.style.height="128px";
    break;
  }
  flag=flag^1;
 }
 b();
</script>

まあ見てくれればわかるけど最初に悩むところとして
var a=document.getElementById("anime");
のIDがanimeっていうタグがないけどこれ何、というところです。
実際にブラウザで起動してF12ボタンでデバッグしてみるとそのようなエラーが書いてあるかと思われます。
まあつまり、Element取得においてなのですがこれはjavascriptより前にHTMLで書かれたもののみを見るのでこれを適応したいすべてのHTMLタグはすべてjavascriptより前に置かなければなりません。
そうすると今回は必然的にIDがanimeであるdivを前に置く必要があったためにこうなってしまったわけです。

そうするともう一つ疑問が出る人もいるかもしれません。
styleはだいじょうぶなのか?というものですね。
style、これはいわば設定項目、簡単に言えばこのHTML文章ではすべてこのようなstyleで設定されてます、という宣言のようなものです。
javascriptはその場その場で起動するのに対してCSSは設定なのでこの文章全体に反映されます。
なのでstyleはほとんどの人はhead部分に書くわけです。
それとstyleもjavascriptも<html>タグの中ならどこに書いてもいくつ書いても大丈夫だったはずです。

上のことを頭の隅において色々アレンジして面白いものにチャレンジしてみてください。

ただし少しばかり注意が必要なものがあります。
それはsetTimeout関数です。
この関数は指定時間後に指定された関数を実行させるという関数なのですが、これ人によって解釈がずれたりして大変なのです。
例えば次のような例を見てみましょう。
setTimeout('window.alert("7000")',7000);
setTimeout('window.alert("5000")',5000);
setTimeout('window.alert("3000")',3000);
上の例は一見7秒後に一回、その後5秒後に一回、そしてそのあと3秒後に一回ずつalert表示されるように見えます。
が、しかし実際は起動してから3・5・7秒後に一回ずつ表示があるのです。
どうしてこうなるのでしょうか。

これはsetTimeout関数の罠なのですが、この関数は指定された時間の後に指定された関数を起動させる時限爆弾を設置するだけの関数なのです。
時限爆弾を設置するのは一瞬なのですぐに次の命令に行きます。
そうすると上の例の3つはほぼ同時に時限爆弾がセットされます。
よって待ち時間の短いものから順に起動されていくのです。
ではどうすれば時間差で起動させることが可能でしょうか。
下の例を見てください

var bflag=0;flag=0;
//インターバルの開始
var timerID = setInterval(
function(){
 //もし爆発していない時限爆弾がなければ
 if(bflag!=1){
 bflag=1;
//設置する時限爆弾を条件分岐で選択
 switch(flag){
  case  0:setTimeout("window.alert('7000',7000);bflag=0;flag++;",7000);break;
  case  1:setTimeout("window.alert('5000',5000);bflag=0;flag++;",5000);break;
  default:break;
  }
 }
//もし2つの関数が終われば
 if(flag == 2){
  setTimeout("window.alert('3000',3000);bflag=0;flag++;",3000);
  clearInterval(timerID);//Interval解除!!
  timerID = null;
 }
},200);

まあ人のコードなんてぱっと見てわかるものではないので説明します。
まずsetInterval関数ですが、これは一定時間ごとに指定された関数を実行させる関数です。
今回は200msごとに繰り返していますがもし重いと思ったら1000ぐらいまで上げてもいいと思います。
またこの関数は見ての通り
clearInterval(返り値を代入した変数の名前);
で解除できます。

その中身ですが、まずbflagですがこれが1の時setTimeoutによって時限爆弾が設置されないようにしてます。つまり"Busy"flag、今は別のsetTimeoutが動いていますよってことです。
次にflag、これはsetTimeoutが何番目まで中身の関数の実行を終わらせたかということです。
要は順番にsetTimeoutを呼び出すためのものです。
そして実行の流れとしては

200msごとにbflagをチェックしてbflagが0の時bflagを1にして次のsetTImeoutを実行、それが終わればbflagを0にし、flagに1を足しててまた次の関数を実行する。
flagが2、つまり2つ目までの処理が終わったら、3つ目の時限爆弾を設置してそのあとbflagをチェックしているsetIntervalを解除する。
その後最後の関数が実行されて終了。

手順はそこまで難しくないとおもいます。
またこの方法以外にも色々やり方はあるので考えてみてください。

今回はここまで、次回はCSSを使用しないアニメーションを作ってみましょう。
ほな、さいなら。