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

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

基本ファイルとその確認

サンプルファイル構成

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

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

サンプルHTML

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

<!DOCTYPE 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

このサンプル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枚だけ紹介します。

変化前

変化中1

変化中2

変化後

とりあえず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秒取っているので、スタードが遅くなりました。


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

ページトップへ

作り直す

ここで、根本的な部分を考え直してみます。
HTMLで、切り替える画像の枚数分だけ<img>を用意しました。
これはこれで確かに必要です。
ただ、「壁紙チェンジャー」として必要なのは、beforeとafterの2枚だけなのです。
そこで、HTMLで用意する画像は2枚とし、その2枚に対し表示する画像をJavaScriptで切り替えるという考え方で作り直してみます。
さらに、動作のタイミングも考え直してみます。
すべてが秒数で管理されていたのですが、これでは、前の動作が終わっていなくても時間が来たら次の動作が始まってしまうというバグが発生してしまうかもしれません。
「イベントリスナー」を導入して、動作が終わったかどうかの確認してから、次の動作に移るという考え方で作り直してみます。
なお、「イベントリスナー」の導入は、「壁紙チェンジャー2(スライド編)」を参考にして下さい。

サンプルHTML

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

<!DOCTYPE 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="fade0"/>
  <img id="fade1"/>
 </body>
</html>

サンプルHTMLソース4

<body>内に画像を並べる方法ではないので、切り替えに必要なimgタグが2つあるだけになりました。
この2つの画像で切り替えを演出するのですが、画像自体も入れ替えます。
そうすることで、何枚も切り替えることが可能になります。
その仕組みは、JavaScriptで記述します。

ページトップへ

サンプルjavascript

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

var interval;
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("fade0");
 elm1 = document.getElementById("fade1");
 imgCount = img_name.length;
 current = 0;
 next = 1;

 wait();
}

function wait() {
 elm0.src = "./images/" + img_name[current];
 elm0.className = "on stay";
 elm1.src = "./images/" + img_name[next];
 elm1.className = "off stay";
 setTimeout(fade, interval * 1000);
}

function fade() {
 elm0.className = "off";
 elm1.className = "on";
 elm1.addEventListener('webkitTransitionEnd',fade_end,false);
}

function fade_end() {
 elm1.removeEventListener('webkitTransitionEnd',fade_end,false);
 current = next;
 next=(current + 1) % imgCount;

 wait();
}

サンプルJavaScript4

HTMLの要素を画像要素2つにしたことで、この2つへの処理に集中できます。
画像ソースやクラスの設定はもちろんですが、イベントリスナーの追加が大きな変更かと思います。
秒数によるタイミングではなく、フェードが終わったかどうかをきちんと確認してから次の動作に移る形になりました。
クラスの指定も特徴的で、スタイルシートのプロパティを小分けし、クラスを複数適用することで動きを制御しています。

ページトップへ

サンプルスタイルシート
* {
  margin: 0;
  padding: 0;
  border: 0;
}

body {
  width: 640px;
  height: 960px;
}

img {
  position: absolute;
  top: 0px;
  left: 0px;
  width: 640px;
  height: 960px;
  opacity: 0;
  -webkit-transition-property: opacity;
  -webkit-transition-duration: 1s;
}

.off {
  opacity: 0;
}
.on {
  opacity: 1;
}

.stay {
  -webkit-transition-property: none;
}

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

今回の作り直しは、JavaScriptがメインですので、スタイルシートの変更はほとんどありません。

処理の都合で変更になった部分と、スタイルの小分けによる追加があるくらいです。

ページトップへ

サンプル表示

中身を作り直しただけなので、見た目は何も変わりません。
フェードによる壁紙チェンジャーは、これで完成とします。

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

ページトップへ

2013/01/18