Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="ja">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <meta http-equiv="X-UA-Compatible" content="ie=edge">
- <title>15パズル</title>
- <style>
- *, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; position: relative; }
- .center, .misc, .puzzle { left: 0; right: 0; margin-left: auto; margin-right: auto; }
- .misc { width: 100%; padding: 10px; max-width: 320px; }
- .header { text-align: center; font-size: 30px; }
- .puzzle { width: 100%; padding: 10px; max-width: 320px; }
- .puzzle_container { border: 1px solid #000; width: 100%; padding-top: 100%; }
- .puzzle_blank { position: absolute; width: 25%; padding-top: 25%; }
- .puzzle_pannel { position: absolute; width: 25%; padding-top: 25%; cursor: pointer; }
- .puzzle_pannel::before { content: ""; width: calc(100% - 10px); height: calc(100% - 10px); position: absolute; top: 5px; left: 5px; border: 1px solid #000; }
- .puzzle_num { display: flex; justify-content: center; align-items: center; width: 100%; height: 100%; position: absolute; top: 0; left: 0; font-size: 30px; }
- .shuffled .puzzle_pannel { transition: top 0.3s, left 0.3s; }
- </style>
- </head>
- <body>
- <header class="header">15パズル</header>
- <main class="main">
- <div class="puzzle"></div>
- <div class="misc">
- <button class="reset">RESET</button>
- <button class="shuffle">SHUFFLE</button>
- </div>
- </main>
- <script>
- (function(d){
- // 1から15までのパネル及びブランク(null)の位置関係を調べるための配列
- // 長さは4x4の16。座標(x,y)に位置する要素は、配列上ではpannels[x+y*4]に存在する
- const pannels = [];
- let container = null; // パズル本体
- // パネルをスライドさせる処理
- // x, y はパネルの座標でそれぞれ0以上4未満の整数
- function slide(elem, dx, dy) {
- const x = Math.max(Math.min(3, parseInt(elem.dataset.x) + dx), 0);
- const y = Math.max(Math.min(3, parseInt(elem.dataset.y) + dy), 0);
- elem.style.left = x * 25 + "%";
- elem.style.top = y * 25 + "%";
- elem.dataset.x = x;
- elem.dataset.y = y;
- }
- //動かしたいパネルの上下左右にブランク(null)があるか判定し、もしブランクがあるなら
- //パネルとブランクを入れ替えスライドさせる
- function action(elem) {
- const x = parseInt(elem.dataset.x);
- const y = parseInt(elem.dataset.y);
- const current = x + 4 * y; // 配列上のパネルのインデックス
- const above = current - 4;
- const bellow = current + 4;
- const left = current - (x > 0 ? 1 : 0);
- const right = current + (x > 2 ? 0 : 1);
- if (pannels[above] === null) {
- pannels[above] = pannels[current];
- pannels[current] = null;
- slide(elem, 0, -1);
- } else if (pannels[bellow] === null) {
- pannels[bellow] = pannels[current];
- pannels[current] = null;
- slide(elem, 0, 1);
- } else if (pannels[left] === null) {
- pannels[left] = pannels[current];
- pannels[current] = null;
- slide(elem, -1, 0);
- } else if (pannels[right] === null) {
- pannels[right] = pannels[current];
- pannels[current] = null;
- slide(elem, 1, 0);
- }
- }
- // パネルを作ってコンテナに追加する
- function createPannel(num, x, y) {
- if (num > 15) { // numが16ならブランク
- return null;
- }
- const ret = d.createElement("div");
- //パネルの現在座標はdata属性で保持
- ret.dataset.x = x;
- ret.dataset.y = y;
- ret.style.top = y * 25 + "%";
- ret.style.left = x * 25 + "%";
- const span = d.createElement("span");
- span.classList.add("puzzle_num");
- span.textContent = String(num);
- ret.classList.add("puzzle_pannel");
- ret.addEventListener("click", function(){
- action(this);
- });
- ret.appendChild(span);
- container.appendChild(ret);
- return ret;
- }
- //パネルをシャッフル。
- //ブランクの周囲のパネルを一つランダムに選択し動かすのを指定回数繰り返す
- function shuffle(times) {
- container.classList.remove("shuffled"); // CSSアニメーションを一旦停止
- while (times-- > 0) {
- const offset = [-4, 4, -1, 1];
- const blank = pannels.indexOf(null);
- if (blank < 4) { // ブランクが一番上の行にある場合
- offset[0] = 4;
- }
- if (blank > 11) { // ブランクが一番下の行にある場合
- offset[1] = -4;
- }
- if (blank % 4 === 0) { // ブランクが一番左の列にある場合
- offset[2] = 1;
- }
- if (blank % 4 === 3) { // ブランクが一番右の列にある場合
- offset[3] = -1;
- }
- action(pannels[blank + offset[Math.random() * 4 | 0]]);
- }
- container.classList.add("shuffled"); // CSSアニメーションを再度セット
- }
- //パズルの初期化
- //必要なエレメントを作ってDOMに追加する
- function init() {
- container = d.createElement("div");
- container.classList.add("puzzle_container");
- //15個のパネルをconatinerに追加
- for (let y = 0; y < 4; ++y) {
- for (let x = 0; x < 4; ++x) {
- pannels.push(createPannel(y * 4 + x + 1, x, y));
- }
- }
- d.querySelector(".puzzle").appendChild(container);
- container.classList.add("shuffled");
- }
- //パズルのリセット
- //一旦パズルを全部破棄してもう一度初期化
- function reset() {
- while (pannels.length > 0) {
- pannels.pop();
- }
- d.querySelector(".puzzle").removeChild(container);
- container = null;
- init();
- }
- //ページ読み込み時の処理
- init();
- d.querySelector(".reset").addEventListener("click", reset);
- d.querySelector(".shuffle").addEventListener("click", function(){
- shuffle(1000);
- });
- })(document);
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement