Cover Image for 高品質なスロー静止画アニメーションプログラム(html カスタム要素)を作ってみた

高品質なスロー静止画アニメーションプログラム(html カスタム要素)を作ってみた

NEXT.js
SvelteKit
WordPress
実験
animation
drawImage
requestAnimationFrame

はじめに

動画サイトで音楽を視聴していると、映像がゆっくりと移動する演出があったりします。派手な演出もその時の気分によっては良いのですが、最近では地味な演出を好んだりします。そこで今回は静止画(主に写真を想定)がゆっくりと移動する落ち着いたアニメーションプログラム(html カスタム要素)を作ってみました。静止画(写真)を利用して、手軽にアニメーションできます。実験で作ったプログラムですが結構気に入っているので公開します。また、作業の備忘録にもなるので記事にしました。

高品質な静止画(写真)を高品質なまま、カクカクしないで滑らかにアニメーションします。気に入ったら、ご自由に使ってみてください。

想定読者

この記事の想定読者は、次のようになります。

  • 自分のWebページで、高品質な写真を使って静止画アニメーションをしてみたいと思う方。(プログラミングの知識は無くても利用可能ですが、セットアップするには PC や Linux などのファイル作成・転送・編集は出来る必要があります)WordPress でのセットアップについては、筆者が実際に行った作業を元にして説明します。Next.js, SvelteKit へのセットアップも簡単に説明します。
  • プログラミングをする読者で、このようなプログラムに関心のある方。プログラムの解説は記事の後半で行います。

デモ・プレイグラウンド

この記事のアイキャッチ画像を使ったアニメーションです。アニメーションは写真を拡大したものを、画像左下から斜め右上方向に移動して、画像中央を表示する位置で終了します。実際にご覧ください。

アニメーションをもう一度見たい場合は、画像をクリック・タップしてください。

CodePen を使ったプレイグラウンドを置きます。JS (javascript) ソースコードは、ここから取得(コピー)してください。画面右上の「EDIT ON」をクリックすると html・JS を編集できるようになります。画像ソース(src)、アニメーション時間 (animeTime)、拡大比率 (expansionRatio) を変更できます。画像ソースは必須です。アニメーション時間と拡大比率はオプションで、指定しない場合はデフォルト値(時間は 15 秒、拡大は 1.15 倍)となります。

WordPress での使用方法

少し先にあるセットアップ方法でプログラムをセットアップした後に、アニメーションしたい場所に「カスタムHTML」を挿入して、次ように記述します。(アニメーション時間 (animeTime)と拡大比率 (expansionRatio) を指定することもできます)

<anime-image
  src=(ここに画像ソースを記述)
/>

使用する画像ソース(写真)について

なるべく解像度の高い写真をおすすめします。この記事のアイキャッチ・デモで使用している写真は 3840 x 2160 です。640 x 480 のような写真でも拡大表示でアニメーションできます。

解像度の高い写真はファイルサイズが大きくなるので、jpeg ならば avif などに変換してファイルサイズを小さくするのを推奨します。変換する方法は様々ありますが、良く分からない場合は Squoosh がお手軽で良いかもしれません。

セットアップ(インストール)方法

プログラム自体は、1ファイルに記述した JS ファイルです。このファイルを実行環境に入れて使用できるようにします。WordPress に関しては、やった事のない読者を想定して手順を紹介します。

WordPress の場合

プラグインを作れば簡単にインストールできますが、現状、作ってません。筆者は手作業でインストールしました。この方法だと(恐らく)使用しているテーマは何でも大丈夫ではないかと思います。

WordPress で使用しているテーマによっては、「カスタムjavascript」を個別のページで設定できるものがあります。例えば Cocoon には存在しますが、Twenty Twenty-four ではみつかりませんでした。筆者は Cocoon を使っているので、「カスタムjavascript」に JS プログラム (animeImage.js) をエディタで開いてコピー&ペーストして動作確認できました。特に、試しに動かしたい場合はこの方法が簡単だと思います。

(1) JS ファイルを WordPress の 利用しているテーマフォルダに置きます。

  • JS ファイル名は animeImage.js とします。このファイルの中身は、デモのところの CodePen からコピペで作成します。
  • テーマファイルの置かれるフォルダ名は「wp-content/themes」で、このフォルダ以下に個別のテーマフォルダが存在します。例えば、Twenty Twenty-four ならば「twentytwentyfour」というフォルダです。利用しているテーマフォルダに animeImage.js を置きます。
  • 実際にファイルを置く方法は、ファイル転送、または vi 等のなんらかのエディタで直接ファイルを作成するなど、お好きな方法で行ってください。

この Blog サイトの記事「Google Cloud 無料枠の Compute Engine で WordPress を立ち上げる」を参照して、Linux(GCP) に docker を使って WordPress を立ち上げた読者の場合は、GCP に ssh でログインして sudo bash でルートユーザーになってから「cd /var/lib/docker/volumes/wp_wordpress_data/_data/wp-content/themes」とすればテーマフォルダに行けます。

