无需 HTML 或 JS 即可构建 CSS 游戏
这一切都始于@jheyy 的一个演示(由于某种原因,它似乎不再在我的计算机上运行),其中他没有使用单个 HTML 元素,就创建了一个马里奥赛车动画……然后是一条推文:
我对这个挑战感到很兴奋,但不得不承认,我尝试修改JHey的代码,但没能完全解决它。我让它做了一些事情,但效果不大……所以我决定自己独立做点什么。虽然不会那么花哨,但至少能让它正常工作。
结果在这个 CodePen 上。你也可以在 Youtube 上观看演示:
注意:我只是提供了演示的链接,而不是将其嵌入到文章中,因为 WebKit 浏览器(Chrome、Safari、Brave、新版 Edge 等)似乎在处理大型动画和倾斜元素时存在问题,导致浏览器速度变慢或颜色闪烁。该演示在 Firefox 上效果会更好。
该游戏实际上可以运行(有一些怪癖,我们稍后会看到)。并且,正如在链接的 Codepen 中看到的那样,它由以下部分组成:
- 200 行 CSS(包括注释)。
- 0 行 JavaScript。
- 0 行 HTML。
HTML 的零行只是一种假象:仍然有一个带有<html>
和<body>
标签的基本 HTML 结构(正如我们在另一篇文章中解释的那样)...我们将使用它们来生成游戏。
这是什么巫术?是怎么做到的?我们来看看。
但在此之前,我要说的是,这是一种有趣的 Web 开发和 CSS 方法,非常适合练习和学习,但它几乎没有实际效果。结果是一个不错的演示,但千万不要在生产网站上尝试这样的东西。
如何完成
本文将包含大量详细的解释,但如果您想观看该过程的视频,您可以在我的 Youtube 频道上观看:
即使我们只有两个元素(<html>
和<body>
),如果我们算上每个元素的::before
和伪元素,我们就可以设置六种样式。::after
本场比赛仅使用了六个中的五个。
:背景html
和光标
遗嘱的格式html
包括三个重要部分:
- 游戏的基本背景是蓝天和绿色的地面。
- 一个看起来像汽车的定制光标。
- 透视,因此一些元素看起来是三维的(我们稍后会看到)。
背景可以用 来实现linear-gradient
。天空部分不用太花哨,因为稍后会被其他元素覆盖。地面也会包含一些其他的 ,radial-gradient
所以它不仅仅是纯绿色,而是拥有不同的色调和形状。
自定义光标可能更有趣一些。在 CSS 中,我们可以通过cursor
属性指定要使用的光标。我们可以从系统光标列表中选择,也可以使用 来设置外部图像url()
。
我们希望将所有代码都封装在 CSS 中,因此我们选择了内联 SVG 而不是外部图片文件。您可以使用如下协议添加内联 SVG :data:
html {
cursor: url(data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg'>...</svg>), auto;
}
指定自定义游标时有两个关键事项:
- 您必须始终包含系统回退。否则,浏览器将不会将图像显示为光标。
- 内联 SVG 必须包含该
xmlns
属性,否则 SVG 将不会被呈现。
对于内联 SVG,我们使用矩形和路径来模拟一辆小型汽车。结果简单却引人注目:
关于汽车的最后一个细节:汽车的左上角是实际的指针;这并不自然,因为你可以越过其中一个障碍物而不会迷路。
该cursor
属性允许设置指针在光标内的位置坐标。不过,该值必须小于 32。因此,我们相应地修改 CSS:
html {
cursor: url(data:image/svg+xml;utf8,...) 31 31, pointer;
}
html::before
:道路
html
由于我们说过稍后会看到的透视图,我们可以用::before
伪元素创建一个灰色矩形,并应用3D 旋转,使道路看起来像是逐渐消失,在远处变得越来越小。
这个作品的难点在于尺寸。最初高度是 100%,但旋转后就没到地平线的边缘了。我们不得不手动调整高度,让它们对齐。
注意:有一个数学公式可以计算精确的高度。但尝试一下更快捷的方法,几秒钟内就能得到足够接近的值。
为了绘制道路,我们使用两个线性渐变:
- 一条水平线表示铺设区域(透明-灰色-透明)。
- 一条垂直线代表红色和白色的肩线。
最后,我们创建一个小动画,让背景从上到下移动,模拟汽车的行驶。这就是相对论!汽车静止不动;是整个世界在它的轮胎下移动。
@keyframes moveRoad {
0% { background-position: 0 0; }
100% { background-position: 0 16.3vh; }
}
后来我们添加了一些阴影和模糊来稍微修饰一下道路。不过,没什么特别的。
html::after
:天空
html
的::after
伪元素用于绘制天空和地平线上的一些山脉,但它也服务于一个根本目的:隐藏主体的溢出。
正如我们将在下一节中看到的那样,它body
会“溢出”容器,看起来像是飞上了天空。为了避免这种情况,我们将天空放在它上面。然后用太阳、一些山脉、一两棵树来装饰它……它看起来就像一个护身符。
我们用“径向渐变”来生成太阳和圆润的山脉;对于山脉和树木,我们使用了linear-gradient
。然后稍微调整了一下背景的大小和位置,让所有东西都到位。
body
:乐趣开始了
这是一个棘手的部分,如果你看过视频,就会发现我的头撞到了桌子上。
这个想法是利用车身来创建一条汽车必须遵循的路径,以“赢得游戏”(游戏永远不会结束,所以唯一的选择就是最终失败)。
首先,我们首先将它制作成一个非常高的元素,然后以与道路旋转相同的角度倾斜它;这样,它就会与道路重叠。
作者注:我的问题就是从这里开始的。我当时在 WebKit 浏览器上编写代码,它开始难以处理较大的倾斜元素,尤其是在裁剪和添加动画效果之后。这时,我把开发浏览器换成了 Firefox,现在看起来运行良好了。
我们可以通过裁剪主体来实现这一点clip-path
。我们只保留部分轨道,并使用之字形路径,同时移除/裁剪其余部分。
现在,汽车/老鼠可以在 上面从右向左移动(反之亦然),body
但无需将鼠标悬停在其上方,因为它已被剪切 - 是时候用 画一些油渍了radial-gradient
。
html::after
,道路就会出现在地平线上
然后我们添加动画,让道路/障碍物从上向下移动。我们可以使用translate
变换来实现(现在不需要 3D 动画了)。
剩下唯一要做的就是当汽车/老鼠驶过油渍时停止游戏。为此,我们还剩下两个伪元素……但我们只需要一个。
body::after
:结束画面
伪元素body::after
将是一个 Game Over 屏幕。默认情况下,它是不可见的(display: none
),只有当鼠标移到body
(例如,当汽车驶过油渍时)才会显示。
我们还必须设置样式body
。在这种情况下,重要的是:
- 删除
clip-path
。否则游戏结束消息也会被裁剪! - 调整视口的高度。而不是一个很高的容器。
- 移除 3D 旋转。否则游戏结束信息也会倾斜!
- 删除动画。这样消息就不会直接滑出视口了。
- 添加 z-index。这样
body
就位于 之上html::after
。
我们可以通过使用以下几条规则重置样式来完成所有这些操作:
body:hover {
transform: none;
width: 100vw;
height: 100vh;
top: 0;
left: 0;
z-index: 1;
clip-path: none;
animation: none;
background: none;
}
body:hover::after {
display: flex;
}
我们使用了Flexbox ,这样body::after
我们就可以轻松地使用 和align-items
来居中文本justify-content
。
::before
在/::after
伪元素中添加多行内容的一个技巧是将 放在\a
新行应该出现的位置,然后添加 ,white-space: pre
以便保留空格:
body::after {
content: "GAME OVER \a\a Move the mouse \a outside of the window \a to restart the game";
white-space: pre;
/* ... */
}
额外功能和关注点
自定义设置
这个游戏的一个很酷的地方——或者至少我认为它很酷——是它可以轻松配置。
通过用CSS 变量替换一些值,我们只需更新一行代码就能调整不同的动画和游戏设置。说真的,这很酷吧?
在这个游戏中,我们添加了三个设置:
- 速度:油渍从上向下移动的动画时间(以秒为单位)。数值越低,游戏速度越快(难度也越大)。
- Timing:动画计时函数。默认情况下,它是“线性”的,这意味着动画始终保持相同的速度。我们可以将其更改为任意值,让游戏以不同的速度移动,从而增加难度。
- Animation:动画迭代次数。默认情况下,它是“无限的”,这意味着游戏将持续运行,但你可以将其更改为任意数字……说实话,这不是最好的变量名。
作者注:我尝试添加第四个 CSS 变量,以便玩家能够选择游戏的高分辨率/低分辨率版本(在小屏幕或低分辨率下运行效果更佳)。但结果不如预期……以后会再改进。
问题:hover
玩家如果输了,就必须将汽车/老鼠放在油渍上……但有一个问题:只有老鼠移动时才会触发该:hover
状态。
如果玩家将汽车放置在道路中间,然后松开鼠标,浏览器将不会触发:hover
效果,游戏永远不会结束。
从安全角度来看,这种行为是有意义的:我们不想:hover
无意中触发副作用(例如,没有任何用户交互)......这对这个游戏来说有点不幸(对于这个游戏也是如此)。
另外,还有一个陷阱。由于视角的原因,body
当它在屏幕上向上移动时会“收缩”。这意味着页面的侧面没有被覆盖body
,可以用来欺骗游戏。
一个可能的解决方案是将 的宽度body
加大,比如说200vw
而不是100vw
。这可以解决问题,但会使动画变得困难和滞后。
一个提高性能的方案是使用实际图像作为背景和图像污点。这样浏览器就不需要进行太多计算,动画也会更流畅。
最后的想法
开发这款游戏非常有趣。代码编写大约花了两个半小时(上面的视频是20倍速的,所以只有7分钟),还需要一些额外的时间进行规划。
正如我们上面提到的,这并不实用,尽管这是学习和探索 CSS 的好方法。
遗憾的是,它在 WebKit 浏览器(目前是功能最丰富的浏览器)上运行得不够流畅。新的挑战是开发一个没有那么多 3D 旋转和动画的第二个版本,这样应该能更好地适应这些浏览器。
文章来源:https://dev.to/alvaromontoro/building-a-css-game-without-html-or-js-1m30