ホーム画面の壁紙を切り替える(スライド編)

前回は「フェード」という切り替え方法による壁紙チェンジャーを紹介しました。
opacityという透過率を変更できるプロパティを用いて切り替えたのですが、今回はleftの値を変化させることで「スライド」という切り替えによる壁紙チェンジャーを紹介します。

基本ファイルとその確認

サンプルファイル構成

今回作るサンプルウィジェットを含むテーマファイルの基本構成は右図の通りですが、前回のフェード編に手を加えただけのものです。
ファイルのダウンロードはこちらーー>ダウンロード

<注意>
各ウィジェットの最初のサンプルには画像を入れてありますが、その後のサンプルには画像を用意しておりませんので、最初のサンプルにある画像をコピーしたり、各自で用意するなどして下さい。

では、ファイルを1つ1つ見ていきましょう。

サンプルHTML

HTMLから見ていきましょう。
以下に示すのが、Wallpaper.htmlの中身(ソース)です。

<?xml version="1.0" encoding="utf-8"?>
<html>
 <head>
  <meta name="viewport" content="width=device-width,minimum-scale=0.5,initial-scale=0.5"/>
  <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 interval;
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 interval;
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 prev;
var next;

この新たな変数を宣言しておきます。

大きく変更がある箇所は、slide関数内部です。

prev=(current + imgCount-1) % imgCount;

前画像は現画像の1つ前なので、current-1で表すことができますが、currentが0だった場合のことを考慮して、画像総数を足すことで実現しています。

next=(current + 1) % imgCount;

次画像はマイナスになることはないので、1を足した数字を画像総数でわり、その余りを求めます。これはフェード編でも説明済みです。

imageArray[prev].className = 'stay';

前画像を表すprevを利用し、画像を右の画面外のポジション(stay)を指定します。

current = next ;

一通りポジション(クラス)指定したところで、画像を表す変数の値を次の画像のそれにしておきます。
前画像(prev)も、次画像(next)も、currentから算出されるので、currentだけをずらしておきます。

ページトップへ

スタイルシートの変更
img.stay {
  left: 640px;
}

サンプルスタイルシート2

さきほどのjavascriptと同様に、待機ポジション(stay)の指定を追加します。

サンプル表示

やはり実機で確認していただきたいのですが、新たに表示される画像が右から移動してきます。
ここでまた仕様を変更しなければならないことがあります。
それは、画像同士における奥行き方向の位置関係です。

本来なら、画面に入ってくる画像と出て行く画像は手前に表示されていなければなりません。
しかし、HTMLで記述した画像順に、奥から手前へと並んだまま、その順番は変わらないので、
このサンプルにおける画像(apple2.tiff)がどの状態でも手前に表示されたままなのです。
ここでも課題が残りましたので、次のサンプルで対応しましょう。

今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード

ページトップへ

バグを取る2

では前回の課題である「奥行き方向の位置指定」をしましょう。
これは、「動き」ではなく「見た目」の問題なので、スタイルシートの変更だけです。

スタイルシートの変更
img {
  position: absolute;
  top: 0px;
  left: 640px;
  width: 640px;
  height: 960px;
  -webkit-transition-property: left;
  -webkit-transition-duration: 3s;
  z-index: 0;
}
img.off {
  left: -640px;
  z-index: 1;
}
img.on {
  left: 0px;
  z-index: 2;
}
img.on {
  left: 640px;
  z-index: 0;
}

サンプルスタイルシート3

変更(追加)部分を赤くしています。

奥行き方向の位置指定は、z-indexを使います。
3次元軸におけるZ方向のイメージです。
数字が大きくなる程、手前に表示されます。
初期状態と待機状態のときを0にすることで、見えなくても良い時の画像を奥へ配置し、見えるべき画像を1と2にし、手前に表示する訳です。

サンプル表示

実機で確認してください。
切り替え時の画像は常に手前に表示されるようになりました。


今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード

ページトップへ

アニメーション終了検出を導入する

