本連載では分散型マイクロブログ用ソフトウェアMisskeyの開発に関する紹介と、関連するWeb技術について解説を行っています。
今回は画像の美しいプレースホルダを描画することのできるBlurHashについて紹介します。
読み込み中画像の扱い
Webページやアプリでは画像が多用されますが、ユーザーの回線速度によっては表示されるまでは時間がかかります。
Misskeyにおいても、タイムラインではユーザーのアイコン、投稿に添付された画像のサムネイルなどが一画面に多く表示されます。
通常、画像が読み込まれるまでの間は空白の表示になることが多いですが、そのままだとレイアウトシフトを引き起こしたり、殺風景な印象を与えます。
そこで、表示に一工夫加えることで画面読み込み時のUXを向上させることができます。
BlurHash
Misskeyでは、読み込み時のUXを向上させるために、BlurHashというオープンソースのライブラリを使用しています。
BlurHashは、非常に小さい追加のデータ量で、画像が読み込まれるまでの間の簡易な
プレースホルダとしてレンダリングされる画像は、元の画像を大きくぼかした
ちなみに開発しているのはフードデリバリーサービスのWoltです。
仕組み
BlurHashの仕組みを見ておきましょう。
BlurHashでは、まずあらかじめ元画像データをプレースホルダのレンダリング用文字列
次のようなBase83の文字列としてエンコードされます。
eVMb9Zoi-sg2xe?dt8V[s:WA$oR}WBWAS0$:WUSaaybVt7t7oMj^R*
次に、ブラウザやアプリケーションで元画像
BlurHash stringは画像のようなバイナリデータとは異なり、単なるASCII文字列なので、そのままHTMLに埋め込んだり、APIのJSONレスポンス内に埋め込めるのがポイントです。
画像を文字列に変換するアルゴリズムには、JPEGなどでも使われるDCT
実装
それでは、BlurHashの実装を解説します。
エンコード
Node.
ノート: BlurHashの実装はJavaScript以外にも様々な言語でされています。
エンコード時は画像データをUint8ClampedArray
で渡す必要があるので、sharpを使用して画像を読み込んでいます。
import * as blurhash from 'blurhash';
import sharp from 'sharp';
function encodeBlurhash(imageFilePath: string): Promise<string> {
return new Promise((resolve, reject) => {
sharp(imageFilePath)
.raw()
.ensureAlpha()
.resize(64, 64, { fit: 'inside' })
.toBuffer((err, buffer, info) => {
if (err) return reject(err);
let hash;
try {
hash = encode(new Uint8ClampedArray(buffer), info.width, info.height, 5, 5);
} catch (e) {
return reject(e);
}
resolve(hash);
});
});
}
エンコードして生成したBlurHash stringは、元ファイルと一緒にデータベースなどに保存しておきます。
デコード
ブラウザ側でのデコード処理の例を示します。
ノート: デコードについても様々な言語で実装されているので、WebだけでなくiOSやAndroidなどのネイティブアプリでも使うことができます。
import * as blurhash from 'blurhash';
function renderBlurhash(hash: string, canvas: HTMLCanvasElement): void {
const buffer = blurhash.decode(hash, 32, 32);
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(32, 32);
imageData.data.set(buffer);
ctx.putImageData(imageData, 0, 0);
}
なおMisskeyではパフォーマンスを追求するため、独自のデコーダー実装を行っています。
使う際のコツ
BlurHashを使う際のコツをまとめます。
サムネイルをエンコードする
画像を扱う際、元の画像とは別に、縮小されたサムネイルを用意することも一般的です。
BlurHashのエンコードは元画像ではなくサムネイルに対して行うようにするとパフォーマンスを向上できます。
BlurHashは大きくぼかした画像を出力するので、エンコード元画像の解像度は品質に影響を与えません。
高解像度でレンダリングしない
デコード時、Webの場合小さめのcanvasに描画してから、CSSでcanvas要素自体を所望のサイズに引き伸ばすとパフォーマンスを向上できます。
プレースホルダは大きくぼかした画像なので、高解像度でレンダリングしても無駄に処理量が増えるだけです。
詳細度を上げすぎない
BlurHashではエンコード時に、どれだけ詳細度
一見すると詳細度を上げたほうがより綺麗なプレースホルダを表示できそうですが、現在のBlurHashが採用しているDCTでは、むしろ醜くなる傾向があります。
また詳細度を上げるほどデータ量は増えますので、詳細度は上げすぎないほうが無難です。
プレースホルダ以外の用途
BlurHashは主に画像読み込み中のプレースホルダとして使用されることを想定して開発されていますが、他にも用途はあり、Misskeyでは以下の用途にも活用しています。
- センシティブ設定された画像のプレビュー表示
- ユーザーが画像をクリックするまでのプレビューとしてBlurHashを使用することで、センシティブな画像の表示にワンクッション挟んでいます。
- 画像の平均色の抽出
- 画像全体の平均の色をBlurHashで抽出して、ユーザーアイコンの猫耳の色付けに使用したりしています。
まとめ
BlurHashを使うと、ほとんど追加のデータの必要なしに、美しいプレースホルダを表示することができます。ライブラリ自体も軽量で、いろいろな言語で利用できますので導入のハードルも低いでしょう。
Webページの読み込み中、何も表示されないのと簡易でもプレースホルダが表示されるのとでは、ユーザーに与える印象が変わります。ぜひ活用してみてください。