ホーム画面の壁紙を切り替える(フェード編)

「壁紙を切り替える」と言うよりも、「壁紙チェンジャー」と言う方が、聞き覚えがあるかと思います。
壁紙を一定時間おきに切り替えるウィジェットですが、ウィジェットの王道の1つです。
iPhoneは、HTML5 & CSS3に対応しているので、これまでJavaScriptで作っていた「動き」が、スタイルシートでやれるようになりました。
おかげで、JavaScriptなどの記述量が激減し、紹介しやすくなったので、ここで紹介します。

基本ファイルとその確認

サンプルファイル構成

今回作るサンプルウィジェットを含むテーマファイルの基本構成は右図の通りです。
ファイルのダウンロードはこちらーー>ダウンロード
Wallpaper.htmlは、ホーム画面で実行されるウィジェットです。
今回は、「壁紙チェンジャー」ということで、大きさやファイル形式の違う画像を imagesフォルダーに何枚か用意しました。
style.cssは見た目の設定をし、script.jsで壁紙を切り替えます。

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

では、ファイルを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

このサンプルHTMLの特徴は、画像を表示するimgタグがズラ〜っと並んでいることと、bodyタグにonloadあるという2点です。
imgタグは、表示したい画像の数だけ並べる必要があります。もちろん、imgタグの画像ファイル名はそれぞれの画像ファイル名にする必要もあります。
HTMLで、画像をすべて表示してしまう訳です。

すべての画像が揃った段階で処理を始めるので、bodyタグにonloadがある訳です。
そして、その処理がscript.js内のinitに記述されています。

ページトップへ

サンプルJavaScript

では、JSファイルを見ていきましょう。
以下に示すのが、script.jsの中身(ソース)です。
変数宣言と関数2つが記述されています。例のinitもここにあります。

var interval;
var imageArray;
var imageCount ;
var current ;

function init() {
 interval = 5;
 imageArray = document.getElementsByTagName("img");
 imgCount = imageArray.length;
 current = 0;
 for(var i = 0; i < imgCount; i++){
  imageArray[i].className = 'off';
 }
 fade();
}

function fade() {
 imageArray[current].className = 'off';
 current++;
 if(current == imgCount){
  current = 0;
 }
 imageArray[current].className = 'on';

 setTimeout(fade, interval * 1000);
}

サンプルJavaScript1

始めの変数宣言は、このスクリプトで使う変数で、4つあります。
intervalは、壁紙を入れ替える間隔で、秒単位にしました。
imageArrayは、画像情報を格納しておくための配列変数です。
imgCountは、画像の枚数です。
currentは、表示している画像を表す番号に使います。

では、ウィジェットのHTMLが読み込まれてから呼び出される関数initを説明します。
「init」でピンとくる方もいらっしゃるかと思いますが、スクリプトとしての初期設定をここで行ないます。

interval = 5;

壁紙の切り替え時間を5秒にしますが、お好みで構いません。

imageArray = document.getElementsByTagName("img");

今回のスクリプトのポイントになる部分です。
HTMLで、表示する画像のimgタグをズラ〜っと並べましたが、そのimgタグのすべてを見つけ、宣言した配列変数imageArrayに格納するという作業を、この1行でやってくれます。

imgCount = imageArray.length;
current = 0;

配列変数から、配列に格納されている要素の数を参照します。
配列内は画像ばかりのため、配列要素の数が画像の数になるので、imageArray.lengthで求められる配列要素数を、画像数としてimgCountに入れておきます。
ここで、currentも設定しておきます。
一般的に、数字は1から数え始めますが、コンピュータ関連では0から数え始めるのが一般的なので、currentも0からのスタートとします。

for(var i = 0; i < imgCount; i++){
 imageArray[i].className = 'off';
}

用意した画像は、すべて表示しておく必要もないし、表示していてもどうせ見えないので、初期設定として、フェードオフされた状態にしておきます。
(実際には、ここでフェードオフがスタートします)

fade();

準備万端に整ったところで、実際にフェードを行なう関数を呼び出します。
fade関数に飛ぶ(移動する)と理解した方が分かりやすいかもしれません。
これまでのfor文は"i=0"から始まっていますが、このfor文は"i=1"から始まっています。

ページトップへ

では、関数fadeを説明します。

imageArray[current].className = 'off';

currentは現在表示されている画像を表すので、imageArray[current]で、その画像を特定し、そのクラスを"off"に書き換えます。
クラスはスタイルシートで設定しているので、あとで解説します。

current++;
if(current == imgCount){
 current = 0;
}

次の画像を表示するために、currentの値を増やします。
currentは0から始まっているので、最大値はimgCount-1なので、currentがimgCountの値になった時は、上限オーバーとして始めの画像に戻すためにcurrentを0にします。
これで、currentは次の画像を表す値になりました。

imageArray[current].className = 'on';

currentはすでに次の画像を表す値なので、次の画像を表示するために、この状態のcurrentで画像を特定し、そのクラスを"on"に書き換えます。

setTimeout(fade, interval * 1000);

壁紙チェンジャーは、切り替え続けないと意味がないので,一定時間毎に切り替え関数fadeを呼び出します。
setTimeoutで設定する時間は「ミリ秒」なので、「秒」にするために1000を掛けます。

ページトップへ

