パズルにスキップ
先日、子供の頃の小さなパズルおもちゃの思い出が頭に浮かびました。それは、15 個の正方形のタイルが 4 x 4 のセル配置でフレーム内に 1 つの空きスペースを残して配置されるスライダー パズルです。各タイルとフレームの端にある一連の隆起と溝により、タイルをフレーム内に保持しながらタイルが互いにスライドして通過できます。空きスペースに隣接するタイルはいつでもそのスペースに移動できますが、それ以外の場合、タイルは移動できません。タイルを空きスペースに移動すると、タイルの元の新しい空きスペースが残り、別のタイルがその新しいスペースに移動できるようになります。このようにタイルを繰り返しスライドさせることで、あらかじめ決められた順序でタイルを配置するというアイデアです。
これは「15 パズル」と呼ばれるもので、1870 年代から存在しているようです。 Web を検索すると、さまざまなプログラミング言語で書かれた多数のレクリエーションが表示されます。実際、dev.to には https://dev.to/artydev/let-us-code-a-sliding-puzzle-9n を含むいくつかの記事があります。 、https://dev.to/xzanderzone/making-a-slider-puzzle-in-java-script-83m、https://dev.to/claurcia/slide-puzzle-5c55 はすべて JavaScript と https:/ Go の /dev.to/mfbmina/building-a-sliding-puzzle-with-go-3bnj。また、JavaScript を学習している人にとって、優れた入門課題としても提示されています。
しかし、私の興味をそそられたのは、プログラミング言語をまったく使用せずに、Web 上で再作成できるべきだという考えでした。つまり、純粋な HTML と CSS だけを使用した実装です。そこで以下に紹介します。私が妥協しなければならなかったのは、提供されている 10 のゲームがシャッフル前の開始位置が固定されていることです。
この場合、あらかじめ決められた順序で完成した写真を表示します。
この実装の基本原理は、各タイルがフレーム内のどこにあるかの状態記録を保持することです。 HTML や CSS で状態を変更したり保持したりする方法はそれほど多くありませんが、最も一般的なのは「チェックボックス ハック」であり、この実装ではそれを多用しています。チェックボックスのハックに慣れていない人のために説明すると、
各タイルには、それぞれ 4 つのラジオ ボタンを持つ 2 つのラジオ ボタン グループがあります。これらのグループの 1 つは X 軸でのタイルの位置を保持し、もう 1 つは Y 軸での位置を保持します。 (または、必要に応じて、それぞれ水平位置と垂直位置を指定します。) 15 個のタイルには、それぞれがフレーム内の異なるセルを占めるように、最初にラジオ ボタンを介して X 座標と Y 座標の異なる組み合わせが与えられます。
タイルは最初にフレームの上部左側のセルに配置され、次にラジオ ボタンに変換変換を適用することでラジオ ボタンの状態を測定する CSS を介してフレーム内で移動します。
/* "X" refers to the X-axis cell positions, "Y" to the Y-axis cell positions. * 0, 1, 2, 3 refers to the position on that axis, * 0 = left and top, 3 = right and bottom respectively. */ .tile:has(.X0:checked~.Y0:checked) { transform: translate(0%, 0%); } .tile:has(.X0:checked~.Y1:checked) { transform: translate(0%, 100%); } .tile:has(.X0:checked~.Y2:checked) { transform: translate(0%, 200%); } .tile:has(.X0:checked~.Y3:checked) { transform: translate(0%, 300%); } .tile:has(.X1:checked~.Y0:checked) { transform: translate(100%, 0%); } /* and so on for the remainder of the sixteen combinations */
タイルには、8 つのラジオ ボタンに対応する 8 つのラベル要素も含まれます。各ラベルは絶対に互いに重なって配置され、それぞれがタイルを完全に埋め尽くします。ラベルは透明で、最初はすべてのラベルに pointer-events:none を設定することで、クリックやタップに応答しないように設定されています。
次のステップは、CSS セレクターが空のセルがどこにあるかを識別することです。これは消去によって行われ、X、Y 座標が 15 個のタイルのいずれのラジオ ボタン グループ ペアによっても表されていないセルです。
たとえば、これが一致する場合:
.frame:not(:has(.tile .X0:checked~.Y0:checked)) { .... }
その場合、空のセルは現在左上隅のセルにある必要があります。 16 個のセルごとにこれを繰り返すと、そのうちの 1 つが一致します。
それが完了すると、空のセルに隣接するセルが特定できます。空のセルが隅にある場合、そのセルに移動できるタイルはちょうど 2 つあります。そうでない場合、空のセルがフレームのいずれかの辺にある場合、セルに移動できるタイルは 3 つあり、そうでない場合は、空のセルに移動できます。セルは中央の 4 つのセルの 1 つである必要があり、そこに移動できるタイルは 4 つあります。これらのタイルのそれぞれについて、タイルの 8 つのラベルのうち 1 つだけで、タイルを空のセルに移動するために必要な正しいラジオ ボタンがアクティブになります。このラベルは、pointer-events の値を auto に戻すことで有効になります。例として:
/* Top, left corner */ .frame:not(:has(.tile .X0:checked ~ .Y0:checked)) { :is( .tile:has(.X0:checked ~ .Y1:checked) label.Y0, .tile:has(.X1:checked ~ .Y0:checked) label.X0 ) { pointer-events: auto; } } /* right most cell of row two */ .frame:not(:has(.tile .X1:checked ~ .Y3:checked)) { :is( .tile:has(.X1:checked ~ .Y2:checked) label.Y3, .tile:has(.X0:checked ~ .Y3:checked) label.X1, .tile:has(.X2:checked ~ .Y3:checked) label.X1 ) { pointer-events: auto; } } /* second cell from left on row three */ .frame:not(:has(.tile .X2:checked ~ .Y1:checked)) { :is( .tile:has(.X2:checked ~ .Y0:checked) label.Y1, .tile:has(.X2:checked ~ .Y2:checked) label.Y1, .tile:has(.X1:checked ~ .Y1:checked) label.X2, .tile:has(.X3:checked ~ .Y1:checked) label.X2 ) { pointer-events: auto; } }
The last step of the game is to identify when the puzzle is solved. This is simply a case of checking that the 15 tiles all have their expected X and Y axis radio buttons set to their "solved" position.
/* Each tile is assigned a letter "a" to "o". * The puzzle is solved when the tiles are in alphabetical order * reading left to right and top to bottom */ .frame:has(.a .X0:checked ~ .Y0:checked):has(.b .X1:checked ~ .Y0:checked):has( .c .X2:checked ~ .Y0:checked ):has(.d .X3:checked ~ .Y0:checked):has(.e .X0:checked ~ .Y1:checked):has( .f .X1:checked ~ .Y1:checked ):has(.g .X2:checked ~ .Y1:checked):has(.h .X3:checked ~ .Y1:checked):has( .i .X0:checked ~ .Y2:checked ):has(.j .X1:checked ~ .Y2:checked):has(.k .X2:checked ~ .Y2:checked):has( .l .X3:checked ~ .Y2:checked ):has(.m .X0:checked ~ .Y3:checked):has(.n .X1:checked ~ .Y3:checked):has( .o .X2:checked ~ .Y3:checked ) ~ .options .success { display: block; }
The rest is cosmetic. The sliding is done with a simple transition of the transform described above
.tile { transition: 0.5s transform; @media (prefers-reduced-motion) { transition: none; } }
and each tile shows a portion of the game's image using background-size and background-position
.tile { background-size: 400%; } #board1 .tile { background-image: url("https://alohci.net/image/dev.to/slidergame/mullermarc-k7bQqdUf954-unsplash.webp"); } .a { background-position: 0% 0%; } .b { background-position: 33.333% 0%; } .c { background-position: 66.667% 0%; } .d { background-position: 100% 0%; } .e { background-position: 0% 33.333%; } /* and so on for the remaining tiles */
and there's a single set of radio buttons to choose which of the ten games to play.
To play the game, simply click or tap on the tile you want to slide to the empty cell.
I've also provided a "barebones" mode to show the tile letters and radio buttons which might help with the understanding of how the HTML and CSS works.
So here's the completed puzzle game. Please let me know any feedback you have.
以上がHTML と CSS の「パズル」スライダー ゲームの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。