LoginSignup
2
0

APNGの再生タイミングを任意で行う

Posted at

gif でも Lottie でもなく、 apng で実装。

1つ目を再生→数秒終了したままの1つ目を表示→1つ目を非表示&2つ目を再生
のような動きを繰り返し行うようにしたい…。

まず最初に「再生タイミング」と「終了タイミング」ってどうするのか、というところから考えて、 js で制御はできないのか、と試すことに。

apng-canvas.js

以前使用していた apng-canvas.jsAPNG非対応ブラウザ1で表示する際に使用していました。
それもあって、一度こちらで対応してみようとしたのですが、再生→停止→再度再生できるようにはならず。
APNG.parseBuffer の項目にあった rewind() を使って、APNG のアニメーション終了に合わせて再度再生を行おうと思ったのですが、止まる位置にズレが多く、また終了タイミングもうまく取れなかったため、別の方法はないかと探すことになりました。

※一度だけアニメーションを動かすだけの場合であればこちらで大丈夫かと思います。
確かnpmパッケージ登録がされていないプラグインになるので、cdnで読み込む必要があります。

apng-js

上で記述した apng-canvas を作成された方が、別のプラグインも作成されていました。
こちらだと再生・一時停止・停止など、apng をコントロールできるイベントが多く、思った通りの動きはできそうでした。

README に記述してある通りにまずは書いてみました。

index.js
import parseAPNG from 'apng-js';

const img = 'xxx.png'
const apng = parseAPNG(img);

エラーが出てしまい、動きません。
README を見ると、 parseAPNG の引数は画像のURLなどではなく、画像のバイナリデータを入れる、とのことです。バイナリデータ…?

いろいろな記事を参考にしつつ、以下のように書いてみました。

index.js
import apngJs from 'apng-js/lib/index.js'

const parseAPNG = apngJs.default;

const getImgBuffer = (url) =>
	new Promise(async (resolve) => {
		const blob = await fetch(url).then((res) => res.blob());
		const reader = new FileReader();
		reader.readAsArrayBuffer(blob);
		reader.onload = () => {
			resolve(reader.result);
		};
	}
);

async function createAPNGPlayer(_target, _options = {}) {
  const imgBuffer = await getImgBuffer(_target.src);
	const apng = parseAPNG(imgBuffer);
	Object.keys(_options).forEach((key) => {
		apng[key] = _options[key];
	});

	await apng.createImages();
 
    // 画像のバイナリデータを利用し、canvas に反映させるため、canvas は必須
    // JS で作成しなくても、html に書いてあるものを使っても良い
    const ctx = document.createElement('canvas');

	ctx.width = apng.width
	ctx.height = apng.height
 
    _target.appendChild(ctx);

    const player = await apng.getPlayer(ctx.getContext('2d'));
	return player;
}

(async () => {

    // 1つ目
    const img = document.querySelector('.img');
	const apng = await createAPNGPlayer(img);

    // 2つ目
    const img2 = document.querySelector('.img2');
	const apng2 = await createAPNGPlayer(img2);

    // apng の再生
    apng.play();

    apng.on('end', () => {
        apng.stop();
        apng2.play();
    });

    apng2.on('end', () => {
        apng2.stop();
        apng.play();
    });

})();

これで最初に希望していた動きを実装することができました。
あとは再生してみて適宜 settimeoutなどを挟んで調節して終わります。

apng をこうやって扱うこと自体、そこまでなさそうではあります。
ですが、条件が色々と重なった結果使うこともあるかなと思い、記事に残しておきます。もしかしたら自分もすっきり忘れたころに見ながら対応するかもしれない。

参考にさせていただいた記事

  1. 2024年3月15日現在、主要なブラウザは apng に対応しています。
    https://caniuse.com/?search=apng

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0