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

Weird effect when applying transparent border over an element with a gradient background

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

请注意元素的左侧和右侧没有正确的颜色(它们以某种方式切换)并且很奇怪 平坦

HTML

<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);
}

为什么 这会在元素的左侧和右侧显示出奇怪的效果,我能做些什么

这里是fiddle:http://jsfiddle.net/fzndodgx/3/

背景在边框下重复出现。背景仅在元素的 "body" 运行,边框下方是展开并开始重复。

参见 this 示例,边框为 no-repeat

更新

玩弄背景 position & size 可以通过扩展背景然后调整其位置来提供帮助。

检查 this fiddle

或查看片段:

.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 不占用任何 space,您需要相应地增加填充。

padding-boxborder-box 的说明:

演示 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>

小心 这不适用于 IE10 或更低版本:http://caniuse.com/#feat=border-image

根据默认的 CSS 框模型行为,渐变在填充框内开始,因此最开始和最结束的颜色出现直到渐变任一侧无穷大是有意义的,边界将是。

这个梯度也是一样的(NSFW):

它以起始值(紫色)向左无限延伸,以结束值(橙色)向右无限延伸。它可以用这个梯度无限向上继续,它确实如此。

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

解决方案

解决此问题的最简单方法是将 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>


问题中提到的行为的原因

以下是相关的 background 属性,它们决定了这种情况下背景渐变的显示方式:

  • background-origin - 默认值为 padding-box。这意味着背景实际上是相对于填充框定位的,因此从 border.
  • 之后开始
  • background-repeat - 默认值为 repeat。这意味着图像应尽可能多地重复以覆盖整个背景绘画区域。
  • background-clip - 默认值为 border-box。这意味着图像也应该出现在框的边框占据的区域下方。

现在结合所有三个我们可以看到边框必须尽可能多地重复才能出现在边框下方,并且它的起始位置在框的边框之后。这意味着背景必须以循环方式重复以填满左侧边框下方的区域。因此,左边框的颜色与渐变的右端相同,反之亦然。

通过将其更改为 border-box,我们使背景相对于边框框定位。此设置 还会影响背景图像的大小 ,其原因将在下面详细描述。


为什么 box-sizing: border-box 不起作用?

box-sizing 设置为 border-box 不会带来任何变化,因为 属性 仅影响框的大小。它对以下完全没有影响:

  • 渐变图的大小(实际计算逻辑如下所述)
  • 渐变图像的起点(或位置)
  • 背景图片重复

渐变的大小是怎么计算的?

根据W3C spec,以下是未提供明确尺寸时图像尺寸的计算方式(默认值为auto)。

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

注意它是如何谈论图像的大小而不是框的大小的。本质上,无论框的大小如何,当图像本身没有固有高度(which CSS gradients don't have unlike images)时,背景图像的大小将根据关键字 contain 的定义计算。

contain的定义如下:

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-origin属性定义下):

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

因此,当图像没有固有高度(在本例中也没有 background-size)时,图像的大小将等于 background-origin 的值(在我们的案例是 padding-box)。

这就是为什么即使将 box-sizing 设置为 border-box 也没有效果。

注:文中的重点都是我的


如果您将 background-size 明确设置为方框的大小,您会注意到问题是如何在右侧而非左侧得到解决的。这是因为现在图片已经足够大了,不会在右边框下重复,但它的起点还在左边框之后。

.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>

或者您可以调整背景大小和位置:将 20px 添加到背景大小并将其定位在 -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);
}

会产生