APNG/GIF画像を複数表示する方法(連続表示/最初からアニメーション)

# はじめに
APNGもしくはGIF画像を画面内に複数表示する必要があり、いくつか問題にぶつかりました。
私が直面した問題と解決策を残します。

# この記事の対象者
- 連続で同じAPNG/GIF画像を連続表示したい人
- 一度使用したAPNG/GIF画像を最初からアニメーションさせたい

# 同じAPNG/GIF画像を連続表示させる
Vueで作成していきます。最小限のtemplateを共通で利用して、scriptの部分に手を加えていきます。

### 共通template部分
```vue
<template>
    <button @click="createImg">
        画像追加
    </button>
    <div class="gif-list" ref="list"></div>
</template>
```
### 扱うGIFアニメーション画像
以下のカウントダウンアニメーションGIF画像を利用します。再生時間は10秒で、繰り返し再生はしません。

<img src="https://drive.google.com/uc?export=view&id=1AWuguewwzA3cjxk1Vv8aWUBHD1WBj_Jr"/>


## 1. 何も手を加えずGIF画像を連続表示する
まずはシンプルにref属性`list`が指定されているdivタグの下にcreateElementで生成したimgタグを追加していく処理を試してみます。
```js
createImg() {
    const newElement = document.createElement("img");
    newElement.src = "画像パス";
    this.$refs.list.prepend(newElement);
}
```

以下のようになりました。
![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/ef209f44-c2d8-4b06-afba-0b64b5617ba8.gif)

同じGIF画像を追加していくと、最初に表示したGIFアニメーションと同じアニメーションが実行されてしまいます。後から追加したGIF画像を各々最初からアニメーションさせるにはどうすれば良いでしょうか。



## 2. 連続表示したGIF画像を各々最初からアニメーションさせる
URLパラメータを付与して別のファイル名で呼び出すことで、追加したGIF画像がそれぞれ独立して最初からアニメーションするようになります。

```js
createImg() {
    this.count++;
    const newElement = document.createElement("img");
    newElement.src = "画像パス" + `&${this.count}`; // URLパラメータを適当に追加する
    this.$refs.list.append(newElement);
}
```
以下のようになりました。
![Videotogif (1).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/92107f7d-8810-4072-ffc1-d049233b095a.gif)

追加したGIF画像が最初からアニメーションするようになりましたね。

## 連続表示の際に通信量を削減する
これで連続表示で最初からアニメーションさせる件は解決しましたが、パフォーマンス面の問題があります。以下が連続表示した際の通信状況です。

![スクリーンショット 2022-12-03 11.04.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/7e37736b-27e2-fec8-ce41-e4d52aa3e6f4.png)

**毎回通信発生してるじゃん…**

サイズは少量ですが、高速で連打を長時間されたらサイトに負荷がかかってしまいます。なのでこちらも対策を取ります。

### 繰り返しで同じファイル名を利用する

```js
createImg() {
    this.count++;
    const newElement = document.createElement("img");
    newElement.src = "画像パス" + `&${this.count}`;
    newElement.id = `gif-${this.count}`
    this.$refs.list.prepend(newElement);
  
    // 5個表示したらカウントを0にする
    if (this.count === 5) {
        this.count = 0;
    }
}
```
画像を5個表示したらカウントを0に戻し、同じファイル名のGIFアニメーションを表示するようにしました。

以下のようになりました。
![スクリーンショット 2022-12-03 11.56.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/db37a8d2-3b6d-3d7a-3f30-6b0ade51a14e.png)

これで画像取得の最大回数は5回になり、大量の通信発生の心配はありませんね！
しかし実際に表示される内容は以下のようになりました。

![Videotogif (3).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/e8dc64fa-64af-c7f9-5aa2-1a87bd24cb56.gif)
**一度表示したファイル名と同じURLパラメータだと最初からアニメーションしない…**

それでは最初からGIFアニメーションを最初から再生させたい場合は、通信を大量に発生させるしかないのでしょうか。

### アニメーションを最初から再生させる
最終的に以下のようになりました。
```js
createImg() {
    this.count++;
    // 再度表示する場合は一度srcを空にする
    const removeElement = document.querySelector(`#gif-${this.count}`);
    if (removeElement) {
        removeElement.src = "";
        removeElement.remove();
    }

    const newElement = document.createElement("img");
    newElement.src = "画像パス" + `&${this.count}`;
    newElement.id = `gif-${this.count}`
    this.$refs.list.prepend(newElement);
  
    // 5個表示したらカウントを0にする
    if (this.count === 5) {
        this.count = 0;
    }
}
```

以下結果です。

![Videotogif (4).gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2849606/8842a8db-3942-7845-e6ba-5c407c8aa409.gif)

追加したGIF画像が最初から再生されているのがわかりますでしょうか？通信も5回しか発生していません。

追加したのは以下の部分になります。
```js
// 再度表示する場合は一度srcを空にする
const removeElement = document.querySelector(`#gif-${this.count}`);
if (removeElement) {
    removeElement.src = "";
    removeElement.remove();
}
```
画像を最初から再生するために重要なのは既に表示中のGIF画像のsrcを空にしている部分です。画面上に表示されているGIF画像のimg要素のsrcを空にすることで、後から同じGIF画像を挿入したら最初からアニメーションされることがわかりました。