以下の3パターンでエフェクト画像のアニメーションを表示します。
1)メイン画面の全体に画像を拡大して中央に表示します(青い光のエフェクト)
2)表示領域を指定して、その表示領域内にそのままの画像サイズで中央に表示します(黄色い光のエフェクト)
3)表示領域を指定して、その表示領域内に収まるように画像を縮小して中央に表示します(雷のエフェクト)
黒い領域をクリックするとエフェクトのアニメーションを表示します。
※エフェクトの画像は「ぴぽや」様( https://pipoya.net/ )よりお借りしています。
フォルダー構成
以下の構成でフォルダーを作成し、その下にファイルを置きます。
- Sample03
-
Sample03.html
- css
-
Sample03.css
-
- js
-
Sprite.js ※前頁の「画像を表示する方法」で作成したものと同じ
-
Anime.js
-
Canvas.js
-
Loop.js ※以前の頁の「メッセージウィンドウの作り方」で作成したものと同じ
-
Sample03.js
-
- img
-
pipo-btleffect109f.png ※青い光のエフェクト画像
-
pipo-btleffect033.png ※黄色い光のエフェクト画像
-
pipo-btleffect003.png ※雷のエフェクト画像
-
HTML
Sample03.html
<!DOCTYPE HTML>
<html lang='ja'>
<head>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width" />
<title>アニメーションを表示する方法</title>
<link rel="stylesheet" href="css/sample03.css" type="text/css" />
<script src="js/Canvas.js"></script>
<script src="js/Sprite.js"></script>
<script src="js/Anime.js"></script>
<script src="js/Loop.js"></script>
<script src="js/sample03.js"></script>
</head>
<body>
<div class="sample03">
<div class="メイン画面">
<canvas class="レイヤー1" width="640" height="640"></canvas>
</div>
<div class="画像">
<img src="img/pipo-btleffect109f.png" alt="エフェクト1" />
<img src="img/pipo-btleffect033.png" alt="エフェクト2" />
<img src="img/pipo-btleffect003.png" alt="エフェクト3" />
</div>
</div>
</body>
</html>
20〜22行目で表示する3つの画像ファイルを読み込んでいます。
CSS
Sample03.css
body,div,p,td,th {
margin: 0;
padding: 0;
}
.メイン画面 {
position: relative;
width: 640px;
height: 640px;
background-color: rgba(0, 0, 0);
margin: 20px auto;
}
canvas {
position: absolute;
}
.画像 {
display: none;
}
ファイル名が変わっているだけで、内容は前頁の「画像を表示する方法」で作成したものと同じです。
JavaScript
Anime.js
class アニメスプライト extends スプライト {
constructor(cObj) {
super(cObj.canvas);
this.cObj = cObj; // Canvasのオブジェクト
}
画像を設定する(img, 画像の横幅, 画像の縦幅, x1, y1, x2, y2, 拡大フラグ) {
this.img = img; // 表示する画像のimg要素
this.imgWidth = 画像の横幅;
this.imgHeight = 画像の縦幅;
this.フレーム画像の座標を設定する();
this.表示座標を設定する(x1, y1, x2, y2, 拡大フラグ);
}
フレーム画像の座標を設定する() {
this.フレーム数 = 0;
this.フレームカウンタ = 0;
this.imgXList = [];
this.imgYList = [];
const 横フレーム数 = Math.floor(this.img.width / this.imgWidth);
const 縦フレーム数 = Math.floor(this.img.height / this.imgHeight);
for (let i = 0; i < 縦フレーム数; i++) {
for (let j = 0; j < 横フレーム数; j++) {
this.imgXList.push(j * this.imgWidth);
this.imgYList.push(i * this.imgHeight);
this.フレーム数++;
}
}
}
アニメーションを開始する(id, img, 画像の横幅, 画像の縦幅, x1, y1, x2, y2, 拡大フラグ) {
return new Promise(resolve => {
this.画像を設定する(img, 画像の横幅, 画像の縦幅, x1, y1, x2, y2, 拡大フラグ);
this.cObj.更新関数を追加する(id, () => {
if (this.フレームカウンタ >= this.フレーム数) {
this.cObj.更新関数を削除する(id);
resolve();
return;
}
this.imgX = this.imgXList[this.フレームカウンタ];
this.imgY = this.imgYList[this.フレームカウンタ];
this.フレームカウンタ++;
// フレーム画像をコンテクストの座標に表示する
this.ctx.drawImage(this.img, this.imgX, this.imgY, this.imgWidth, this.imgHeight, this.ctxX, this.ctxY, this.ctxWidth, this.ctxHeight);
});
});
}
}
1行目のextendsが示すとおり、アニメスプライトクラスは前頁で作成したスプライトクラスから派生しています。
アニメーションを表示するには、一つの画像ファイル内の複数のフレームから特定のフレームを抜き出して表示する必要があります。スプライトクラスは、画像を画面上の指定した位置に表示することは出来ますが、画像内の特定の部分を抜き出すことはできません。
アニメスプライトクラスは、画像内の特定の部分を抜き出せるようにし、それをスプライトクラスの処理で画面上の指定した位置に表示します。
31行目のアニメーションを表示する関数の1番目の引数は、Canvasオブジェクトに追加する関数のidを任意の値で指定します。
2番目の引数は、アニメーション画像のimg要素を指定します。
3番目の引数は、抜き出すフレームの横幅を指定します。
4番目の引数は、抜き出すフレームの縦幅を指定します。
5〜8番目の引数は、画面上の表示領域の座標を指定します。
9番目の引数は、フレームのサイズが表示領域のサイズよりも小さい場合に、表示領域に合わせて拡大するかどうかを指定します。
32行目では、Promiseのオブジェクトを作成し、アニメーションを表示する処理を非同期で実行します。
34行目では、Canvasオブジェクトから定期的にコールさせたい関数(34〜47行目)を追加しています。
35行目で最後のフレームを表示した後は、36行目でCanvasオブジェクトに追加した関数を削除し、フレームをこれ以上表示しないようにします。
37行目で非同期処理を終了しています。
Canvas.js
class Canvas {
constructor(canvas, fps) {
this.canvas = document.querySelector(canvas);
this.ctx = this.canvas.getContext('2d');
this.更新関数リスト = [];
this.クリック時コールバック関数 = null;
this.canvas.onclick = this.クリック時関数.bind(this);
this.fps = fps;
this.累計時間 = 0;
}
更新関数を追加する(id, 更新関数) {
this.更新関数リスト.push({ id, 更新関数 });
}
更新関数を削除する(id) {
this.更新関数リスト = this.更新関数リスト.filter(x => x.id != id);
}
更新する(経過時間) {
this.累計時間 += 経過時間;
if (this.fps > this.累計時間) {
return;
}
this.累計時間 -= this.fps;
this.クリアする();
this.更新関数リスト.forEach(x => x.更新関数());
}
クリアする() {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
クリック時関数(evt) {
if (this.クリック時コールバック関数) {
this.クリック時コールバック関数(evt);
}
}
}
Canvasクラスは、一定時間ごとにコンテクストをクリアして、コンテクストに描画する関数をコールします。アニメスプライトのオブジェクトを更新する関数はこのクラスのオブジェクトからコールされます。
12行目の更新関数を追加する関数は、更新関数を更新関数リストにidを付けて追加します。
16行目の更新関数を削除する関数は、idで指定した更新関数を更新関数リストから削除します。
20行目の更新する関数は、コンテクストをクリアし、28行目でアニメスクリプトのオブジェクトを更新する関数をコールします。
35行目のクリック時関数は、canvas要素をクリックした時にコールされます。クリック時コールバック関数が指定してあれば、その関数をコールします。
Sample03.js
class Sample03 {
static main() {
Sample03.画像リスト = document.querySelectorAll('.sample03 .画像 img');
Sample03.レイヤー1 = new Canvas(".sample03 .レイヤー1", 1000 / 12);
Sample03.レイヤー1.クリック時コールバック関数 = Sample03.アニメーションを開始する;
Sample03.エフェクト画像 = new アニメスプライト(Sample03.レイヤー1);
ループ.開始する();
}
static async アニメーションを開始する(evt) {
ループ.更新関数を追加する("レイヤー1", Sample03.レイヤー1.更新する.bind(Sample03.レイヤー1));
await Sample03.エフェクト画像.アニメーションを開始する('エフェクト', Sample03.画像リスト[0], 120, 120, 0, 0, 640, 640, true);
await Sample03.エフェクト画像.アニメーションを開始する('エフェクト', Sample03.画像リスト[2], 240, 240, 0, 0, 480, 480);
await Sample03.エフェクト画像.アニメーションを開始する('エフェクト', Sample03.画像リスト[1], 640, 240, 0, 0, 500, 640);
ループ.更新関数を削除する("レイヤー1");
}
}
addEventListener('load', Sample03.main);
4行目で、Canvasクラスのオブジェクトを作成します。
この時、更新間隔は12fps(1秒間に12フレームを表示)にしています。
5行目で、Canvas要素がクリックされた場合は、アニメーションを開始する関数をコールするように設定します。
6行目でアニメスプライトのオブジェクトを作成します。
7行目でループ処理を開始します。
11行目で、ループ処理にCanvasオブジェクトを更新する関数を追加します。
12行目で、アニメスプライトのオブジェクトにアニメーション画像(青い光のエフェクト画像)を設定しアニメーションを表示します。
この行のawaitにより、アニメーションが終了するまで処理を待ちます。
13、14行目も同様にアニメーションを表示します。
3種類のアニメーションの表示が終わると15行目でループ処理からCanvasオブジェクトを更新する関数を削除します。