ホーム画面の壁紙を切り替える2(スライド編)
前回は「フェード」という切り替え方法による壁紙チェンジャーを紹介しました。
opacityという透過率を変更できるプロパティを用いて切り替えたのですが、今回は画像の横位置の値を変化させることで「スライド」という切り替えによる壁紙チェンジャーを紹介します。
基本ファイルとその確認
サンプルファイル構成
今回作るサンプルウィジェットを含むテーマファイルの基本構成は右図の通りですが、前回のフェード編に手を加えただけのものです。
といっても、フェード編の完成形からの変更ではなく、作り直す前(fade3)のファイルと比較して下さい。
ファイルのダウンロードはこちらーー>ダウンロード
では、ファイルを1つ1つ見ていきましょう。
サンプルHTML
HTMLから見ていきましょう。
以下に示すのが、Wallpaper.htmlの中身(ソース)です。
<html>
<head>
<meta name="viewport" content="width=device-width,minimum-scale=0.5"/>
<meta charset="UTF-8">
<meta name="format-detection" content="telephone=no">
<script src="./script.js" type="text/javascript"></script>
<link href="./style.css" rel="stylesheet" type="text/css" />
</head>
<body onload="init()">
<img src="images/img_01.jpg"/>
<img src="images/img_02.jpg"/>
<img src="images/img03.png"/>
<img src="images/img04.png"/>
<img src="images/apple1.gif"/>
<img src="images/ume.gif"/>
<img src="images/candybars.tiff"/>
<img src="images/apple2.tiff"/>
</body>
</html>
サンプルHTMLソース1
フェード編と何も変わりません。
サンプルJavaScript
では、JSファイルを見ていきましょう。
以下に示すのが、script.jsの中身(ソース)です。
フェード編と違う部分を赤くしています。
var delay;
var imageArray;
var imageCount ;
var current ;
function init() {
interval = 5;
delay = 3;
imageArray = document.getElementsByTagName("img");
imgCount = imageArray.length;
current = 0;
wait();
}
function wait() {
setTimeout(slide, interval * 1000);
}
function slide() {
imageArray[current].className = 'out';
current = (current+1) % imgCount;
imageArray[current].className = 'in';
setTimeout(wait, delay * 1000);
}
サンプルJavaScript1
基本的にはフェード編と何も変わりません。
スライド編へ移行するために、"fade"を"slide"にし、"on"と"off"をそれぞれ"in"と"out"にしました。
サンプルスタイルシート
margin: 0px;
padding: 0px;
border: 0px;
}
body {
width: 640px;
height: 960px;
}
img {
position: absolute;
top: 0px;
left: 640px;
width: 640px;
height: 960px;
-webkit-transition-property: left;
-webkit-transition-duration: 3s;
}
img.out {
left: -640px;
}
img.in {
left: 0;
}
サンプルスタイルシート1
次に、スタイルシート(style.css)を見ていきましょう。
こちらもjavascriptと同じで、フェード編からスライド編への移行作業のみです。
アニメーションさせるプロパティですが、opacityをleftに変更しました。
今回のアニメーションは右から左へとスライドすることで切り替えるので、最初の位置を640pxとすることで、画面外右側に配置します。
次に、スライドして画面内に入ってくる画像のクラスを"in"とし、その位置を0pxとします。
そして、画面外へ出て行く画像のクラスを"off"とし、その位置を-640pxとします。
これでスライドによるアニメーションが実現できます。
サンプル表示
基本サンプルファイルは、個々でダウンロード&実行して確認していただきたいのですが、スクリーンショットを4枚だけ紹介します。
右から変化中
右から変化中
左から変化中
左から変化中
とりあえず4枚紹介しましたが、こちらはなおのこと個々の実機で確認していただくのが一番かと思います。
ん?と思っていただけることでしょう。
今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード
バグを取る
実機で確認された方はお分かりかと思いますが、求める動きではありません。
これを図によって解説したいところですが、良い感じの図が用意できなかったので、このまま文字で説明を続けます。
用意したクラスは2つあり、これらはそれぞれ画面に入り切った位置と、画面から左へ出切った位置を指定しています。
スタートが右の画面外なので、用意された画像が一通り移動するまでは、右から左へと移動していきます。
ですが、2週目に入ると、すべての画像が左の画面外からのスタートとなるので、それ以降はずっと左からの「出たり入ったり」を繰り返します。
これを回避するために、これまで2つだったポジションを3つにします。
左へ出て行った時のポジション(out)と、右から入ってきた時のメイン表示用のポジション(in)と、右の画面外で待機するポジション(stay)の3つにします。
この3つのポジション(クラス)を意識してソースを書き換えていきましょう。
javascriptの変更
変更部分を赤くしています。
var delay;
var imageArray;
var imageCount;
var current;
var prev;
var next;
function init() {
interval = 5;
delay = 3;
imageArray = document.getElementsByTagName("img");
imgCount = imageArray.length;
current = 0;
wait();
}
function wait() {
setTimeout(slide, interval * 1000);
}
function slide() {
prev=(current + imgCount-1) % imgCount;
next=(current + 1) % imgCount;
imageArray[prev].className = 'stay';
imageArray[current].className = 'out';
imageArray[next].className = 'in';
current = next ;
setTimeout(wait, delay * 1000);
}
サンプルJavaScript2
現在表示されている画像を表す値を格納する変数は、そのままcurrentのまま使用します。
このcurrentは、今回のスライドでいうところの、それまで表示されており左へと出て行く画像(現画像)を指します。
そして、右の画面外で待機する画像(前画像)をprevで表し、右から入ってくる画像(次画像)をnextで表すことにします。
var next;
この新たな変数を宣言しておきます。
大きく変更がある箇所は、slide関数内部です。
前画像は現画像の1つ前なので、current-1で表すことができますが、currentが0だった場合のことを考慮して、画像総数を足すことで実現しています。
次画像はマイナスになることはないので、1を足した数字を画像総数でわり、その余りを求めます。これはフェード編でも説明済みです。
前画像を表すprevを利用し、画像を右の画面外のポジション(stay)を指定します。
一通りポジション(クラス)指定したところで、画像を表す変数の値を次の画像のそれにしておきます。
前画像(prev)も、次画像(next)も、currentから算出されるので、currentだけをずらしておきます。
スタイルシートの変更
さきほどのjavascriptと同様に、待機ポジション(stay)の指定を追加します。
サンプル表示
やはり実機で確認していただきたいのですが、新たに表示される画像が右から移動してきます。
ここでまた仕様を変更しなければならないことがあります。
それは、画像同士における奥行き方向の位置関係です。
本来なら、画面に入ってくる画像と出て行く画像は手前に表示されていなければなりません。
しかし、HTMLで記述した画像順に、奥から手前へと並んだまま、その順番は変わらないので、
このサンプルにおける画像(apple2.tiff)がどの状態でも手前に表示されたままなのです。
ここでも課題が残りましたので、次のサンプルで対応しましょう。
今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード
バグを取る2
では前回の課題である「奥行き方向の位置指定」をしましょう。
これは、「動き」ではなく「見た目」の問題なので、スタイルシートの変更だけです。
スタイルシートの変更
position: absolute;
top: 0px;
left: 640px;
width: 640px;
height: 960px;
-webkit-transition-property: left;
-webkit-transition-duration: 3s;
z-index: 0;
}
img.out {
left: -640px;
z-index: 1;
}
img.in {
left: 0px;
z-index: 2;
}
img.stay {
left: 640px;
z-index: 0;
}
サンプルスタイルシート3
変更(追加)部分を赤くしています。
奥行き方向の位置指定は、z-indexを使います。
3次元軸におけるZ方向のイメージです。
数字が大きくなる程、手前に表示されます。
初期状態と待機状態のときを0にすることで、見えなくても良い時の画像を奥へ配置し、見えるべき画像を1と2にし、手前に表示する訳です。
サンプル表示
実機で確認してください。
切り替え時の画像は常に手前に表示されるようになりました。
今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード
アニメーション終了検出を導入する
ここで、前回の課題を片付けましょう。
アニメーションにかかる時間を、javascriptでもスタイルシートでも指定していました。
しかし、アニメーションは「見た目の部分」なので、指定はスタイルシートだけにします。
では、javascriptの方の時間稼ぎはどのようにすれば良いのでしょうか?
ここで、考え方を変えてみます。
「時間を稼いでタイミングを合わせる」ではなく、「アニメーションが終わったら次の動作に入る」という形にします。
ここで登場するのが、「イベントリスナー」です。
ある動きを検出してくれるもので、他のスクリプト言語でもよく使われています。
このイベントリスナーを使って、アニメーションの終了を検出しましょう。
javascriptの変更
変更部分を赤くしています。
prev=(current + imgCount-1) % imgCount;
next=(current + 1) % imgCount;
imageArray[prev].className = 'stay';
imageArray[current].className = 'out';
imageArray[next].className = 'in';
// current = next ;
imageArray[current].addEventListener(
"webkitTransitionEnd",
function() {
current = next ;
wait();
},
false
);
}
サンプルJavaScript4
setTimeoutだった部分を丸々置き換えた形になりましたね。
特定の画像にイベントリスナーを登録しています。こんな風に記述するんですね。
この行は、"("で終わっているので、これ以降は「引数」です。
この行は、1つ目の引数を示しています。
何を検出するかを記述するのですが、今回はアニメーションが終わったかどうかを検出します。
current = next ;
wait();
},
実は、ここで1日悩みました。
この部分はfunctionではなく、waitだけでやっていたのですが、アニメーション終了検出の前にcurrentを書き換えているので、これまた求める動きにならなかったんですね。
currentの書き換えもしたいし、waitには飛ばなければならないし、スマートではないかもしれませんが、この部分にfunction関数にて、上記の2つを盛り込みました。
そして、最後の"false"ですが、これは気にしないでください。
私の中でも、ここは「気にしなくても良い」という判断で作成したので、説明は省略します。
サンプル表示
実機で確認してください。
動きは変わりませんが、javascript内のsetTimeoutが、intervalだけになりました。
説明は省きましたが、delayをコメントアウトしていますので、ソースで確認して下さい。
今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード
アニメーション終了検出を解除する
課題の対応で、アニメーション終了検出を導入したのですが、実機で確認して、そのまま放っておいたら、止まっていたのです。
リブートして、ホーム画面にて再確認するのですが、SBSettingsでメモリも確認しながら見ていると、動きがだんだんぎこちなくなり、止まってしまいました。
メモリ消費量も増えはしたものの、それが原因とは思えないので、本家OSXのSafariで動かしてみました。
Webインスペクタで各変数をウォッチして、アクティビティモニタでもメモリ消費を確認しながら眺めていると、やはり最後には止まってしまいました。
変数は上手く切り替わっているので、単純に「フリーズ」という意識で、アクティビティモニタのメモリ消費量からCPU占有率へと目線を移動すると、なんと98%!!
こんなに負担がかかっていては動くものも動かないので、アニメーション終了検出で登録しっ放しのイベントリスナーを解除することにしました。
javascriptの変更
変更部分を赤くしています。
prev=(current + imgCount-1) % imgCount;
next=(current + 1) % imgCount;
imageArray[prev].className = 'stay';
imageArray[current].className = 'out';
imageArray[next].className = 'in';
// current = next ;
imageArray[current].addEventListener(
"webkitTransitionEnd",
slide_end,
false
);
}
function slide_end() {
imageArray[current].removeEventListener("webkitTransitionEnd",
slide_end,
false);
current = next ;
wait();
}
サンプルJavaScript5
イベントリスナー登録部分は、slide_endという関数を呼び出すように変更しました。
この関数slide_endは新規の関数なのですが、アニメーション終了時の処理をこの中に記述します。イベントリスナーの解放も、ここに記述します。
イベントリスナーの解除は、removeEventListenerと記述します。
このremoveEventListenerも引数が必要なのですが、イベントリスナー登録時と同じ引数にします。addEventListenerとremoveEventListenerの引数を見比べてみてください。
コピペなので、同じ引数のはずです。
サンプル表示
実機で確認してください。
このページを作成中、ずっと動作させていたのですが、とまることはありませんでした。
OSXでも確認しましたが、CPU占有率も5%前後と15%前後の数字が交互にでるので、動きに応じて負担が変わっていることを確認しました。
繰り返しながらも占有率が増えることはなかったので、これで解決とします。
今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード
作り直す
スライド指定方法を変更する
今回のスライドも、前回のフェードと同様に、作り方が固まったソースを利用して作り直しましょう。
サンプルHTML
HTMLから見ていきましょう。
以下に示すのが、Wallpaper.htmlの中身(ソース)です。
<html>
<head>
<meta name="viewport" content="width=device-width,minimum-scale=0.5"/>
<meta charset="UTF-8">
<meta name="format-detection" content="telephone=no">
<script src="./script.js" type="text/javascript"></script>
<link href="./style.css" rel="stylesheet" type="text/css" />
</head>
<body onload="init()">
<img id="slide0"/>
<img id="slide1"/>
</body>
</html>
サンプルHTMLソース6
<body>内にすべての画像を並べる方法を廃止し、切り替えに必要な画像2つだけにします。
この2つの画像で切り替えを演出するのですが、画像自体も入れ替えるため、この段階で画像は特定しません。
その切り替えの仕組みは、JavaScriptで記述しますので、何枚も切り替えることが可能になります。
サンプルjavascript
変更部分を赤くしています。
var imageCount ;
var current ;
var next;
var elm0;
var elm1;
var img_name = ["img_01.jpg",
"img_02.jpg",
"img03.png",
"img04.png",
"apple1.gif",
"ume.gif",
"candybars.tiff",
"apple2.tiff"];
function init() {
interval = 2;
elm0 = document.getElementById("slide0");
elm1 = document.getElementById("slide1");
imgCount = img_name.length;
current = 0;
next = 1;
wait();
}
function wait() {
elm0.src = "./images/" + img_name[current];
elm0.className = "out";
elm1.src = "./images/" + img_name[next];
elm1.className = "in";
setTimeout(slide, interval * 1000);
}
function slide() {
elm0.className = "slide_out";
elm1.className = "slide_in";
elm1.addEventListener('webkitTransitionEnd',slide_end,false);
}
function slide_end() {
elm1.removeEventListener('webkitTransitionEnd',slide_end,false);
current = next;
next=(current + 1) % imgCount;
wait();
}
サンプルJavaScript6
フェードと同様に、HTMLの要素を画像要素2つにしたことで、この2つへの処理に集中できます。
画像ソースやクラスの設定はもちろんですが、イベントリスナーの追加が大きな変更かと思います。
サンプルスタイルシート
margin: 0;
padding: 0;
border: 0;
}
body {
width: 640px;
height: 960px;
}
img {
position: absolute;
top: 0px;
width: 640px;
height: 960px;
-webkit-transition-property: -webkit-transform;
-webkit-transition-duration: 1s;
}
img.out {
left: 0px;
-webkit-transition-property: none;
}
img.slide_out {
-webkit-transform: translateX(-640px);
}
img.in {
left: 640px;
-webkit-transition-property: none;
}
img.slide_in {
left: 640px;
-webkit-transform: translateX(-640px);
}
サンプルスタイルシート6
今回の作り直しは、JavaScriptがメインですので、スタイルシートの変更はほとんどありません。
処理の都合で変更になった部分と、スタイルの小分けによる追加があるくらいです。
サンプル表示
こちらも、中身を作り直しただけなので、見た目は何も変わりません。
今回の作り直しでは、移動方法を変更したので、もたつき感がなくなったと思います。
横移動なので、横位置指定の"Left"を使っていたのですが、今回のように"translateX"を使った方が良いことが分かりました。
やり方次第で、ものすごく滑らかに移動するものなのですね。
今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード
スタイルシートを見直す
JavaScriptを作り直すことで、スタイルシートにも変更が生じたのですが、そのスタイルシート自体に重複箇所があるので、今回はスタイルシートを簡素化しましょう。
サンプルHTML
HTMLに変更はありません。
サンプルスタイルシート
margin: 0;
padding: 0;
border: 0;
}
body {
width: 640px;
height: 960px;
}
img {
position: absolute;
top: 0px;
width: 640px;
height: 960px;
-webkit-transition-property: -webkit-transform;
-webkit-transition-duration: 1s;
}
.out {
left: 0px;
}
.in {
left: 640px;
}
.stay {
-webkit-transition-property: none;
}
.slide {
-webkit-transform: translateX(-640px);
}
サンプルスタイルシート7
今回の見直しは、スタイルシートがメインですので、スタイルシートから紹介します。
簡素化することで、重複箇所がなくなったことを確認して下さい。
サンプルjavascript
変更部分を赤くしています。
var imageCount ;
var current ;
var next;
var elm0;
var elm1;
var img_name = ["img_01.jpg",
"img_02.jpg",
"img03.png",
"img04.png",
"apple1.gif",
"ume.gif",
"candybars.tiff",
"apple2.tiff"];
function init() {
interval = 2;
elm0 = document.getElementById("slide0");
elm1 = document.getElementById("slide1");
imgCount = img_name.length;
current = 0;
next = 1;
wait();
}
function wait() {
elm0.src = "./images/" + img_name[current];
elm0.className = "out stay";
elm1.src = "./images/" + img_name[next];
elm1.className = "in stay";
setTimeout(slide, interval * 1000);
}
function slide() {
elm0.className = "slide out";
elm1.className = "slide in";
elm1.addEventListener('webkitTransitionEnd',slide_end,false);
}
function slide_end() {
elm1.removeEventListener('webkitTransitionEnd',slide_end,false);
current = next;
next=(current + 1) % imgCount;
wait();
}
サンプルJavaScript7
スタイルシートを小分けしたので、クラスを複数指定することで、制御を実現しています。
スペースで区切ってクラスを並べることで、複数指定できるサンプルとなっております。
サンプル表示
こちらも、中身を作り直しただけなので、見た目は何も変わりません。
今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード