探索@property及其动画力量
通过 Cloudways 获取经济实惠且轻松无忧的 WordPress 托管计划 —立即开始免费试用。
呃,什么@property?这是 CSS 的新功能!它赋予你超能力。说真的,@propertyCSS 中的一些功能可以解锁我们以前无法实现的功能。
虽然一切都令人兴奋,但也许最有趣的是它提供了一种为自定义 CSS 属性指定类型的@property方法。类型为浏览器提供了更多上下文信息,这带来了一些很酷的效果:我们可以为浏览器提供过渡和动画这些属性所需的信息!
但在我们对此感到兴奋之前,值得注意的是,目前支持情况尚不完善。截至撰写本文时,@propertyChrome 和 Edge 均支持此功能。我们需要密切关注浏览器支持情况,以便能够在其他浏览器(例如 Firefox 和 Safari)上使用此功能。
首先,我们进行类型检查
@property --spinAngle {
/* An initial value for our custom property */
initial-value: 0deg;
/* Whether it inherits from parent set values or not */
inherits: false;
/* The type. Yes, the type. You thought TypeScript was cool */
syntax: '<angle>';
}
@keyframes spin {
to {
--spinAngle: 360deg;
}
}
没错!CSS 中的类型检查。这有点像创建我们自己的迷你 CSS 规范。这是一个简单的例子。来看看我们可以使用的各种类型:
lengthnumberpercentagelength-percentagecolorimageurlintegerangletimeresolutiontransform-listtransform-functioncustom-ident(自定义标识符字符串)
在此之前,我们可能依赖于使用“技巧”来为具有自定义属性的动画提供动力。
CSS 变量很棒,对吧?但作用域的威力却常常被忽视。比如这个 demo,有 3 个不同的动画,但只定义了一个💪 这意味着动态动画😎 https://t.co/VN02NlC4G8 via @CodePen #CSS #animation #webdev #webdesign #coding pic.twitter.com/ig8baxr7F3
- Jhey 🐻🛠 (@jh3yy) 2019 年 11 月 5 日
那么我们能做什么酷炫的事情呢?让我们一起来看看,激发我们的想象力。
让我们为色彩注入活力
如何通过一系列颜色或它们之间的变化来为元素添加动画效果?我非常推崇 HSL 色彩空间,它将元素分解成易于理解的数字:色相、饱和度和亮度。
为色调添加动画效果感觉很有趣。什么东西色彩缤纷?彩虹!我们有很多方法可以制作彩虹。以下是其中一种:
在此示例中,我们为彩虹的不同色带设置了 CSS 自定义属性,以便将:nth-child()其限定在各个色带内。每个色带也都设置了--index相应的属性,用于调整大小。
为了使这些带子动起来,我们可能会使用它--index来设置一些负动画延迟,然后使用相同的关键帧动画来循环色调。
.rainbow__band {
border-color: hsl(var(--hue, 10), 80%, 50%);
animation: rainbow 2s calc(var(--index, 0) * -0.2s) infinite linear;
}
@keyframes rainbow {
0%, 100% {
--hue: 10;
}
14% {
--hue: 35;
}
28% {
--hue: 55;
}
42% {
--hue: 110;
}
56% {
--hue: 200;
}
70% {
--hue: 230;
}
84% {
--hue: 280;
}
}
如果你想要“阶梯式”效果,这或许可以。但是,这些关键帧步长并不是特别精确。我使用步长来14%粗略地跳跃。
我们可以给它添加动画border-color,这样就能完成任务了。但是,我们仍然会遇到关键帧步长的计算问题。而且我们需要编写大量的 CSS 代码才能完成这个任务:
@keyframes rainbow {
0%, 100% {
border-color: hsl(10, 80%, 50%);
}
14% {
border-color: hsl(35, 80%, 50%);
}
28% {
border-color: hsl(55, 80%, 50%);
}
42% {
border-color: hsl(110, 80%, 50%);
}
56% {
border-color: hsl(200, 80%, 50%);
}
70% {
border-color: hsl(230, 80%, 50%);
}
84% {
border-color: hsl(280, 80%, 50%);
}
}
输入@property。我们先为 hue 定义一个自定义属性。这会告诉浏览器我们的自定义属性--hue是一个数字(而不是看起来像数字的字符串):
@property --hue {
initial-value: 0;
inherits: false;
syntax: '<number>';
}
HSL 中的色调值范围从0到360。我们的初始值为0。该值不会继承。在本例中,我们的值是一个数字。动画效果非常简单:
@keyframes rainbow {
to {
--hue: 360;
}
}
是的,就是这个票:
为了确保起始点准确,我们可以为每个频段添加延迟。这能给我们带来一些很棒的灵活性。例如,我们可以增加延迟,animation-duration这样就能得到一个慢速循环。在这个演示中,你可以尝试一下这个速度。
这或许不是最“疯狂”的例子,但我认为,当我们使用那些能够逻辑运用数字的色彩空间时,色彩动画会有一些有趣的机会。之前通过色轮制作动画需要一些技巧。例如,使用预处理器(例如 Stylus)生成关键帧:
@keyframes party
for $frame in (0..100)
{$frame * 1%}
background 'hsl(%s, 65%, 40%)' % ($frame * 3.6)
我们这样做纯粹是因为浏览器无法理解这一点。浏览器会将色轮上从 0 到 360 的过渡视为瞬间过渡,因为两个 hsl 值显示的颜色相同。
@keyframes party {
from {
background: hsl(0, 80%, 50%);
}
to {
background: hsl(360, 80%, 50%);
}
}
关键帧是相同的,因此浏览器假定动画保持在相同的background值,而我们真正想要的是让浏览器遍历整个色调光谱,从一个值开始,并在完成动作后以相同的值结束。
想想我们在这里拥有的所有其他机会。我们可以:
- 动画饱和度
- 使用不同的缓和措施
- 赋予光明生命力
- 尝试
rgb() - 尝试度
hsl()并将我们的自定义属性类型声明为<angle>
巧妙的是,我们可以通过作用域在元素之间共享动画值!想想这个按钮。鼠标悬停时,边框和阴影会通过色轮进行动画。
动画色彩让我想到......哇!
直接编号
因为我们可以定义数字的类型——比如integer和number——这意味着我们可以为数字添加动画效果,而不是将这些数字作为其他内容的一部分。Carter Li实际上在 CSS-Tricks 上写了一篇关于此的文章。诀窍是将integer和 CSS 计数器结合使用。这类似于我们在类似这个“纯 CSS”游戏中使用计数器的方式。
使用counter和 伪元素可以将数字转换为字符串。然后,我们可以将该字符串用作 的content伪元素。以下是一些要点:
@property --milliseconds {
inherits: false;
initial-value: 0;
syntax: '<integer>';
}
.counter {
counter-reset: ms var(--milliseconds);
animation: count 1s steps(100) infinite;
}
.counter:after {
content: counter(ms);
}
@keyframes count {
to {
--milliseconds: 100;
}
}
这给了我们类似这样的结果。很酷。
再进一步,你就拥有了一个可以工作的秒表,只用 CSS 和 HTML 就能实现。点击按钮!最棒的是,它真的可以作为计时器使用。它不会出现时间漂移。在某些方面,它可能比我们常用的 JavaScript 解决方案(例如)更精确setInterval。观看 Google Chrome 开发者提供的这段关于 JavaScript 计数器的精彩视频。
你还能用动画数字做什么?比如倒计时?
动画渐变
你知道线性、径向和圆锥曲线。你有没有想过要给颜色停止点添加过渡或动画效果?好吧,@property可以!
想象一下,我们在海滩上创建了一些波浪的渐变。一旦我们将一些图像叠加在一起,我们就可以制作出类似这样的效果。
body {
background-image:
linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)), var(--wave-four) calc(75% + var(--wave)) 100%),
linear-gradient(transparent 0 calc(35% + (var(--wave) * 0.5)), var(--wave-three) calc(50% + var(--wave)) calc(75% + var(--wave))),
linear-gradient(transparent 0 calc(20% + (var(--wave) * 0.5)), var(--wave-two) calc(35% + var(--wave)) calc(50% + var(--wave))),
linear-gradient(transparent 0 calc(15% + (var(--wave) * 0.5)), var(--wave-one) calc(25% + var(--wave)) calc(35% + var(--wave))), var(--sand);
}
这里面有很多事情要做。不过,分解一下,我们用 来创建每个色标calc()。在计算过程中,我们加上 的值--wave。这里的巧妙之处在于,当我们为该--wave值设置动画时,所有波浪层都会移动。
这就是实现这一目标所需的全部代码:
body {
animation: waves 5s infinite ease-in-out;
}
@keyframes waves {
50% {
--wave: 25%;
}
}
如果不使用@property,我们的波浪会在涨潮和退潮之间变化。但是,有了它,我们就能得到像这样的凉爽效果。
想想我们在处理图像时还能遇到的其他巧妙机会,真是令人兴奋。比如旋转。或者如何为某个角度制作动画conic-gradient……但是,在一个……之内border-image。布拉姆斯·范·达姆(Bramus Van Damme)出色地诠释了这个概念。
让我们通过创建一个充电指示器来分解它。我们将同时为角度和色调设置动画。我们可以从两个自定义属性开始:
@property --angle {
initial-value: 0deg;
inherits: false;
syntax: '<number>';
}
@property --hue {
initial-value: 0;
inherits: false;
syntax: '<angle>';
}
动画将在每次迭代时稍作停顿来更新角度和色调。
@keyframes load {
0%, 10% {
--angle: 0deg;
--hue: 0;
}
100% {
--angle: 360deg;
--hue: 100;
}
}
现在让我们将其作为border-image元素的应用。
.loader {
--charge: hsl(var(--hue), 80%, 50%);
border-image: conic-gradient(var(--charge) var(--angle), transparent calc(var(--angle) * 0.5deg)) 30;
animation: load 2s infinite ease-in-out;
}
很酷。
可惜的是,border-image它和 效果不太好border-radius。不过,我们可以在它后面加一个伪元素。把它和之前的数字动画技巧结合起来,我们就得到了一个完整的充电/加载动画。(没错,当达到 100% 时,它会发生变化。)
变形也很酷
动画变换的一个问题是特定部分之间的过渡。它经常会中断或看起来不正常。考虑一个经典的例子:一个球被抛出。我们希望它从 A 点移动到 B 点,同时模拟重力效应。
初步尝试可能看起来像这样
@keyframes throw {
0% {
transform: translate(-500%, 0);
}
50% {
transform: translate(0, -250%);
}
100% {
transform: translate(500%, 0);
}
}
但是,我们很快就会发现它并不像我们想要的那样。
以前,我们可能使用过包装元素,并单独为它们添加动画。但是,有了@property,我们可以为变换的各个值添加动画。而且所有这些都在一条时间轴上完成。让我们通过定义自定义属性,然后在球上设置变换来改变这种工作方式。
@property --x {
inherits: false;
initial-value: 0%;
syntax: '<percentage>';
}
@property --y {
inherits: false;
initial-value: 0%;
syntax: '<percentage>';
}
@property --rotate {
inherits: false;
initial-value: 0deg;
syntax: '<angle>';
}
.ball {
animation: throw 1s infinite alternate ease-in-out;
transform: translateX(var(--x)) translateY(var(--y)) rotate(var(--rotate));
}
现在对于我们的动画,我们可以针对关键帧组合我们想要的变换:
@keyframes throw {
0% {
--x: -500%;
--rotate: 0deg;
}
50% {
--y: -250%;
}
100% {
--x: 500%;
--rotate: 360deg;
}
}
结果如何?我们期望的曲线路径。而且,我们可以根据所使用的不同计时函数,让它看起来有所不同。我们可以将动画分成三部分,并使用不同的计时函数。这样,球的移动方式就会呈现出不同的效果。
考虑另一个例子,我们有一辆汽车,我们想让它绕着一个圆角的正方形行驶。
我们可以使用与处理球类似的方法:
@property --x {
inherits: false;
initial-value: -22.5;
syntax: '<number>';
}
@property --y {
inherits: false;
initial-value: 0;
syntax: '<number>';
}
@property --r {
inherits: false;
initial-value: 0deg;
syntax: '<angle>';
}
汽车transform正在使用以下计算来vmin保持响应:
.car {
transform: translate(calc(var(--x) * 1vmin), calc(var(--y) * 1vmin)) rotate(var(--r));
}
现在可以为汽车编写极其精确的逐帧旅程。我们可以从 的值开始--x。
@keyframes journey {
0%, 100% {
--x: -22.5;
}
25% {
--x: 0;
}
50% {
--x: 22.5;
}
75% {
--x: 0;
}
}
汽车在 x 轴上正确行驶。
然后我们在此基础上添加 y 轴的行程:
@keyframes journey {
0%, 100% {
--x: -22.5;
--y: 0;
}
25% {
--x: 0;
--y: -22.5;
}
50% {
--x: 22.5;
--y: 0;
}
75% {
--x: 0;
--y: 22.5;
}
}
嗯,这并不完全正确。
让我们增加一些额外的步骤来@keyframes让事情顺利进行:
@keyframes journey {
0%, 100% {
--x: -22.5;
--y: 0;
}
12.5% {
--x: -22.5;
--y: -22.5;
}
25% {
--x: 0;
--y: -22.5;
}
37.5% {
--y: -22.5;
--x: 22.5;
}
50% {
--x: 22.5;
--y: 0;
}
62.5% {
--x: 22.5;
--y: 22.5;
}
75% {
--x: 0;
--y: 22.5;
}
87.5% {
--x: -22.5;
--y: 22.5;
}
}
啊,现在好多了:
剩下的就是车辆的旋转了。我们在转弯处设置了 5% 的窗口。虽然不是很精确,但它确实展现了车辆的潜力:
@keyframes journey {
0% {
--x: -22.5;
--y: 0;
--r: 0deg;
}
10% {
--r: 0deg;
}
12.5% {
--x: -22.5;
--y: -22.5;
}
15% {
--r: 90deg;
}
25% {
--x: 0;
--y: -22.5;
}
35% {
--r: 90deg;
}
37.5% {
--y: -22.5;
--x: 22.5;
}
40% {
--r: 180deg;
}
50% {
--x: 22.5;
--y: 0;
}
60% {
--r: 180deg;
}
62.5% {
--x: 22.5;
--y: 22.5;
}
65% {
--r: 270deg;
}
75% {
--x: 0;
--y: 22.5;
}
85% {
--r: 270deg;
}
87.5% {
--x: -22.5;
--y: 22.5;
}
90% {
--r: 360deg;
}
100% {
--x: -22.5;
--y: 0;
--r: 360deg;
}
}
就这样,一辆汽车绕着弯曲的正方形行驶!无需包装器,无需复杂的数学运算。我们用自定义属性完成了这一切。
使用变量来驱动整个场景
到目前为止,我们已经看到了一些相当巧妙的@property可能性,但将所有这些结合起来,可以提升到一个新的高度。例如,我们只需几个自定义属性就可以为整个场景提供支持。
考虑以下 404 页面的概念。两个已注册的属性驱动不同的移动部分。我们有一个使用 裁剪的移动渐变-webkit-background-clip。阴影通过读取属性值来移动。我们摆动另一个元素来实现灯光效果。
就是这样!
想想我们能用 定义类型来做哪些事情,真是令人兴奋@property。通过为浏览器提供有关自定义属性的额外上下文,我们可以实现以前使用基本字符串无法实现的功能。
你对其他类型有什么想法?时间和分辨率可以带来有趣的过渡效果,虽然我承认我没能像我希望的那样实现。url也可以做得更简洁,比如像图片轮播那样在一系列来源之间过渡。只是集思广益而已!
希望这篇快速介绍@property能激发你的灵感,让你去尝试并制作出属于自己的精彩演示!我期待看到你的成果。欢迎在评论区分享!

浙公网安备 33010602011771号