为什么要使用repeating-linear-gradient

发布于 大漠

这一切都源于@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"]来完成的。

slider

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-gradientbackground-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-gradientbackground-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-gradientbackground-size创建一个接近的东西,在一定的位置上对一个方向进行重复出现。

例如,如果我们希望他只覆盖元素水平方向的部分,我们必须设置repeat-y。第一个渐变背景在水平方向的0*$size-x开始,第二个是1*$size-x等。其中$size-xbackground-sizex部分。下图说明了它是如何工作的:

纹理

但这只适用于滑块轨道部分有多少纹理在元素的$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-gradientbackground-size之间具有一定的差距。

而更好的方法是限制元素repeating-linear-gradient重复部分,不管重复部分的尺寸是多少,我们只通过background-size来设置,而且设置background-repeat:no-repeat让其不平铺。

这样通过改变background-sizex轴的值来控制滑块进度的条纹值。

同样的想法有助于我们更好的控制整个滑块的进度条。假设我们希望滑块在整个进度条的中间位置,只需要将background-sizx值设置为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-positionY轴按顶部或底部对齐。例如,我们可以这样做:

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/Material Matters: The Evolution of Air in Sneakers