纯 CSS 德军总部 是我几周前做的一个小项目。 这是一个使用 CSS 3D 转换和动画的实验。
设计灵感来自于 FPS 演示 而另一 德军总部密码笔,我决定建立自己的版本。 它大致基于原版 Wolfenstein 1D 游戏的第 9 集 - 3 楼。
编辑:这个游戏故意需要一些快速反应来避免游戏结束屏幕。
这是一个播放视频:
简而言之,我的项目只不过是一个精心编写的长 CSS 动画。 加上一些实例 复选框黑客.
:checked ~ div { animation-name: spin; }
环境由 3D 网格面组成,动画大多是普通的 3D 平移和旋转。 没什么特别的。
但是,有两个问题特别难以解决:
- 每当玩家点击敌人时播放“武器开火”动画。
- 当快速移动的老板得到最后一击时,进入一个戏剧性的慢动作。
在技术层面上,这意味着:
- 选中下一个复选框时重播动画。
- 选中复选框时减慢动画速度。
事实上,在我的项目中都没有得到妥善解决! 我要么最终使用了解决方法,要么就放弃了。
另一方面,经过一番挖掘,最终我找到了解决这两个问题的关键:改变运行 CSS 动画的属性。 在本文中,我们将进一步探讨这个主题:
- 很多互动的例子。
- 解剖:每个示例如何工作(或不工作)?
- 幕后花絮:浏览器如何处理动画状态?
看着我 “扔我的砖头”.
问题 1:重放动画
第一个例子:“只是另一个复选框”
我的第一个直觉是“只需添加另一个复选框”,这不起作用:
每个复选框单独工作,但不能同时工作。 如果一个复选框已被选中,则另一个不再起作用。
以下是它的工作原理(或“不起作用”):
-
animation-name
of<div>
isnone
默认情况下。 - 用户点击一个复选框,
animation-name
成为spin
, 动画从头开始。 - 过了一会儿,用户点击另一个复选框。 新的 CSS 规则生效,但
animation-name
is 仍然spin
,这意味着没有添加或删除动画。 动画只是继续播放,就好像什么都没发生一样。
第二个例子:“克隆动画”
一种工作方法是克隆动画:
#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2; }
下面是它的工作原理:
animation-name
isnone
原来。- 用户点击“旋转!”,
animation-name
成为spin1
. 动画spin1
是从头开始的,因为它是刚刚添加的。 - 用户点击“再次旋转!”,
animation-name
成为spin2
. 动画spin2
是从头开始的,因为它是刚刚添加的。
请注意,在第 3 步中, spin1
由于 CSS 规则的顺序而被删除。 如果“再次旋转!”将不起作用首先检查。
第三个例子:“附加相同的动画”
另一种工作方法是“附加相同的动画”:
#spin1:checked ~ div { animation-name: spin; }
#spin2:checked ~ div { animation-name: spin, spin; }
这与前面的示例类似。 您实际上可以这样理解行为:
#spin1:checked ~ div { animation-name: spin1; }
#spin2:checked ~ div { animation-name: spin2, spin1; }
请注意,当“再次旋转!” 选中后,旧的运行动画将成为新列表中的第二个动画,这可能不直观。 一个直接的后果是:如果 animation-fill-mode
is forwards
. 这是一个演示:
如果你想知道为什么会这样,这里有一些线索:
animation-fill-mode
isnone
默认情况下,这意味着“如果不播放动画,则根本没有效果”。animation-fill-mode: forwards;
意思是“动画播放完毕后,必须永远停留在最后一个关键帧”。spin1
的决定总是覆盖spin2
因为spin1
稍后出现在列表中。- 假设用户点击“旋转!”,等待完全旋转,然后点击“再次旋转!”。 在这一刻。
spin1
已经完成,并且spin2
刚刚开始。
讨论
经验法则:您不能“重新启动”现有的 CSS 动画。 相反,您想要添加和播放新动画。 这可以通过以下方式证实 W3C 规范:
一旦动画开始,它就会继续,直到它结束或动画名称被删除。
现在比较最后两个示例,我认为在实践中,“克隆动画”通常会更好,尤其是在 CSS 预处理器可用的情况下。
问题 2:慢动作
有人可能会认为减慢动画只是设置更长的时间 animation-duration
:
div { animation-duration: 0.5s; }
#slowmo:checked ~ div { animation-duration: 1.5s; }
确实,这有效:
……或者是吗?
通过一些调整,应该更容易看到问题。
是的,动画变慢了。 不,它看起来不太好。 当您切换复选框时,狗(几乎)总是“跳跃”。 此外,这只狗似乎跳到了一个随机的位置,而不是最初的位置。 怎么来的?
如果我们引入两个“阴影元素”会更容易理解:
两个阴影元素都运行相同的动画但不同 animation-duration
. 它们不受复选框的影响。
当您切换复选框时,元素会立即在两个阴影元素的状态之间切换。
引用 W3C 规范:
动画运行时对动画属性值的更改会应用,就好像动画从开始时就具有这些值一样。
这是继 无国籍 设计,它允许浏览器轻松确定动画值。 实际计算说明 此处 和 此处.
另一种尝试
一个想法是暂停当前动画,然后添加一个较慢的动画从那里接管:
div {
animation-name: spin1;
animation-duration: 2s;
}
#slowmo:checked ~ div {
animation-name: spin1, spin2;
animation-duration: 2s, 5s;
animation-play-state: paused, running;
}
所以它有效:
……或者是吗?
当您单击“Slowmo!”时,它确实会变慢。 但如果你等了一整圈,你会看到一个“跳跃”。 实际上,它总是跳到“Slowmo!”时的位置。 被点击。
原因是我们没有 from
定义了关键帧——我们不应该。 当用户点击“Slowmo!”时, spin1
在某个位置暂停,并且 spin2
从完全相同的位置开始。 我们根本无法事先预测那个位置……或者我们可以吗?
一个可行的解决方案
我们可以! 通过使用自定义属性,我们可以捕获第一个动画中的角度,然后将其传递给第二个动画:
div {
transform: rotate(var(--angle1));
animation-name: spin1;
animation-duration: 2s;
}
#slowmo:checked ~ div {
transform: rotate(var(--angle2));
animation-name: spin1, spin2;
animation-duration: 2s, 5s;
animation-play-state: paused, running;
}
@keyframes spin1 {
to {
--angle1: 360deg;
}
}
@keyframes spin2 {
from {
--angle2: var(--angle1);
}
to {
--angle2: calc(var(--angle1) + 360deg);
}
}
请注意: @property
本例中使用的是 并非所有浏览器都支持.
“完美”的解决方案
之前的解决方案有一个警告:“退出慢速模式”效果不佳。
这是一个更好的解决方案:
在这个版本中,可以无缝进入或退出慢动作。 也没有使用实验功能。 那么它是完美的解决方案吗? 是和不是。
这个解决方案就像“换档”“齿轮”一样工作:
- 齿轮:有两个
<div>
s。 一个是另一个的父母。 两者都有spin
动画但有不同animation-duration
. 元素的最终状态是两个动画的累加。 - 换档:一开始只有一个
<div>
它的动画正在运行。 另一个暂停。 当复选框被切换时,两个动画交换它们的状态。
虽然我真的很喜欢这个结果,但有一个问题:这是一个很好的利用 spin
动画,通常不适用于其他类型的动画。
一个实用的解决方案(带 JS)
对于一般的动画,可以用一点 JavaScript 来实现慢动作功能:
快速说明:
- 自定义属性用于跟踪动画进度。
- 当复选框被切换时,动画会“重新启动”。
- JS代码计算正确
animation-delay
以确保无缝过渡。 我建议 本文 如果你不熟悉负值animation-delay
.
您可以将此解决方案视为“重新启动动画”和“换档”方法的混合体。
在这里正确跟踪动画进度很重要。 解决方法是可能的,如果 @property
不可用。 例如,这个版本使用 z-index
跟踪进度:
旁注:最初,我也尝试创建纯 CSS 版本,但没有成功。 虽然不是 100% 肯定,但我认为这是因为 animation-delay
is 不可动画.
这是一个带有最少 JavaScript 的版本。 只有“进入慢动作”有效。
如果您设法创建一个可用的纯 CSS 版本,请告诉我!
慢动作任何动画(使用 JS)
最后,我想分享一个适用于(几乎)任何动画的解决方案,即使有多个复杂的 @keyframes
:
基本上,你需要添加一个动画进度跟踪器,然后仔细计算 animation-delay
对于新动画。 但是,有时获取正确的值可能很棘手(但可能)。
例如:
animation-timing-function
是不是linear
.animation-direction
是不是normal
.- 中的多个值
animation-name
具有不同animation-duration
的和animation-delay
S.
致谢
在遇到纯 CSS 项目后,我开始走这条路。 有些是 精致的艺术品,有些是 复杂的装置. 我最喜欢的是那些涉及 3D 对象的,例如,这个 弹跳球 这 包装立方体.
一开始,我不知道这些是如何制作的。 后来我阅读并从中学到了很多 Ana Tudor 的这个不错的教程.
事实证明,使用 CSS 构建和动画 3D 对象与使用 CSS 没有太大区别 混合器,只是有点不同的味道。
结论
在本文中,我们检查了 CSS 动画在 animate-*
属性被改变。 特别是我们制定了“重播动画”和“动画慢动作”的解决方案。
我希望你觉得这篇文章很有趣。 请让我知道你的想法!