猫の手ツール

Algorithm & Visual Engineering

CSS背景制御と配列操作による
非破壊的パズルエンジンの実装

「スワップパズル・チェンジ」は、アップロードされた画像を実際に分割して保存するプロセスを一切行いません。CSSの描画特性とJavaScriptの配列操作を組み合わせ、100%クライアントサイドで完結する堅牢なパズル体験を実現しています。

1. background-position による仮想スライス技術

このツールの最大の特徴は、画像をN個のファイルに「切り刻んでいない」ことです。単一の画像URLを使い、グリッド内の各ピース(div)に対して、背景の表示開始位置(`background-position`)とサイズを動的に計算して割り当てる手法を採用しています。

メリット: Canvasで画像を切り出し、複数のBlobやデータURLを生成するメモリ負荷の高い処理をスキップできます。これにより、5x5(25ピース)といった高密度な分割でも、アップロード直後に遅延なくゲームを開始することが可能です。

// ピースごとの背景位置を動的にマッピングするロジック
// ピースが持つ本来のID(正解位置)に基づいてオフセットを算出
const pieceId = // ピースの本来のインデックス;
const gridSize = // 3, 4, 5などの分割数;

// 背景サイズを分割数に合わせて拡大
innerDiv.style.width = `${gridSize * 100}%`;
innerDiv.style.height = `${gridSize * 100}%`;

// 独自の計算ロジックにより、1枚の画像から特定部位を抽出
// X座標とY座標のオフセットを負の値でセットすることで、覗き窓のように表示
innerDiv.style.left = `-${(pieceId % gridSize) * 100}%`;
innerDiv.style.top = `-${Math.floor(pieceId / gridSize) * 100}%`;

2. 配列の分割代入を用いたアトミックなスワップ処理

ユーザーが2つのピースを選択した際の入れ替え処理は、DOMを直接操作するのではなく、データモデル(`pieces`配列)の操作を起点にしています。JavaScriptの「分割代入(Destructuring Assignment)」を活用することで、一時変数を必要としないクリーンな実装を行っています。

メリット: ステート(状態)の更新が直感的でバグが入りにくくなります。また、クリア判定においても `Array.prototype.every` を用いて、配列のインデックスとピースのIDを比較するだけの宣言的なコードで完結しています。

// 2つのインデックスを指定してデータを入れ替える
function swapPieces(idx1, idx2) {
    // 分割代入によりアトミックに入れ替えを実行
    [pieces[idx1], pieces[idx2]] = [pieces[idx2], pieces[idx1]];
    
    // データ更新後にUIを再描画(リアクティブな設計)
    renderPuzzle();
    
    // クリア判定のトリガー
    checkClear();
}

// 判定ロジック:全要素が「現在の場所 = 本来の場所」であればクリア
function checkClear() {
    const isCleared = pieces.every((p, i) => p.id === i);
    // ...以降の処理
}

3. Canvas API による動的スタンプ合成

クリア時に生成される記録画像は、Canvas APIを使用してブラウザ上で動的にレンダリングされます。元の画像をベースに、ブランドカラーのオーバーレイ、角丸クリッピング、そしてタイムやスワップ回数のテキストをレイヤー状に重ねて合成します。

メリット: サーバーサイドにデータを送信せずにSNSシェア用の高品質画像を生成できるため、ユーザーのプライバシー保護を絶対条件としつつ、ゲームとしての達成感を視覚的に提供できます。

// 記録画像のレイヤー描画プロセス
const ctx = canvas.getContext('2d');

// 1. ベース画像の描画(独自の座標とアスペクト比調整)
ctx.drawImage(originalImage, // 独自のパラメータで描画);

// 2. 統計情報パネルの背景を合成
ctx.fillStyle = '#4f46e5'; // 独自のカラー
ctx.beginPath();
// 独自の角丸パス作成処理
// ctx.roundRect(...) のような描画

// 3. テキスト情報をスタンプとして焼き付け
ctx.font = 'bold 32px sans-serif';
ctx.fillText(`TIME: ${formattedTime}`, // 独自のX座標, 独自のY座標);
ctx.fillText(`SWAPS: ${moves}`, // 独自のX座標, 独自のY座標);

Developer's Note

このパズルの開発で最も重視したのは、「触感」の軽快さです。スライダーパズルとは異なり、どのピースでも自由に選んで入れ替えられるため、ユーザーは思考停止せずにスピーディーに操作します。そのリズムを崩さないよう、CSS Transitionによるアニメーションと、配列操作による軽量な再描画のバランスを調整しました。

また、iPhoneユーザー向けに `heic2any` を使用した変換処理を組み込んでいます。通常ブラウザでは表示できない高効率画像フォーマットを、バックエンドなしで扱えるようにしたのは、当サイトの「完全サーバーレス」というポリシーへのこだわりです。

家族やペットの写真を、一切外に漏らすことなく安心してパズルにできる。この安心感こそが、技術が提供すべき最大の価値だと考えています。