アップロードした画像をパズルにする #Python - Qiita

画像をアップロードするとそれをパズルにするwebアプリを作りました。
作り方をご紹介します!
制作には主にpythonを使用しています。

puzzle_2.png

パズルの仕様

・基本の分割は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にしてます

puzzle_1.png

puzzle_2.png

画面収録 2025-04-24 2.05.22.gif

puzzle_3.png



フラッグシティパートナーズ海外不動産投資セミナー 【DMM FX】入金

Source link