(2) 利用しているテーマフォルダにある functions.php の最後に、次に示す行を追記します。

function add_script_files(){
  wp_enqueue_script('anime-image-js',
    get_template_directory_uri().'/animeImage.js', array(), false, true);
}
add_action( 'wp_enqueue_scripts', 'add_script_files' );

functions.php の編集を誤ると、WordPress が正常に動作しなくなるリスクが有ります。その場合に備えて、元に戻せるようにfunctions.php のバックアップを取るなどの対策をしてください。自己責任の上で作業を行ってください。

Next.js の場合

筆者の場合は、app/[slug]/page.tsx に 以下を追加しました。

import "../../lib/animeImage";

animeImage.js を JS などを置いているフォルダに置いて、import 文のパスが正しいか確認してください。animeImage.js の内容は、デモの CodePen からコピペしてください。

JS が使えるプラットホーム・フレームワークならば何でも使えるように考えて Pure JS でプログラムを作成したのですが、Next.js (React) との相性は良くありません。筆者は記事を WordPress で書いているのでフレームワークに依存したくなかった・出来なかった、共通で使いたいという事情もあります。開発環境でテストしていると、クライアントとサーバーで DOM が一致しないというワーニングが出ていました。このアニメ JS は実行時に、表示用の canvas を DOM に追加しているので、SSG で作成している筆者の場合、サーバー側では canvas が存在しないので不一致となります。ただ、本番環境で動かすと、DOM 不一致のワーニングは出ていないのでこのまま使用することにしました。

SvelteKit の場合

筆者の場合は、src/routes/[slug]/+page.svelte に 以下を追加しました。(赤字の部分)

<script lang="ts">
(省略)
  import { onMount } from 'svelte';
  onMount(() => {
    import("$lib/animeImage");
  });
(省略)

animeImage.js を src/lib に置いています。animeImage.js の内容は、デモの CodePen からコピペしてください。

アニメーションプログラム解説

何を使って動かすか?

選択肢は、WebGL, CSS, Canvas 2D API drawImage() があるかと思います。やりたい事は静止画の移動です。それに対して WebGL は大袈裟に感じたので外しました。CSS を使って移動アニメーションを簡単に作れるのですが、ゆっくりスムースに動かすところで躓きました。画像の移動単位がピクセル単位、つまり整数なので、動きの速いアニメーションでは問題無くスムースに見えます。しかし、「ゆっくり」と動かすと「カクカク」してしまいます。解決できないか少し試行錯誤したのですが、良い解決策を見つけられませんでした。

ゆっくりとスムースに動かすには、描画座標を整数でなく小数で扱える必要があります。Canvas 2D API の CanvasRenderingContext2D.drawImage() メソッドは、座標を小数で扱えます。このメソッドは理解しやすく扱いも簡単なので使用することにしました。参考にした Webサイトを貼っておきます。

og-image
CanvasRenderingContext2D: drawImage() メソッド - Web API | MDN
Canvas 2D API の CanvasRenderingContext2D.drawImage() メソッドは、キャンバスに画像を描画するいくつかの方法を提供します。
https://developer.mozilla.org/ja/docs/Web/API/CanvasRenderingContext2D/drawImage

アニメーションの描画タイミング

アニメーションをやるのにちょうど良いメソッド、requestAnimationFrame があるのでこれを利用しました。これはディスプレイのリフレッシュレートに合わせてくれます。筆者が使用している液晶ディスプレイのリフレッシュレートは 60Hz ですが、実際に計測してみると毎秒 60 回描画する (60fps) のを確認できます。ゲーム用ディスプレイは高いリフレッシュレートで、それに合わせると恐らく 144 fps などになるのでしょう。

JS ソースコードの 56 行目あたりに fps コンソール表示 ON/OFF の if 文があります。fps 表示ロジックは、この if (false) で実行しないようにしています。これを True に修正すると fps をコンソール出力するので、興味があれば試してみてください。

  if (false) { // fps 計測ロジック
    ++info.count;
    if ((t - info.beforeTime) > 1000.0) {
      console.log(info.src + ": " + elapsed + ": "
        + Math.round(info.count / ((t - info.beforeTime) * 0.001)) + " fps");
      info.beforeTime = t;
      info.count = 0;
    }
  }

オフスクリーン Canvas の利用

本プログラムのアニメーションは、風景写真で森林の葉の一枚一枚のような細かい描写の写真は苦手です。テストで、上高地・河童橋の写真をPCでアニメーションしたところ、激しく画面がチラついて「見れたものでない」惨状となりました。(画像サイズを 1280 x 720 に縮小した写真を参考までに貼っておきます)スマホで確認すると、あまり気にならないレベルでアニメーション表示します。スマホの画像処理は優秀と感じました。チラつきの原因は、フレーム毎の画像縮小時のピクセル輝度が大きく変化するためと思われます。

kappa-bashi

解決策を試してみて、効果的な方法を見つけました。最初に、オフスクリーン Canvas に表示するサイズの縮小画像を描画しておき、オフスクリーン Canvas の内容を位置を移動しながら実際に表示する Canvas に描画するという方法です。現在は、この方法を採用しています。

