画像をアップロードするとそれをパズルにするwebアプリを作りました。
作り方をご紹介します!
制作には主にpythonを使用しています。
パズルの仕様
・基本の分割は10×10
・ピースがシャッフルされた状態でスタート
・ドラッグ&ドロップでピースを移動する
・ピースを正しい場所にすべて配置すると完成メッセージを出す
実装
pythonのstreamlitとPILを主に用いて実装しました。
分割
def split_image(image, rows=10, cols=10):
width, height = image.size
piece_w = width // cols
piece_h = height // rows
pieces = []
for i in range(rows):
for j in range(cols):
box = (j * piece_w, i * piece_h, (j + 1) * piece_w, (i + 1) * piece_h)
piece = image.crop(box)
buf = io.BytesIO()
piece.save(buf, format="PNG")
base64_str = base64.b64encode(buf.getvalue()).decode()
pieces.append({
"id": i * cols + j,
"data": f"data:image/png;base64,{base64_str}"
})
return pieces, piece_w, piece_h
box: (left, upper, right, lower) 形式で座標指定し
image.crop() でその部分を切り出しています。
切り出したピースを一時的にPNG形式で保存し、それをBase64エンコード
.decode()はバイナリ→文字列変換(JSで埋め込みやすくするため)
各ピースはユニークなID(左上→右下へ0から順に)と画像データを持つ辞書にしてリスト化して管理しています。
画像を描画
function render() {{
container.innerHTML = "";
order.forEach((piece, index) => {{
const img = document.createElement("img");
img.src = piece.src;
img.draggable = true;
img.className = "piece";
img.dataset.index = index;
container.appendChild(img);
}});
}}
orderの順番通りにピース画像を並べます。
data-indexは後でドラッグ時の入れ替えに使用しています!
ドラッグ & ドロップ
let draggedIndex = null;
container.addEventListener("dragstart", e => {{
draggedIndex = e.target.dataset.index;
}});
どのピースがドラッグされたか記録し
container.addEventListener("drop", e => {{
const targetIndex = e.target.dataset.index;
if (draggedIndex !== null && targetIndex !== null) {{
[order[draggedIndex], order[targetIndex]] = [order[targetIndex], order[draggedIndex]];
render();
checkCompletion();
}}
}});
ドロップ先のピースと位置を交換し、render()で再描画しています。
checkCompletion()は完成チェックの関数です。
実際の画面
実際に作成した画面はこんな感じです
便宜上パズルを4×4にしてます