サンプルスタイルシート
* {
   margin: 0px;
   padding: 0px;
   border: 0px;
}
body {
   width: 640px;
   height: 960px;
}
img {
  position: absolute;
  top: 0px;
  left: 0px;
  width: 640px;
  height: 960px;
  -webkit-transition-property: opacity;
  -webkit-transition-duration: 3s;
}
img.off {
  opacity: 0;
}
img.on {
  opacity: 1;
}

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

次に、スタイルシート(style.css)を見ていきましょう。

いつものように、「*」で全ての要素をリセットします。
bodyは、iPhoneの標準サイズに合わせるために、幅と高さを指定します。

imgが今回の目玉です。
位置指定と大きさ指定は、いつも通りの見慣れた指定かと思います。
ここで登場するのが、「transition」です。
transitionは、時間的変化(アニメーション)の指定をします。
transition-propertyで、変化させたいプロパティを指定するのですが、今回はフェードさせることが目的なので、ここではopacityを指定します。
transition-durationは、変化にかかる時間を指定しますが、今回はゆっくり変化させるために、3秒とします。
iPhoneは、HTMLの表示にwebkitというレンダリングエンジン(群)を使用しているので、-webkit-を付加しています。

これで、imgタグに対するアニメーション指定ができたので、このimgタグのクラスを指定しておきます。
img.offで画像を消したいので、opacityを0にします。
img.onでは逆に画像を表示させたいので、opacityを1にします。
javascriptによってこのクラスが書き換えられると、imgタグに指定されたアニメーション(時間的変化)が始まります。

ページトップへ

サンプル表示

基本サンプルファイルは、個々でダウンロード&実行して確認していただきたいのですが、スクリーンショットを3枚だけ紹介します。

変化前

変化中

変化後

とりあえず3枚紹介しましたが、やはり個々の実機で確認していただくのが一番かと思います。

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

ページトップへ

ソースを見直す

ここでjavascriptのソースを見直してみましょう。
これは、作っては見たものの、このページを作成中に気になったことであり、拡張するまえにやっておいた方が良いと判断したためです。

javascriptの変更

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

var interval;
var imageArray;
var imageCount ;
var current ;

function init() {
 interval = 5;
 imageArray = document.getElementsByTagName("img");
 imgCount = imageArray.length;
 current = 0;
 fade();
}

function fade() {
 imageArray[current].className = 'off';
 current = (current+1) % imgCount;
 imageArray[current].className = 'on';

 setTimeout(fade, interval * 1000);
}

サンプルJavaScript2

2カ所ありますが、1つ目は変更ではなく削除です。
初期設定で、クラスをoffにする形で画像を消していたのですが、スタイルシートで代用することにしました。
2つ目は変更なのですが、次の画像を表すcurrentの計算方法を、if文による算出方法ではなく、演算による算出方法にしました。
1つ増加させたcurrentを画像総数imgCountで割った余りを求めることで実現しています。
これで、結構シンプルになったのではないでしょうか?

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

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

さきほどのjavascriptで削除した部分を補うための変更です。
と言うより追加ですね。

始めからopacityを0にしておくことで、目に見えない状態で表示します。

追加・変更したサンプルを実行すると、右のようになります。
とてもシンプルで見やすいかな?

サンプル表示

基本的には変わりませんが、スタートが違います。
今回は、何もないところからの表示になります。
ちなみに、スクリーンショットはありません。


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

ページトップへ

ソースを見直す2

ソースの「記述方法」について見直しましたが、ここでは「構造」について見直します。
アニメーションにかかる時間が、そのアニメーションの間隔(インターバル)の中に入っているのです。
具体的には、アニメーションは5秒おきに実行されますが、そのアニメーションに3秒かかるので、アニメーションが終わってから次のアニメーションが始まるまで2秒しかありません。
インターバルを5秒で設定したのなら、アニメーションが終わってから5秒は動いて欲しくないので、インターバルとは別に、アニメーション時間も時間稼ぎをしましょう。

javascriptの変更

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

var interval;
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(fade, interval * 1000);
}

function fade() {
 imageArray[current].className = 'off';
 current = (current+1) % imgCount;
 imageArray[current].className = 'on';

 setTimeout(wait, delay * 1000);
}

サンプルJavaScript3

こでまでは、初期設定が終わったらフェードに飛んでいましたが、waitという時間稼ぎ用の関数を作り、そのwaitに飛びます。
waitは、時間稼ぎでしかありません。intervalの時間後にfadeに飛びます。
fadeに飛んだら切り替えがスタートするのですが、この関数でも時間稼ぎしています。
アニメーションが終わってからwaitに飛ぶようにするためです。
このようにすることで、切り替えの間隔と切り替えにかかる時間を独立させることができます。
スタイルシートに変更はありません。

サンプル表示

こちらも基本的には変わりません。
ただ、切り替えの間隔をしっかり5秒取っているので、スタードが遅くなりました。


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

ページトップへ

残った課題

今回ウィジェットは、フェードすれば良いという形で終わった感じです。
やはり、きちんと壁紙が表示され、しばらくしてからフェードが始まるようにしなければなりませんが、この課題は次回に持ち越しとします。
さらに、アニメーションにかかる時間の3秒をjavascriptでもスタイルシートでも指定しています。
問題ないような動きにはなっていますが、時間を変更しようとすると、どちらも同じ数字で変更しなければならないので、プログラム的には無駄な記述です。
この課題も持ち越しとします。

2011/10/01