这一切都源于 @Keith Clark 最近在 Twitter发的帖子 。
CSS repeating-linear-gradients, do we need these? Can't the same thing be achieved with a linear-gradient and background-size?
这是一个很有意思的问题。
它有一个堂兄弟属性 repeating-radial-gradient
,肯定可以派上用场。他让我顺利完成了使用CSS的径向渐变制作一张黑胶唱片效果。
但是 repeating-linear-gradient
呢?
直到今年,我也觉得他们是无用的。但是在2月初,我制作一个 canvas
的案例 时, 自己掉进了一个坑里。最终在这个案例的基础上延伸出 近百个滑块的示例 ,而且都是使用 input[type="range"]
来完成的。
input[type="range"]
大部分样式都采用了斑马形条纹来填充,所以制作这些需要使用渐变,尤其是重复性渐变。因为仅使用正常的线性渐变是无法完成这样的效果。让我们一起来看看是为什么?
我们先用重复线性渐变来制作类似下图的纹理:
这个纹理是由黑色和蓝色的条纹组成,其中蓝色条纹的大小是黑色条纹的两倍。如果黑色条纹的宽度是 .25em
,使用 repeating-linear-gradient
这样写代码:
background: repeating-linear-gradient(135deg, #000, #000 .25em /* black stripe */, #0092b7 0, #0092b7 .75em /* blue stripe */ );
渐变从左上角( 135deg
)开始。如果你需要一个重复的线性渐变以及他们的角度是如何工作的,可以看看 这篇文章 所做的解释(忽略有关于旧的语法部分)。有一个黑色条纹从 0
到 .25em
线性渐变,接着一条蓝色条纹从 .25em
到 .75em
。( 蓝色条纹宽度 = .75em - .25em = .5em = 黑色条纹宽度的两倍 = .25em * 2
),可以看到效果:
现在我们尝试使用纯 linear-gradient
和 background-size
实现相同的条纹效果。我们将一个方形的背景加上带有角度 .25em
宽度黑色条纹和 .5em
宽度的蓝色条纹。一个正方形的对角线是直角边的 √2
(根号二)。如果我们知道对角线长度是 .25em + .50em = .75em
,然后 background-size
的尺寸是 .75em / √2
。代码如下所示:
background: linear-gradient(135deg, #000 .25em, #0092b7 0); background-size: .75em/sqrt(2) .75em/sqrt(2);
可是效果看起来并不是我们想要的:
如果我们把 .25em
的黑色条纹放在中间呢?让我们试试!
background: linear-gradient(135deg, #0092b7 calc(50% - .125em) /* blue corner */, #000 0, #000 calc(50% + .125em) /* black stripe */, #0092b7 0 /* blue corner */ ); background-size: .75em/sqrt(2) .75em/sqrt(2);
与我们想要的效果更接近了一些,但它仍然有点不好看:
如果我们将其放大,问题变得更显而易见,而我们需要做的就是填写那些黑色的角落。
这意味着在线性渐变的最开始和最后一个地方添加一个小的黑色条纹。这些将是最初的黑色条纹宽度一半( .25em
的一半就是 .125em
)。
background: linear-gradient(135deg, #000 .125em /* black corner */, #0092b7 0, #0092b7 calc(50% - .125em) /* blue stripe */, #000 0, #000 calc(50% + .125em) /* black stripe */, #0092b7 0, #0092b7 calc(100% - .125em) /* blue stripe */, #000 0 /* black corner */ ); background-size: .75em/sqrt(2) .75em/sqrt(2);
这看起来,稍为好多了,对吗?
现在的效果还不是我们想要的,因为蓝色条纹宽度太窄。这主要是因为对角线的计算不正确。从左上角到右下角,我们现在有一半的黑色条纹( .125em
),蓝色条纹( .5em
),黑色条纹( .25em
),另一个蓝色条纹( .5em
)和另一个半的黑色条纹( .125em
)。所以加起来对角线是 1.5em
。这意味着我们需要改变 background-size
的值为 1.5 em / √(2) 1.5 em /√(2)
:
这下完美了。视觉上的效果看上去和 repeating-linear-gradient
基本上相同。但在这种情况之下,我们需要太多的计算,太多的代码,而且四舍五入的计算还会存在误差。(如果我们将 font-size
值设置为 1.734em
将会解决上面示例存在的问题)。
如果在这种情况之下,你没有更好的方案来实现 repeating-linear-gradient
效果,是否考虑将渐变的角度值不是 135deg
,而是一个 120deg
呢?
使用 reeating-linear-gradient
并且将 135deg
换成 120deg
,可以得到几乎一样的纹理效果:
在Firefox和Edge/IE下的效果很完美,但在Chrome下看起来有点问题。我们可以让他们之间有 1px
间距来修复。
background: repeating-linear-gradient(120deg, #0092b7 0, #000 1px /* transition from previous blue stripe */, #000 .25em, #0092b7 calc(.25em + 1px) /* from black to blue */, #0092b7 .75em );
来看看Chrome下修复后的效果:
使用 linear-gradient
,只将角度 135deg
改成 120deg
。但这是不够的:
我们还需要修改 background-size
的尺寸。因为他们不是一个正方形。 X
轴长度是 1.5em*abs(cos(120deg))
, Y
轴长度是 1.5em*abs(sin(120deg))
。
Chrome下边缘看起来不好看,所以需要加上 1px
的小技巧来修复
使用 linear-gradient
替代 repeating-linear-gradient
并不太好,但它确实存在。
有什么 linear-gradient
和 background-size
不能替代 repeating-linear-gradient
的吗?
事实上是有的。
如果你注意到上面滑块的示例,上面介绍的条纹渐变只用在滑块的轨道之上。
IE和Firefox有专门的伪元素(分别是 -ms-fill-lower
和 -moz-progress
),而我们需要做的是在伪元素上创建背景。
但对于Webkit内核浏览器,唯一的办法,需要给滑块的轨道添加一个背景,然后随着滑块的调整来调整背景。这对于 linear-gradient
来说是一个大问题。我们无法通过 background-size
来控制。除此之外,只有 repeating-linear-gradient
才能做到。
如果我们设置 background-repeat
的值为数值,有点类似于 animation-iteration-count
类似。比如 background-repeat: .5 1.5
,一半的背景显示的是水平,而有一些是垂直方向显示。但我们是无法这样做到。或许有一些方法可以在一定的范围内实现呢?
唯一的方法,我们使用 linear-gradient
和 background-size
创建一个接近的东西,在一定的位置上对一个方向进行重复出现。
例如,如果我们希望他只覆盖元素水平方向的部分,我们必须设置 repeat-y
。第一个渐变背景在水平方向的 0*$size-x
开始,第二个是 1*$size-x
等。其中 $size-x
是 background-size
的 x
部分。下图说明了它是如何工作的:
但这只适用于滑块轨道部分有多少纹理在元素的 $size-x
被覆盖。如果我们想要控制的更好,或许需要更多的相同渐变来覆盖。
background-image: linear-gradient(135deg, #000 0.125em, #0092b7 0, #0092b7 calc(50% - .125em), #000 0, #000 calc(50% + .125em), #0092b7 0, #0092b7 calc(100% - .125em), #000 0 ), linear-gradient(135deg, #000 0.125em, #0092b7 0, #0092b7 calc(50% - .125em), #000 0, #000 calc(50% + .125em), #0092b7 0, #0092b7 calc(100% - .125em), #000 0 ) /* repeat as many times as needed */; ; background-position: 0*1.5em/sqrt(2) 0, 1*1.5em/sqrt(2) /* repeat for 2, 3, 4 and so on... */; background-repeat: repeat-y;
重复相同的渐变20次,这也太可怕了。我们可以使用Sass,担编译出来的CSS也让人难以接受
$grad: linear-gradient(135deg, #000 0.125em, #0092b7 0, #0092b7 calc(50% - .125em), #000 0, #000 calc(50% + .125em), #0092b7 0, #0092b7 calc(100% - .125em), #000 0 ); background-image: $grad, $grad /* repeat as many times as needed */; background-position: 0*1.5em/sqrt(2) 0, 1*1.5em/sqrt(2) /* repeat for 2, 3, 4 and so on... */; background-repeat: repeat-y;
下面的示例展示了这种行为的整个过程
总之,我的建议是,不要这样做!代码太可怕了,而且很容易被打断,因为在 linear-gradient
和 background-size
之间具有一定的差距。
而更好的方法是限制元素 repeating-linear-gradient
重复部分,不管重复部分的尺寸是多少,我们只通过 background-size
来设置,而且设置 background-repeat:no-repeat
让其不平铺。
这样通过改变 background-size
的 x
轴的值来控制滑块进度的条纹值。
同样的想法有助于我们更好的控制整个滑块的进度条。假设我们希望滑块在整个进度条的中间位置,只需要将 background-siz
的 x
值设置为 50%
。这是一个很简单而且很有创意的想法:
width: 19em; background: repeating-linear-gradient(90deg, #c8c8c8, #c8c8c8 0.125em /* lines */, transparent 0.125em, transparent 1.25em /* space between */ ) 50% no-repeat; background-size: 12.625em /* = 10*1.25em + .125em */ .5em;
结果看起来像这样:
为了主线和次线有区别,我们需要添加第二个重复的渐变,第二个重复渐变的高度是第一个的两倍:
background: repeating-linear-gradient(90deg, /* major */ #c8c8c8, #c8c8c8 .125em /* lines */, transparent , transparent 2.5em /* space between */ ) 50% no-repeat, repeating-linear-gradient(90deg, /* minor */ #c8c8c8, #c8c8c8 .125em /* lines */, transparent , transparent 1.25em /* space between */ ) 50% no-repeat; background-size: 12.625em 1em, 12.625em .5em;
其结果看起来像这样:
我们需要将 background-position
在 Y
轴按顶部或底部对齐。例如,我们可以这样做:
background-position: right 50% bottom 2.25em;
这正像我们开始说的第一个示例:
下图滑块的示例完全可以按这种方式来制作
有两个相同的 repeating-linear-gradient
,第一个水平重复部分定位在 25%
,而第二个是 75%
。
background: repeating-linear-gradient(90deg, #6b4c1e, #6b4c1e 1px, #e1ba75 0, #e1ba75 2px, transparent 0, transparent 4px ) 25% 50% /* left */, repeating-linear-gradient(90deg, #6b4c1e, #6b4c1e 1px, #e1ba75 0, #e1ba75 2px, transparent 0, transparent 4px ) 75% 50% /* right */ orange; background-repeat: no-repeat; background-size: 10px 10px;
但这是一个 难维护 的样式代码,我们可以使用Sass来维护:
$grip: repeating-linear-gradient(90deg, #6b4c1e, #6b4c1e 1px, #e1ba75 0, #e1ba75 2px, transparent 0, transparent 4px); /* we use this twice */ background: $grip 25% 50% /* left */, $grip 75% 50% /* right */ orange; background-repeat: no-repeat; background-size: 10px 10px;
其效果如下:
重复渐变曾是 令人头痛 的一个东东,我们仍然遇到很多 渐变的问题 ,尤其是大面积使用的之时。他们在 OS X的Webkit浏览器和Firefox浏览器 渲染时宽度总是不一样(尽管在Windows系统上的Firefox下很少会失败)。
但事情已经进步了很多,我相信日后会得到更多的改善。甚至在年初的时候,我们可以看到Chrome41和Chrome Canary43版本 都存在很大的差异 。
重复渐变现在可以安全使用,我觉得它应该得到更多的关注,因为重复渐变让我们码代码变得更容易,而且做出来的效果更酷。
本文根据 @ANA TUDOR 的《 Why Do We Have repeating-linear-gradient
Anyway? 》所译,整个译文带有我们自己的理解与思想,如果译得不好或有不对之处还请同行朋友指点。如需转载此译文,需注明英文出处: https://css-tricks.com/why-do-we-have-repeating-linear-gradient-anyway/ 。
常用昵称“大漠”,W3CPlus, Sass中国 创始人,目前就职于手淘。中国Drupal社区核心成员之一。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《 图解CSS3:核心技术与案例实战 》。