4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

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

Last updated at Posted at 2025-04-24

画像をアップロードするとそれをパズルにする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

4
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?