Algorithm & UI/UX
Canvas上での柔軟なスタンプ描画とマルチデバイス座標変換
本ツールでは、サーバーへの画像アップロードを一切行わず、ブラウザ内のみで高度なスタンプ合成を実現しています。その核となるCanvas操作の最適化と、直感的な操作を支えるアルゴリズムについて紐解きます。
1. マルチライン・テキストの動的計測と自動レイアウト
ユーザーが入力した任意の行数のテキストに対し、常に最適なサイズのスタンプ背景(ふせん)を描画するため、Canvas APIの measureText を利用した動的なサイズ計算を行っています。
単に文字を描画するだけでなく、全行の中で最も長い行の幅を基準にボックスのサイズを決定し、フォントスケールに合わせた余白(padding)を動的に割り当てています。
// テキストのマルチライン計測と背景サイズの決定(概念コード)
function calculateStampLayout(ctx, note, scale) {
const lines = note.split('\n');
const baseSize = // 独自のフォントスケール係数に基づく基本サイズ計算
ctx.font = `${baseSize}px 'Zen Kurenaido'`;
let maxWidth = 0;
// 各行の幅を計測して最大幅を取得
lines.forEach(line => {
const width = ctx.measureText(line).width;
if (width > maxWidth) maxWidth = width;
});
const padding = baseSize; // スケールに応じた動的な余白
return {
width: maxWidth + padding * 2,
height: lines.length * baseSize * // 行間係数 + padding
};
} 2. 解像度依存を排除する座標変換変換アルゴリズム
Canvasの内部解像度(描画サイズ)と、画面上の表示サイズ(CSSサイズ)が異なる場合、マウスやタッチの座標をそのまま使うとスタンプの位置がズレてしまいます。
これを防ぐため、getBoundingClientRect で取得した表示領域と実際の解像度の比率をリアルタイムに計算し、座標を正規化するロジックを実装しています。
// 表示領域と内部解像度のギャップを埋める座標変換
function getNormalizedPos(canvas, event) {
const rect = canvas.getBoundingClientRect();
const clientX = // マウスまたはタッチイベントからの座標取得
const clientY = // マウスまたはタッチイベントからの座標取得
// Canvasの解像度と表示サイズの比率(スケール)を算出
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
// 画面上の座標をCanvas内部の座標系へ変換
return {
x: (clientX - rect.left) * scaleX,
y: (clientY - rect.top) * scaleY
};
} 3. クライアントサイドでのHEIC変換とリサイズ処理
iPhone等の最新デバイスで撮影されたHEIC形式の画像をそのままブラウザで扱うため、heic2any を用いた動的な変換処理を行っています。
また、高解像度画像によるブラウザのメモリ枯渇を防ぐため、アスペクト比を維持しつつ、一定の「安全な解像度」へ自動的にリサイズするハックを導入し、快適な動作を保証しています。
// 負荷を抑えるための自動リサイズとフォーマット変換
async function processImage(file) {
let blob = file;
// HEIC形式の場合はブラウザで扱える形式にオンメモリ変換
if (isHeic(file)) {
blob = await convertHeicToJpeg(file);
}
const img = await loadImg(blob);
const MAX_VAL = // 端末の負荷を考慮した独自の最大解像度しきい値
// アスペクト比を維持したリサイズ計算
let { w, h } = // 独自のチューニング値による縮小計算
// オフスクリーンCanvasを用いて描画・リサイズを実行
const offscreen = document.createElement('canvas');
// ...描画処理
return offscreen.toDataURL('image/jpeg', // 独自のクオリティ設定値);
} Developer's Note
このツールで最もこだわったのは、単なる「文字入れ」ではなく、「日記としての温かみ」をどう表現するかです。 手書き風フォントの読み込み待ちが発生してもレイアウトが崩れないよう、フォントのロード状態を監視してからCanvasを描画する制御を入れています。
また、スマホユーザーが「画像を保存する」ボタンで躓かないよう、iPhone特有の挙動(インライン表示での保存の難しさ)を考慮し、長押しを推奨するモーダルを別途実装するなど、技術的なロジックと同じくらいUXの細部にも注力しました。 すべての処理がブラウザで完結しているため、プライバシーを気にせず「日常の断片」をエモく彩っていただければ幸いです。