ここで、前回の課題を片付けましょう。
アニメーションにかかる時間を、javascriptでもスタイルシートでも指定していました。
しかし、アニメーションは「見た目の部分」なので、指定はスタイルシートだけにします。
では、javascriptの方の時間稼ぎはどのようにすれば良いのでしょうか?

ここで、考え方を変えてみます。
「時間を稼いでタイミングを合わせる」ではなく、「アニメーションが終わったら次の動作に入る」という形にします。
ここで登場するのが、「イベントリスナー」です。
ある動きを検出してくれるもので、他のスクリプト言語でもよく使われています。
このイベントリスナーを使って、アニメーションの終了を検出しましょう。

javascriptの変更

変更部分を赤くしています。

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 ;

 imageArray[current].addEventListener(
   "webkitTransitionEnd",
   function() {
     current = next ;
     wait();
   },
   false
 );
}

サンプルJavaScript4

setTimeoutだった部分を丸々置き換えた形になりましたね。

imageArray[current].addEventListener(

特定の画像にイベントリスナーを登録しています。こんな風に記述するんですね。

この行は、"("で終わっているので、これ以降は「引数」です。

"webkitTransitionEnd",

この行は、1つ目の引数を示しています。
何を検出するかを記述するのですが、今回はアニメーションが終わったかどうかを検出します。

function() {
 current = next ;
 wait();
},

実は、ここで1日悩みました。
この部分はfunctionではなく、waitだけでやっていたのですが、アニメーション終了検出の前にcurrentを書き換えているので、これまた求める動きにならなかったんですね。
currentの書き換えもしたいし、waitには飛ばなければならないし、スマートではないかもしれませんが、この部分にfunction関数にて、上記の2つを盛り込みました。

そして、最後の"false"ですが、これは気にしないでください。
私の中でも、ここは「気にしなくても良い」という判断で作成したので、説明は省略します。

サンプル表示

実機で確認してください。
動きは変わりませんが、javascript内のsetTimeoutが、intervalだけになりました。



今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード

ページトップへ

アニメーション終了検出を解除する

課題の対応で、アニメーション終了検出を導入したのですが、実機で確認して、そのまま放っておいたら、止まっていたのです。
リブートして、ホーム画面にて再確認するのですが、SBSettingsでメモリも確認しながら見ていると、動きがだんだんぎこちなくなり、止まってしまいました。
メモリ消費量も増えはしたものの、それが原因とは思えないので、本家OSXのSafariで動かしてみました。
Webインスペクタで各変数をウォッチして、アクティビティモニタでもメモリ消費を確認しながら眺めていると、やはり最後には止まってしまいました。
変数は上手く切り替わっているので、単純に「フリーズ」という意識で、アクティビティモニタのメモリ消費量からCPU占有率へと目線を移動すると、なんと98%!!
こんなに負担がかかっていては動くものも動かないので、アニメーション終了検出で登録しっ放しのイベントリスナーを解除することにしました。

javascriptの変更

変更部分を赤くしています。

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 ;

 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は新規の関数なのですが、アニメーション終了時の処理をこの中に記述します。イベントリスナーの解放も、ここに記述します。

imageArray[current].removeEventListener("webkitTransitionEnd",

イベントリスナーの解除は、removeEventListenerと記述します。
このremoveEventListenerも引数が必要なのですが、イベントリスナー登録時と同じ引数にします。addEventListenerとremoveEventListenerの引数を見比べてみてください。
コピペなので、同じ引数のはずです。

サンプル表示

実機で確認してください。
このページを作成中、ずっと動作させていたのですが、とまることはありませんでした。
OSXでも確認しましたが、CPU占有率も40%前後と70%前後の数字が交互にでるので、動きに応じて負担が変わっていることを確認しました。
繰り返しながらも占有率が増えることはなかったので、これで解決とします。


今回のサンプルファイルのダウンロードは、こちらからどうぞーー>ダウンロード

ページトップへ

残った課題

今回のウィジェットは、結果的にスライド表示することができたと思いますが、やはり、スタート時とスライド中のもたつきが気になります。
HTMLの段階で、最初から画像をすべて表示しているので仕方ないのかな・・・

2011/10/03