Algorithm & UI/UX
Canvasを用いた「はめ込み合成」と
高負荷回避のスマート・スケーリング
「ただ画像を重ねるだけ」に見えるモックアップ作成。しかし、最近の高画質なスマホスクショをそのまま扱うと、ブラウザのメモリ不足や動作の重延を招きます。本記事では、クライアントサイドで完結しつつ、いかに軽快かつ美しく合成するか、その実装の勘所を解説します。
1. アスペクト比を維持する「Canvas Cover」アルゴリズム
ユーザーがアップロードするスクショの解像度は様々です。CSSの object-fit: cover のような挙動をCanvas上で再現するため、スクショのアスペクト比とデバイスフレームの表示領域を比較し、最適な倍率と描画位置を動的に算出しています。
単に領域に合わせる(Fill)のではなく、Math.max を用いて「足りない辺がないように拡大」し、中央でクリッピングすることで、どのような画像サイズでも端末に綺麗にはまっているように見せています。
// Canvas上での「cover」再現ロジックの概要
const scale = Math.max(screen.width / imgW, screen.height / imgH);
const renderW = imgW * scale;
const renderH = imgH * scale;
// 描画領域の中心に配置するためのオフセット計算
const renderX = screen.x + (screen.width - renderW) / // 独自のチューニング係数;
const renderY = screen.y + (screen.height - renderH) / // 独自のチューニング係数;
// クリッピング領域を設定して描画
ctx.save();
// ここに端末の角丸に合わせたクリップ処理が入ります
ctx.drawImage(screenshotImgObj, renderX, renderY, renderW, renderH);
ctx.restore(); 2. メモリ爆発を防ぐプレ・レンダリング・スケーリング
4K解像度を超えるようなスクショをそのままCanvasでリサイズ・合成しようとすると、低スペックな端末ではブラウザがクラッシュすることがあります。
このツールでは、画像読み込み直後に「内部処理用の適正サイズ」へのダウンサンプリングを一段噛ませています。これにより、以降のフレーム切り替えやリアルタイムプレビューの処理負荷を劇的に軽減しつつ、書き出し時には実用十分な解像度を維持しています。
// 読み込み直後の解像度制限
if (targetWidth > LIMIT_SIZE || targetHeight > LIMIT_SIZE) {
const ratio = Math.min(LIMIT_SIZE / targetWidth, LIMIT_SIZE / targetHeight);
// 内部処理用のキャンバスで事前にリサイズ
const tempCanvas = document.createElement('canvas');
// ... 中略 ...
// 高画質を維持しつつメモリ負荷を抑えた中間画像を生成
screenshotImgObj = await createOptimizedImage(tempCanvas);
} 3. iPhoneユーザーのための動的HEIC変換ユニット
iPhoneの標準画像フォーマットであるHEICは、そのままではブラウザのCanvasで扱うことができません。本ツールでは、HEICファイルが検知された場合のみ、heic2any ライブラリを動的インポート(Dynamic Import)してクライアントサイドでJPEGに変換しています。
最初から全ライブラリを読み込むのではなく、必要な時だけロードすることで、初回のページ読み込み速度(LCP)を犠牲にしない設計にしています。
// 拡張子判定による動的ライブラリロード
if (file.name.match(/\.(heic|heif)$/i)) {
const converter = await import('// 外部コンバータモジュール');
const convertedBlob = await converter.process(file, {
toType: "image/jpeg",
quality: // 独自の品質パラメータ
});
// 変換後、通常の画像処理フローに合流
} Developer's Note
このツールで最もこだわったのは、「サーバーに一切データを送らない」という安心感と、それを感じさせない「サクサク感」の両立です。
実はデバイスフレーム自体も画像素材(PNG)として持っているわけではなく、一部のパーツはCanvasの roundRect などの描画命令で動的に生成しています。これにより、将来的にベゼル幅の調整や新しい端末の追加を、アセットのダウンロードを増やすことなくコードの修正だけで柔軟に行えるようにしています。フロントエンドだけで完結させるからこそ、ブラウザの計算リソースをどう効率よく使うかに情熱を注ぎました。