画面がチラつく原因を調べていて、「サブピクセルレンダリング」という技術を知りました。個人的に面白いと思ったので紹介しておきます。

サブピクセルレンダリング(Subpixel rendering)とは、液晶・有機ELモニタ上のピクセルを構成する三原色の発光源を、仮想的にそれぞれ一つのピクセルとみなして横方向に三倍の解像度を得る技術のことである。アンチエイリアスの一種。

本来は連携動作で適切な色を得るための画素をばらばらに発光させるため、イメージの周辺部では独特な色のにじみが発生する。このことから、主として色の違いが大きな問題にならないフォントのレンダリングに用いられる。

https://ja.wikipedia.org/wiki/サブピクセルレンダリング より引用

フレーム毎の描画座標計算

リフレッシュレートは実行環境で異なるため、つまり fps は実行環境で異なるため、アニメーション開始時からの経過時間を使って座標計算します。

HiDPI 対応

HiDPI や Retina では、表示上のピクセル (CSSピクセル) と物理ピクセルの比率 (devicePixelRatio) が 2 以上になります。この比率が 3 だった場合、表示上のピクセルが 600 x 300 の画像を表示するのに適した画像サイズは 1800 x 900 となります。600 x 300 の画像で表示した場合、ぼやけた表示になってしまいます。プログラムでは、この比率を window.devicePixelRatio で取得できます。

筆者が使用しているディスプレイは HiDPI 対応でない普通のものですが、アニメーションを表示してみると少しぼやけて見えます。Chrome と Firefox を見比べると、そもそも画質が異なるのですが、Firefox の方が差を大きく感じました。試しに、devicePixelRatio を 2 として、縦横倍の画像にしてみるとクッキリした感じになりました。プログラムでは、devicePixelRatio の最小値は 2 になるようにしています。

class AnimeImage extends HTMLElement {
  constructor() {
(省略)
    // devicePixelRatio=1 のディスプレイでも 2 をセットした方が綺麗に見えたので、最小で 2 とする。
    this.info.dpr = window.devicePixelRatio >= 2 ? window.devicePixelRatio : 2;

終わりに

Webブラウザ上で『高品質』なスローアニメーションが可能なようにプログラムを組んでみました。短いプログラムですが、試行錯誤した結果なので結構時間は掛かりました。テストして不具合・バグなどは修正していますが、まだ有ると思います。明らかにおかしいと思われるバグは、「お問い合わせ」で教えてもらえると修正できるかも知れませんので、よしなにお願いします。

このアニメーションを気に入って、使ってもらえると嬉しいです。使用する場合、筆者への連絡は不要です。

【おまけ】本記事のアイキャッチ

久しぶりに、コーエーテクモゲームスさんの「DEAD OR ALIVE Xtreme Venus Vacation」(デッド オア アライブ エクストリーム ヴィーナス バケーション:DOAXVV)で筆者が撮影した写真をアイキャッチにしてみました。

アイキャッチ画像の著作権は「コーエーテクモゲームス」さんにあります。画像の転載などはしないようお願いします。

キャラクターは「ななみ」です。ゲームで撮影する時、小道具を使うことができます。最近追加された小道具「ハンバーガーセット片手で持つ」を使って撮影してみました。大きなハンバーガーが良く見えるように、やや上にカメラを置いて撮影しています。背景は出来るだけボケるように絞りは最大に開いてますが、もう少しボカせれば良かったなと思います。小道具は、日傘、線香花火、シャボン玉、等々、けっこう沢山あります。

DOAXVV のモデリングやテクスチャは良く出来ていると思います。小道具であっても手を抜かないで作ってますね。ゲームを作っているクリエーターの方々、立派です。ところで、DOAXVV は、なかなか欲望に素直なゲームです。衣装(水着)には際どいものもあります。(普通の衣装でも「着崩れ」という機能があるため際どく変化したりします)今回の撮影では衣装(水着)をいくつかチョイスして撮影してました。実は、万人向けには刺激が強いと思われるので没にした写真があったりします。(爆)


More Stories

Cover Image for PageSpeed Insights で携帯パフォーマンススコアを満点 (100) にした

PageSpeed Insights で携帯パフォーマンススコアを満点 (100) にした

実験
AVIF
LCP
PageSpeed Insights
srcset
TBC

実験として PageSpeed Insights の携帯(スマホ)パフォーマンスを満点 (100) になるまでチューニングしたので、その作業内容について記事にしてみました。主に LCP と TBT について改善しています。少しでもパフォーマンスチューニングの参考になれば幸いです。

Cover Image for WordPress で Tailwind CSS を使う方法

WordPress で Tailwind CSS を使う方法

WordPress
Tailwind CSS

WordPress で Tailwind CSS を使う方法 3種類を具体的に説明しています。「Tailwind CDN を使う方法」「プラグインを使う方法」「Tailwind CLI を使う方法」の 3種類です。