在有渐变背景的元素上应用透明边框时的奇怪效果

当在以 linear-gradient为背景的元素上应用透明边框时,我得到了一个奇怪的效果。

enter image description here

注意元素的左右两边没有正确的颜色(它们在某种程度上被切换了) ,而且奇怪地是 平的

超文本标示语言

<div class="colors">
</div>

CSS

.colors {
width: 100px;
border: 10px solid rgba(0,0,0,0.2);
height: 50px;
background: linear-gradient(to right,
#78C5D6,
#459BA8,
#79C267,
#C5D647,
#F5D63D,
#F08B33,
#E868A2,
#BE61A5);
}

为什么 在元素的左侧和右侧显示出一种奇怪的效果,而 什么可以对此做些什么呢?

这是小提琴: http://jsfiddle.net/fzndodgx/3/

5238 次浏览

背景在边框下重复出现。背景只在元素的“主体”中运行,边框下面是一个扩展,重复开始发生。

请参见边框上带有 no-repeat这个示例。

更新

玩背景 positionsize可以帮助扩大背景,然后调整它的位置。

看看 这把小提琴

或者参见片段:

.colors {
padding: 10px;
width: 100px;
border: 10px solid rgba(0, 0, 0, 0.2);
height: 50px;
background: linear-gradient(to right, #78C5D6, #459BA8, #79C267, #C5D647, #F5D63D, #F08B33, #E868A2, #BE61A5);
background-size: 117%;
background-position-x: 130px;
}
<div class="colors"></div>

这是因为 gradient的起点和终点位于 padding-box的边缘,而 border是在 padding-box之外呈现的。因此,边界看起来很滑稽,因为 backgroundpadding-box之外的每一边重复,以覆盖 border-box

box-shadow:inset是在 padding-box内部渲染的(就像背景一样) ,并且在视觉上给出了与 border相同的效果,所以你可以尝试用它来代替 border:

box-shadow: inset 0 0 0 10px rgba(0,0,0,0.2);
padding: 10px;

因为 box-shadow不占用任何空间,所以需要相应地增加填充。

padding-boxborder-box的插图: enter image description here

演示 http://jsfiddle.net/ilpo/fzndodgx/5/

如果你不想使用 box-shadow,你可以使用 border-image并调整渐变的颜色: http://jsfiddle.net/9pcuj8bw/5/

.colors {
width:100px;
height: 50px;
background: linear-gradient(to right,
#78C5D6,
#459BA8,
#79C267,
#C5D647,
#F5D63D,
#F08B33,
#E868A2,
#BE61A5) no-repeat;
border: 10px solid;
border-image: linear-gradient(to right,
#0bc3b8,
#068e8c,
#f8c617,
#ea5f24,
#b2492c) 1;
}
<div class="colors"></div>

Careful this works not on IE10 or lower: http://caniuse.com/#feat=border-image

渐变开始在填充框内根据默认的 CSS 框模型行为,所以它是有意义的,非常开始和非常结束的颜色出现,直到无穷远的任何一边的渐变,在那里的边界。

这个梯度(NSFW)也是如此:

A GOD AWFUL EYE SORE

它在左边的起始值(紫色)中无限延续,在右边的结束值(橙色)中无限延续。它可以通过这个梯度无限向上运动,就是这样。

这就是我对为什么会出现这种情况的理解,一种解决方案是使用不同的盒子模型。

解决方案

解决此问题的最简单方法是将 background-origin属性的值设置为 border-box

.colors {
width: 100px;
border: 10px solid rgba(0, 0, 0, 0.2);
height: 50px;
background: linear-gradient(to right, #78C5D6, #459BA8, #79C267, #C5D647, #F5D63D, #F08B33, #E868A2, #BE61A5);
background-origin: border-box;
}
<div class="colors"></div>


Reason for the behavior mentioned in question

The following are the relevant background properties that determine the way the background gradient is displayed for this case:

  • background-origin - The default value is padding-box. It means that the background is actually positioned with respect to the padding box and so starts from after the border.
  • background-repeat - The default value for this is repeat. It means that the image should be repeated as much as needed to cover the entire background painting area.
  • background-clip - The default value for this is border-box. It means that the image should be present under the area occupied by the borders of the box also.

Now combining all three we can see that the border must be repeated as much as possible for it to be present even under the borders and that it's starting position is after the border of the box. This implies that the background must be repeated in a cyclic manner so as to fill up the area under the border on the left side. Because of this the left border has the color as the right end of the gradient and vice-versa.

By changing it to border-box, we are making the background get positioned with respect to the border box. This setting also has an effect on the size of the background image and the reason for it is described below in detail.


Why does box-sizing: border-box not work?

Setting box-sizing as border-box does not bring about any change because that property affects only the size of the box. It has absolutely no effect on the following:

  • The size of the gradient image (actual calculation logic is described below)
  • The starting point (or position) of the gradient image
  • The repetition of the background image

How is the size of the gradient calculated?

As per W3C spec, the below is how the image's dimensions are calculated when no explicit size is provided (default value is auto).

If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for ‘contain’

Note how it talks about the size of the image and not that of the box. In essence, irrespective of the size of the box, the size of the background image would be calculated based on the definition for keyword contain when the image itself has no intrinsic height (which CSS gradients don't have unlike images).

The definition for contain is as follows:

Scale the image, while preserving its intrinsic aspect ratio (if any), to the largest size such that both its width and its height can fit inside the background positioning area.

Background positioning area is defined as follows (under background-origin property definition):

For elements rendered as a single box, specifies the background positioning area

Thus, when the image has no intrinsic height (and in this case no background-size also), the size of the image would be equal to that of background-origin's value (which in our case is padding-box).

This is why even setting the box-sizing as border-box has no effect.

Note: emphasis within the quoted texts are all mine


If you explicitly set the background-size as the size of the box, you would notice how the issue is resolved on the right side but not on the left side. This is because now the image is large enough to not repeat under the right border but its starting point is still after the left border.

.colors {
width: 100px;
border: 10px solid rgba(0, 0, 0, 0.2);
height: 50px;
background: linear-gradient(to right, #78C5D6, #459BA8, #79C267, #C5D647, #F5D63D, #F08B33, #E868A2, #BE61A5);
background-size: 110px 60px;
}
.colors-2 {
width: 100px;
border: 10px solid rgba(0, 0, 0, 0.2);
height: 50px;
background: linear-gradient(to right, #78C5D6, #459BA8, #79C267, #C5D647, #F5D63D, #F08B33, #E868A2, #BE61A5);
box-sizing: border-box;
background-size: 100px 50px;
}
<div class="colors">
</div>
<div class="colors-2">
</div>

background-origin属性的默认值是 padding-box,这意味着背景的位置和大小相对于填充框。

由于 background-clip属性默认为 border-box,所以边框下面的背景也是 延伸; 它只是在边框下面重复自己。这就是为什么你会看到背景的右边在左边框之下,反之亦然。

所以,只要改变原点:

.colors {
width: 100px;
border: 10px solid rgba(0, 0, 0, 0.2);
height: 50px;
background: linear-gradient(to right, #78C5D6, #459BA8, #79C267, #C5D647, #F5D63D, #F08B33, #E868A2, #BE61A5);
background-origin: border-box;
}
<div class="colors"></div>

Alternately you can play with background size and position: add 20px to the background size and position it at -10px -10px:

.colors {
width: 100px;
border: 10px solid rgba(0, 0, 0, 0.2);
height: 50px;
background: linear-gradient(to right, #78C5D6, #459BA8, #79C267, #C5D647, #F5D63D, #F08B33, #E868A2, #BE61A5);
background-position: -10px -10px;
background-size: calc(100% + 20px) calc(100% + 20px);
}
<div class="colors"></div>

其他的答案已经显示了如何解决这个问题,但我认为我只需要指出,如果你增加 border-width,很明显,背景实际上是重复的。

.colors {
width: 100px;
border: 100px solid rgba(0,0,0,0.2);
height: 50px;
background: linear-gradient(to right,
#78C5D6,
#459BA8,
#79C267,
#C5D647,
#F5D63D,
#F08B33,
#E868A2,
#BE61A5);
}

会产生

enter image description here