跳至拼图
前几天,我的脑海中突然浮现出童年时的一个小拼图玩具的回忆,这是一个滑块拼图,其中 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中文网其他相关文章!