”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > HTML 和 CSS 中的“拼图”滑块游戏

HTML 和 CSS 中的“拼图”滑块游戏

发布于2024-08-15
浏览:346

\

跳至拼图

有一天,我的脑海中突然浮现出童年时的一个小拼图玩具的回忆,这是一个滑块拼图,其中 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;
  }
}

游戏的最后一步是确定谜题何时被解决。这只是检查 15 个图块是否都将其预期的 X 和 Y 轴单选按钮设置为“已解决”位置的情况。

/* 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;
}

剩下的都是装饰性的。滑动是通过上述变换的简单转换来完成的

.tile {
  transition: 0.5s transform;
  @media (prefers-reduced-motion) {
    transition: none;
  }
}

每个图块使用背景大小和背景位置显示游戏图像的一部分

.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 */

还有一组单选按钮可以选择玩十个游戏中的哪一个。

要玩游戏,只需点击或点按您想要滑动到空单元格的图块即可。

我还提供了“准系统”模式来显示平铺字母和单选按钮,这可能有助于理解 HTML 和 CSS 的工作原理。

这就是完成的益智游戏。如果您有任何反馈,请告诉我。


版本声明 本文转载于:https://dev.to/alohci/15-puzzle-slider-game-in-html-and-css-5g2i?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • CSS可以根据任何属性值来定位HTML元素吗?
    CSS可以根据任何属性值来定位HTML元素吗?
    靶向html元素,在CSS 中使用任何属性值,在CSS中,可以基于特定属性(如下所示)基于特定属性的基于特定属性的emants目标元素: 字体家庭:康斯拉斯(Consolas); } 但是,出现一个常见的问题:元素可以根据任何属性值而定位吗?本文探讨了此主题。的目标元素有任何任何属性值,属...
    编程 发布于2025-07-17
  • eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
    eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
    称量()和ast.literal_eval()中的Python Security 在使用用户输入时,必须优先确保安全性。强大的Python功能Eval()通常是作为潜在解决方案而出现的,但担心其潜在风险。 This article delves into the differences betwee...
    编程 发布于2025-07-17
  • 在细胞编辑后,如何维护自定义的JTable细胞渲染?
    在细胞编辑后,如何维护自定义的JTable细胞渲染?
    在JTable中维护jtable单元格渲染后,在JTable中,在JTable中实现自定义单元格渲染和编辑功能可以增强用户体验。但是,至关重要的是要确保即使在编辑操作后也保留所需的格式。在设置用于格式化“价格”列的“价格”列,用户遇到的数字格式丢失的“价格”列的“价格”之后,问题在设置自定义单元格...
    编程 发布于2025-07-17
  • Java数组中元素位置查找技巧
    Java数组中元素位置查找技巧
    在Java数组中检索元素的位置 利用Java的反射API将数组转换为列表中,允许您使用indexof方法。 (primitives)(链接到Mishax的解决方案) 用于排序阵列的数组此方法此方法返回元素的索引,如果发现了元素的索引,或一个负值,指示应放置元素的插入点。
    编程 发布于2025-07-17
  • 如何有效地转换PHP中的时区?
    如何有效地转换PHP中的时区?
    在PHP 利用dateTime对象和functions DateTime对象及其相应的功能别名为时区转换提供方便的方法。例如: //定义用户的时区 date_default_timezone_set('欧洲/伦敦'); //创建DateTime对象 $ dateTime = ne...
    编程 发布于2025-07-17
  • 在Pandas中如何将年份和季度列合并为一个周期列?
    在Pandas中如何将年份和季度列合并为一个周期列?
    pandas data frame thing commans date lay neal and pree pree'和pree pree pree”,季度 2000 q2 这个目标是通过组合“年度”和“季度”列来创建一个新列,以获取以下结果: [python中的concate...
    编程 发布于2025-07-17
  • 图片在Chrome中为何仍有边框?`border: none;`无效解决方案
    图片在Chrome中为何仍有边框?`border: none;`无效解决方案
    在chrome 中删除一个频繁的问题时,在与Chrome and IE9中的图像一起工作时,遇到了一个频繁的问题。和“边境:无;”在CSS中。要解决此问题,请考虑以下方法: Chrome具有忽略“ border:none; none;”的已知错误,风格。要解决此问题,请使用以下CSS ID块创建带...
    编程 发布于2025-07-17
  • 人脸检测失败原因及解决方案:Error -215
    人脸检测失败原因及解决方案:Error -215
    错误处理:解决“ error:((-215)!empty()in Function Multultiscale中的“ openCV 要解决此问题,必须确保提供给HAAR CASCADE XML文件的路径有效。在提供的代码片段中,级联分类器装有硬编码路径,这可能对您的系统不准确。相反,OPENCV提...
    编程 发布于2025-07-17
  • CSS强类型语言解析
    CSS强类型语言解析
    您可以通过其强度或弱输入的方式对编程语言进行分类的方式之一。在这里,“键入”意味着是否在编译时已知变量。一个例子是一个场景,将整数(1)添加到包含整数(“ 1”)的字符串: result = 1 "1";包含整数的字符串可能是由带有许多运动部件的复杂逻辑套件无意间生成的。它也可以是故意从单个真理...
    编程 发布于2025-07-17
  • C++成员函数指针正确传递方法
    C++成员函数指针正确传递方法
    如何将成员函数置于c [&& && && && && && && && && && &&&&&&&&&&&&&&&&&&&&&&&华仪的函数时,在接受成员函数指针的函数时,要在函数上既要提供指针又可以提供指针和指针到函数的函数。需要具有一定签名的功能指针。要通过成员函数,您需要同时提供对象指针(此...
    编程 发布于2025-07-17
  • 在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    在Java中使用for-to-loop和迭代器进行收集遍历之间是否存在性能差异?
    For Each Loop vs. Iterator: Efficiency in Collection TraversalIntroductionWhen traversing a collection in Java, the choice arises between using a for-...
    编程 发布于2025-07-17
  • FastAPI自定义404页面创建指南
    FastAPI自定义404页面创建指南
    response = await call_next(request) if response.status_code == 404: return RedirectResponse("https://fastapi.tiangolo.com") else: ...
    编程 发布于2025-07-17
  • 为什么HTML无法打印页码及解决方案
    为什么HTML无法打印页码及解决方案
    无法在html页面上打印页码? @page规则在@Media内部和外部都无济于事。 HTML:Customization:@page { margin: 10%; @top-center { font-family: sans-serif; font-weight: bo...
    编程 发布于2025-07-17
  • 为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    为什么不````''{margin:0; }`始终删除CSS中的最高边距?
    在CSS 问题:不正确的代码: 全球范围将所有余量重置为零,如提供的代码所建议的,可能会导致意外的副作用。解决特定的保证金问题是更建议的。 例如,在提供的示例中,将以下代码添加到CSS中,将解决余量问题: body H1 { 保证金顶:-40px; } 此方法更精确,避免了由全局保证金重置引...
    编程 发布于2025-07-17
  • 如何同步迭代并从PHP中的两个等级阵列打印值?
    如何同步迭代并从PHP中的两个等级阵列打印值?
    同步的迭代和打印值来自相同大小的两个数组使用两个数组相等大小的selectbox时,一个包含country代码的数组,另一个包含乡村代码,另一个包含其相应名称的数组,可能会因不当提供了exply for for for the uncore for the forsion for for ytry...
    编程 发布于2025-07-17

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3