早在 2012 年,Internet Explorer 10 发布了,除了其他功能之外,它终于支持了 CSS 渐变,并且能够仅使用 CSS 来对其进行动画!当时没有其他浏览器支持此功能,但我对未来充满希望。
遗憾的是,六年过去了,在这个领域并没有什么改变。Edge 支持使用 CSS 对渐变进行动画,就像 IE 10 当时那样,但其他浏览器都没有添加对该功能的支持。虽然使用动画 background-size
或 background-position
或叠加在顶部的伪元素的 opacity
或旋转 可以帮助我们实现很多酷炫的效果,但这些变通方法仍然有限。
有一些效果,我们无法在不添加大量额外元素或额外渐变的情况下重现,例如下面看到的“百叶窗效果”。
在 Edge 中,使用 关键帧 animation
可以实现以上效果。
html {
background: linear-gradient(90deg, #f90 0%, #444 0) 50%/ 5em;
animation: blinds 1s ease-in-out infinite alternate;
}
@keyframes blinds {
to {
background-image: linear-gradient(90deg, #f90 100%, #444 0);
}
}
如果这看起来 很 WET,我们可以 DRY 一点,稍微使用一下 Sass。
@function blinds($open: 0) {
@return linear-gradient(90deg, #f90 $open*100%, #444 0);
}
html {
background: blinds() 50%/ 5em;
animation: blinds 1s ease-in-out infinite alternate;
}
@keyframes blinds { to { background-image: blinds(1) } }
虽然我们使编写的代码以及以后需要编辑的内容更易于维护,但编译后的 CSS 中仍然存在重复,并且我们受到只能在具有相同单位的停止之间进行动画的限制。在 0%
和 100%
之间进行动画效果很好,但尝试使用 0
或 0px
而不是 0%
时,动画将不再发生。更不用说 Chrome 和 Firefox 直接从橙色变为灰色,根本没有停止位置 animation
了!
幸运的是,如今我们有一个更好的选择:CSS 变量!
从一开始,CSS 变量就不可动画,但如果我们使用它们的属性可动画,我们可以获得 transition
(但不能获得 animation
!)效果。例如,在 transform
函数中使用时,我们可以对 transform
属性进行 transition
。
让我们考虑一个示例,当选中复选框时,一个框会移动和压缩。在这个框上,我们设置了一个取决于初始值为 1
的因子 --f
的 transform
。
.box {
/* basic styles like dimensions and background */
--f: 1;
transform: translate(calc((1 - var(--f))*100vw)) scalex(var(--f));
}
当复选框 :checked
时,我们将 CSS 变量 --f
的值更改为 .5
。
:checked ~ .box { --f: .5 }
在 .box
上设置 transition
使其平滑地从一种状态过渡到另一种状态。
.box {
/* same styles as before */
transition: transform .3s ease-in;
}
请注意,由于 此错误,这在 Edge 的当前版本中并不真正有效。
然而,CSS 渐变是背景图像,它们只能在 Edge 和 IE 10+ 中进行动画。因此,虽然我们可以简化自身的工作并减少为过渡生成的 CSS 量(如以下代码所示),但在扩展支持方面我们仍然没有取得进展。
.blinds {
background: linear-gradient(90deg, #f90 var(--pos, 0%), #444 0) 50%/ 5em;
transition: .3s ease-in-out;
:checked ~ & { --pos: 100%; }
}
Houdini 来了,它允许我们注册自定义属性并对其进行动画。目前,这仅由 Blink 浏览器在 实验性 Web 平台功能 标志后面支持,但它仍然比仅仅 Edge 支持更进一步。
回到我们的示例,我们注册了 --pos
自定义属性。
CSS.registerProperty({
name: '--pos',
syntax: '<length-percentage>',
initialValue: '0%',
inherits: true
});
请注意, 表示它不仅接受长度和百分比值,还接受它们的
calc()
组合。相反, |
仅接受长度和百分比值,但不接受它们的 calc()
组合。
请注意,现在必须明确指定 inherits
,即使在规范的先前版本中它是可选的。
但是,这样做 在 Chrome 中没有任何区别,即使启用了标志,可能是因为在过渡的情况下,正在过渡的是其值取决于 CSS 变量的属性,而不是 CSS 变量本身。由于我们通常无法在 Chrome 中对两个背景图像进行过渡,因此这也失败了。
它确实在 Edge 中有效,但它在 Edge 中甚至在未注册 --pos
变量的情况下也能有效,因为 Edge 允许我们对渐变进行过渡。
在启用了标志的 Blink 浏览器中,有效的 是使用 animation
而不是 transition
。
html {
background: linear-gradient(90deg, #f90 var(--pos, 0%), #444 0) 50%/ 5em;
animation: blinds .85s ease-in-out infinite alternate;
}
@keyframes blinds { to { --pos: 100%; } }
但是,这现在在 Edge 中不再有效,因为虽然 Edge 可以对渐变背景进行动画,但它不能对自定义属性执行相同操作。
因此,我们需要对 Edge 采取另一种方法。这就是 @supports
派上用场的地方,因为我们只需检查是否支持 -ms-
前缀属性即可。
@function grad($pos: 100%) {
@return linear-gradient(90deg, #f90 $pos, #444 0);
}
html {
/* same as before */
@supports (-ms-user-select: none) {
background-image: grad(0%);
animation-name: blinds-alt;
}
}
@keyframes blinds-alt { to { background-image: grad() } }
停止位置不是我们唯一可以以此方式进行动画的属性。我们可以对渐变角度执行相同的操作。它背后的理念几乎相同,只是现在我们的 animation
不再是交替的了,而是使用 easeInOutBack
类型的计时函数。
@function grad($ang: 1turn) {
@return linear-gradient($ang, #f90 50%, #444 0);
}
html {
background: grad(var(--ang, 0deg));
animation: rot 2s cubic-bezier(.68, -.57, .26, 1.65) infinite;
@supports (-ms-user-select: none) {
background-image: grad(0turn);
animation-name: rot-alt;
}
}
@keyframes rot { to { --ang: 1turn; } }
@keyframes rot-alt { to { background-image: grad(); } }
请记住,就像停止位置一样,我们只能在 Edge 中以相同单位表示的渐变角度之间进行动画,因此使用 grad(0deg)
而不是 grad(0turn)
调用 Sass 函数不起作用。
当然,我们现在使用的 CSS 变量接受角度值而不是长度和百分比。
CSS.registerProperty({
name: '--ang',
syntax: '<angle>',
initialValue: '0deg',
inherits: true
});
以类似的方式,我们也可以对径向渐变进行动画。关于 CSS 变量方法真正令人惊奇的一点是,它允许我们对渐变的不同组件进行不同的动画,这在将渐变作为整体进行动画时是无法实现的,就像 Edge 所做的那样(这就是为什么以下演示在 Edge 中效果不佳的原因)。
假设我们有以下 radial-gradient()
。
$p: 9%;
html {
--x: #{$p};
--y: #{$p};
background: radial-gradient(circle at var(--x) var(--y), #f90, #444 $p);
}
我们注册了 --x
和 --y
变量。
CSS.registerProperty({
name: '--x',
syntax: '<length-percentage>',
initialValue: '0%',
inherits: true
});
CSS.registerProperty({
name: '--y',
syntax: '<length-percentage>',
initialValue: '0%',
inherits: true
});
然后我们添加动画。
html {
/* same as before */
animation: a 0s ease-in-out -2.3s alternate infinite;
animation-name: x, y;
animation-duration: 4.1s, 2.9s;
}
@keyframes x { to { --x: #{100% - $p} } }
@keyframes y { to { --y: #{100% - $p} } }
我们得到的结果如下所示。
我们可以使用这种在渐变函数中使用的不同自定义属性进行动画的技术,使我们初始示例中的百叶窗以相反的方向关闭,而不是后退。为此,我们引入了两个额外的 CSS 变量,--c0
和 --c1
。
$c: #f90 #444;
html {
--c0: #{nth($c, 1)};
--c1: #{nth($c, 2)};
background: linear-gradient(90deg, var(--c0) var(--pos, 0%), var(--c1) 0) 50%/ 5em;
}
我们注册了所有这些自定义属性。
CSS.registerProperty({
name: '--pos',
syntax: '<length-percentage>',
initialValue: '0%',
inherits: true
});
CSS.registerProperty({
name: '--c0',
syntax: '<color>',
initialValue: 'red',
inherits: true
});
/* same for --c1 */
我们对第一个停止位置 --pos
使用与之前相同的动画,此外,我们为另外两个变量引入了两个 steps()
动画,每次第一个 animation
(更改 --pos
值的动画)完成一次迭代时,它们的值就会切换。
$t: 1s;
html {
/* same as before */
animation: a 0s infinite;
animation-name: c0, pos, c1;
animation-duration: 2*$t, $t;
animation-timing-function: steps(1), ease-in-out;
}
@keyframes pos { to { --pos: 100%; } }
@keyframes c0 { 50% { --c0: #{nth($c, 2)} } }
@keyframes c1 { 50% { --c1: #{nth($c, 1)} } }
我们得到了以下结果。
我们也可以将其应用于 radial-gradient()
(只有 background
声明发生变化)。
background: radial-gradient(circle, var(--c0) var(--pos, 0%), var(--c1) 0);
完全相同的策略也适用于 conic-gradient()
。
background: conic-gradient(var(--c0) var(--pos, 0%), var(--c1) 0);
重复渐变也是一种选择,在径向情况下会产生涟漪般的效果。
$p: 2em;
html {
/* same as before */
background: repeating-radial-gradient(circle,
var(--c0) 0 var(--pos, 0px), var(--c1) 0 $p);
}
@keyframes pos { 90%, 100% { --pos: #{$p} } }
在圆锥形情况下,它会产生螺旋/射线效果。
$p: 5%;
html {
/* same as before */
background: repeating-conic-gradient(
var(--c0) 0 var(--pos, 0%), var(--c1) 0 $p);
}
@keyframes pos { 90%, 100% { --pos: #{$p} } }
我们还可以添加另一个 CSS 变量来使事物更有趣。
$n: 20;
html {
/* same as before */
background: radial-gradient(circle at var(--o, 50% 50%),
var(--c0) var(--pos, 0%), var(--c1) 0);
animation: a 0s infinite;
animation-name: c0, o, pos, c1;
animation-duration: 2*$t, $n*$t, $t;
animation-timing-function: steps(1), steps(1), ease-in-out;
}
@keyframes o {
@for $i from 0 to $n {
#{$i*100%/$n} { --o: #{random(100)*1%} #{random(100)*1%} }
}
}
我们需要注册这个变量,才能使整个过程正常工作。
CSS.registerProperty({
name: '--o',
syntax: '<length-percentage>',
initialValue: '50%',
inherits: true
});
就这样!结果如下所示。
我认为使用关键帧动画更改渐变的未来看起来相当酷。但与此同时,对于跨浏览器解决方案而言,JavaScript 方法 仍然是唯一有效的方法。
太棒了!!!!
为了更轻松/更“声明式”的基于 JS 的渐变动画,我编写了
animate-backgrounds
,它允许您连接到标准 jQuery 或 anime.js 动画,并像任何其他 CSS 属性一样对渐变进行动画。这里 是一个基于浏览器的工具,用于玩转自定义(通过 Sass 混合)和动画渐变模式,例如 Lea Verou 的 模式库 中的模式。想想“动画方格布”。