跳至拼圖
前幾天,我的腦海中突然浮現出童年時的一個小拼圖玩具的回憶,這是一個滑塊拼圖,其中15 個方形瓷磚以4 x 4 的單元格排列方式放置在一個框架中,留下一個可用空間。每塊瓷磚和框架邊緣上的一組脊和凹槽允許瓷磚彼此滑過,同時將瓷磚固定在框架中。在任何給定時間,與自由空間相鄰的任何圖塊都可以移動到該空間中,否則這些圖塊將被阻止移動。將一塊圖塊移動到可用空間中,然後在該圖塊來自的地方留下一個新的可用空間,然後另一個圖塊可以移動到該新空間。這個想法是透過以這種方式重複滑動圖塊來將圖塊排列成某種預定的順序。
顯然這被稱為“15 Puzzle”,自 1870 年代以來就已存在。搜尋網路會回傳許多用各種程式語言寫的遊戲,實際上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://dev .to/claurcia/slide-puzzle-5c55 Go 中的/dev.to/mfbmina/building-a-sliding-puzzle-with-go-3bnj。對於那些學習 JavaScript 的人來說,它也是一個很好的入門挑戰。
不過,激起我興趣的是它應該可以在網路上完全不使用程式語言來重新創建!也就是說,僅使用純 HTML 和 CSS 的實作。所以我在下面介紹一下。我必須做出的一個妥協是,提供的 10 場比賽具有固定的預先洗牌的起始位置。
為此,預定的順序是顯示完整的圖片。
此實現的基本原理是每個圖塊保留其在幀內位置的狀態記錄。在 HTML 和 CSS 中更改和保持狀態的方法並不多,但最常見的是“複選框黑客”,並且此實現大量使用了它。對於不熟悉複選框駭客的人來說,當
因此每個圖塊都有一對單選按鈕組,每個單選按鈕組有四個單選按鈕。其中一組保留圖塊在 X 軸上的位置,另一組保留圖塊在 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 */
圖塊還包含八個標籤元素,對應八個單選按鈕。每個標籤都是絕對定位的,彼此重疊,並且每個標籤都完全填充圖塊。標籤是透明的,並且最初透過設定pointer-events:none 將其設定為不回應點擊和點擊。
下一步是 CSS 選擇器辨識空白儲存格的位置。這是透過消除來完成的,它是 X、Y 座標不由 15 個圖塊中任何一個的單選按鈕組對錶示的單元格。
例如,如果符合:
.frame:not(:has(.tile .X0:checked~.Y0:checked)) { .... }
那麼空單元格目前必須位於左上角儲存格中。對 16 個儲存格中的每一個重複此操作,其中一個將完全匹配。
完成後,可以辨識與空白單元格相鄰的儲存格。如果空單元格位於角落,則剛好有兩個圖塊可以移動到該單元格中,否則,如果空單元格靠著框架的一側,則有三個圖塊可以移動到該單元格中,否則空單元格可以移動到該單元格。單元格必須是四個中間單元格之一,並且有四個方塊可以移動到它。對於每個圖塊,圖塊的八個標籤之一將啟動將圖塊移動到空單元格所需的正確單選按鈕。透過將其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中文網其他相關文章!