【问题标题】:Using percentage values with background-position on a linear-gradient在线性梯度上使用具有背景位置的百分比值
【发布时间】:2019-01-14 19:10:48
【问题描述】:

有没有办法让background-position 取百分比值?目前,我的按钮仅适用于 widthbackground-position 的显式值。

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;
}

.button {
  display: flex;
  justify-content: center;
  align-items: center;
  text-decoration: none;
  color: white;
  font-weight: bold;
  width: 350px;
  height: 50px;
  border: 1px solid green;
  transition: background 0.5s;
  background-repeat: no-repeat;
  background-image: linear-gradient(to left, #2484c6, #1995c8 51%, #00bbce), linear-gradient(to right, #2484c6 0%, #1995c8 51%, #00bbce 76%);
}
.button-pixel {
  background-position: -350px 0px, 0px 0px;
}
.button-pixel:hover {
  background-position: 0px 0px, 350px 0px;
}
.button-percentage {
  background-position: -100% 0px, 0px 0px;
}
.button-percentage:hover {
  background-position: 0% 0px, 100% 0px;
}
<a href="#" class="button button-pixel">In Pixel</a>
<a href="#" class="button button-percentage">In Percentage</a>

【问题讨论】:

  • @TemaniAfif 答案如此广泛,它是否满足百分比悬停背景位置的解决方案?
  • @Temani Afif 请看看答案

标签: css background background-image linear-gradients background-position


【解决方案1】:

TL;DR

当使用渐变作为背景时,与background-position 一起使用的所有百分比值都是等效的,因此您不会看到任何差异。你需要指定一个不同于容器大小的background-size

body {
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;
  min-height:90vh;
}

.button {
  text-decoration: none;
  color: white;
  font-weight: bold;
  width: 350px;
  height: 50px;
  text-align:center;
  transition: background 0.5s;
  background-repeat: no-repeat;
  background-image: 
    linear-gradient(to left, #2484c6, #1995c8 51%, #00bbce), 
    linear-gradient(to right, #2484c6 0%, #1995c8 51%, #00bbce 76%);
  background-size:200% 100%; /*Changed this*/
}
.button-pixel {
  background-position: -350px 0px, 0px 0px;
}
.button-pixel:hover {
  background-position: 0px 0px, 350px 0px;
}
.button-percentage {
  background-position: 100% 0px, 0px 0px;
}
.button-percentage:hover {
  background-position: 0% 0px, 100% 0px;
}
<a href="#" class="button button-pixel">Pixel</a>
<a href="#" class="button button-percentage">Percentage</a>

背景位置如何工作?

让我们用一张经典的图片来解释background-position的工作原理。

使用像素值时,无论大小如何,参考都是图像的上/左角。这就像使用带有定位元素的top/left

.b {
  width:200px;
  height:200px;
  background:url(https://picsum.photos/100/100?image=1069) no-repeat;
  border:1px solid;
  background-position:0 0;
  position:relative;
  animation:back 5s infinite linear alternate;
}
.b:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:10px;
  height:10px;
  background:red;
  animation:change 5s infinite linear alternate;
}

@keyframes back{to{background-position:200px 200px;}}
@keyframes change{to{top:200px; left:200px;}}
&lt;div class="b"&gt;&lt;/div&gt;

使用百分比值时,参考与使用像素值时不同;它不再是左上角:

.b {
  width:200px;
  height:200px;
  background:url(https://picsum.photos/100/100?image=1069) no-repeat;
  border:1px solid;
  background-position:0% 0%;
  position:relative;
  animation:back 3s infinite linear alternate;
}
.b:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:10px;
  height:10px;
  background:red;
  animation:change 3s infinite linear alternate;
}
@keyframes back{to{background-position:100% 100%;}}
@keyframes change{to{top:200px; left:200px;}}
&lt;div class="b"&gt;&lt;/div&gt;

在这种情况下,我们需要考虑两个参数:容器的大小和图像的大小。这是它如何工作的说明(我取了一个background-position 等于30% 30%):

首先,我们考虑图像以找到我们将用于放置图像的参考点。考虑到图像的大小(如用绿线定义的那样),它是图像内部的点,它位于从左上角/左上角30% 30% 处。然后,考虑到容器的大小,我们将该点在容器内从上/左角放在30% 30%

从这个逻辑中,我们可以清楚地识别出一些琐碎的案例,比如

                50% 50%(中)                 100% 100%(右下)                 100% 0%(右上)

现在很明显,如果图像的大小等于容器的大小,那么什么都不会发生,因为所有位置都是相等的。图像的顶部/左侧已经在容器的顶部/左侧(0% 0%),中心已经在中心(50% 50%)等等。

.b {
  width:200px;
  height:200px;
  background:url(https://picsum.photos/200/200?image=1069) no-repeat;
  border:1px solid;
  background-position:0% 0%;
  position:relative;
  animation:back 5s infinite linear alternate;
}
.b:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:10px;
  height:10px;
  background:red;
  animation:change 5s infinite linear alternate;
}
@keyframes back{to{background-position:100% 100%;}}
@keyframes change{to{top:100%; left:100%;}}
&lt;div class="b"&gt;&lt;/div&gt;

上述逻辑在应用于渐变时是相同的,因为渐变被视为图像,默认情况下,如果您不指定background-size,渐变的大小将是其容器的大小,这与使用图像时不同.

如果我们参考background-sizespecification,我们可以看到您的问题是如何出现的:

如果两个值都是自动的,则 的固有宽度和/或高度 应使用图片,如果有,缺失维度(如果有)的行为 如上所述自动。如果图像既没有内在 宽度也不是固有高度,其大小由 contain 确定。

还有:

包含

缩放图像,同时保留其固有纵横比 (如果有的话),最大尺寸,使其宽度和高度 可以放在背景定位区域内。

还有:

位图图像(例如 JPG)总是具有固有的尺寸和 比例。

CSS &lt;gradient&gt;s 没有固有尺寸固有比例ref

图像始终具有内在价值,因此在大多数情况下,它的大小与其容器的大小不同,因此带有百分比单位的background-position 会产生影响。但是渐变没有内在价值,因此渐变的尺寸将等于其容器的大小,并且具有百分比值的background-position 永远不会起作用,除非我们指定一个不同于其容器尺寸的background-size



更深入

我们在上面的例子中看到background-size在使用0%100%之间的值时是如何工作的,但是使用负值或大于100%的值呢?逻辑是一样的,但是找参考点会比较麻烦。

负值 (

假设我们想在-50% 0 放置一个背景。在这种情况下,参考点将位于图像之外。这是一个例子:

.b {
  width:200px;
  height:200px;
  border:1px solid;
  background:url(https://picsum.photos/100/100?image=1069) -50% 0 no-repeat;
}
&lt;div class="b"&gt;&lt;/div&gt;

正如我们在插图中看到的,我们首先考虑图像的-50%,即-50px,以便定义我们的参考点(即距离图像左边缘-50px)。然后,考虑到容器的大小(距容器左边缘-100px),我们将该点放置在-50%。然后我们绘制图像,就得到了上面的结果。只有100px 的图像可见。

我们可能还注意到,当图像的大小小于容器的大小时,负百分比值与负固定值的行为相同(两者都会将图像向左移动)。在这种情况下,-50% 0-50px 0 相同。

.b {
  width:200px;
  height:200px;
  display:inline-block;
  border:1px solid;
  background:url(https://picsum.photos/100/100?image=1069) -50% 0/100px 100px no-repeat;
}
.a{
  background:url(https://picsum.photos/100/100?image=1069) -50px 0/100px 100px no-repeat;
}
<div class="b">
</div>
<div class="b a">
</div>

例如,如果我们将图像大小增加到150px 150px-50% 0 将与-25px 0 相同。

当我们使尺寸大于容器时,负值将开始将图像向右移动(就像正像素值一样),这是合乎逻辑的,因为图像的50% 将增加,而容器的50% 将保持不变。

如果我们考虑前面的插图,这就像增加顶部的绿线,直到它比底部的大。所以只有标志不足以知道背景图像将如何移动;我们还需要考虑尺寸。

.b{
  width:200px;
  height:200px;
  border:1px solid;
  background:url(https://picsum.photos/300/300?image=1069) -50% 0/50px 50px no-repeat;
  animation:change 2s linear infinite alternate; 
}
@keyframes change{
  to {background-size:300px 300px}
}
<div class="b">
</div>

对于渐变,逻辑上也会发生同样的情况:

.b {
  width:200px;
  height:200px;
  border:1px solid;
  background:linear-gradient(to right,red,blue) -50% 0/50px 150px no-repeat;
  animation:change 2s linear infinite alternate; 
}
@keyframes change{
  to   {background-size:300px 150px}
}
<div class="b">
</div>

大值 (> 100%)

与前面相同的逻辑:如果我们在150% 0 定义背景,那么我们从左边缘(或从右边缘50%)考虑我们的参考点50%,然后我们将它放置在150%容器的左边缘。

.b {
  width:200px;
  height:200px;
  border:1px solid;
  background:url(https://picsum.photos/100/100?image=1069) 150% 0/100px 100px no-repeat;
}
&lt;div class="b"&gt;&lt;/div&gt;

在这种情况下,150% 0 等价于 150px 0,如果我们开始增加背景大小,我们将获得与之前演示的相同的行为:

.b {
  width:200px;
  height:200px;
  border:1px solid;
  background:url(https://picsum.photos/300/300?image=1069) 150% 0/50px 50px no-repeat;
  animation:change 2s infinite linear alternate;
}
@keyframes change {
  to {background-size:300px 300px}
}
&lt;div class="b"&gt;&lt;/div&gt;

特殊情况

使用[0% 100%] 范围之外的值可以让我们隐藏背景图像,但是我们如何找到确切的值以完全隐藏图像呢?

让我们考虑下图:

我们的图像宽度为Ws,容器宽度为Wp,我们需要找到p的值。从图中我们可以得到如下公式:

p * Wp = p * Ws + Ws   <=>   p = Ws/(Wp - Ws)   where p in [0,1]

如果容器大小是200px,图像是100px,那么p1 所以100%(我们当然添加了负号,它是-100%)。

如果我们考虑使用background-size 而不是固定值的百分比值,我们可以使这个更通用。假设background-sizeS%。然后我们将有Ws = Wp * s (s in [0,1] and S=s*100%),公式将是

p = Ws/(Wp - Ws)   <=>   p = s / (1 - s)

加上负号就是p = s / (s - 1)

现在如果我们想隐藏右侧的图像,我们在右侧执行相同的逻辑(我们考虑上图的镜像),但是由于我们总是会考虑左侧边缘来找到我们需要的百分比添加100%

p'% 的新百分比为100% + p%,公式将为p' = 1 + p --&gt; p' = 1 + s / (1 - s) = 1 / (1 - s)

这里有一个动画来说明上面的计算:

.b {
  width:200px;
  height:50px;
  margin:5px;
  border:1px solid;
  background:linear-gradient(to right,red,blue) no-repeat;
  background-size:calc(var(--s) * 100%) 100%;
  animation:change 4s linear infinite alternate;
}
@keyframes  change{
   from { /*Hide on the left*/
     background-position:calc(var(--s)/(var(--s) - 1) * 100%)
   }
   to { /*Hide on the right*/
     background-position:calc(1/(1 - var(--s)) * 100%)
   }
}
<div class="b" style="--s:0.5">
</div>
<div class="b" style="--s:0.8">
</div>
<div class="b" style="--s:2">
</div>

让我们计算一些值:

s=0.5 时,我们有一个background-size 等于50%,百分比值将从-100%200%。在这种情况下,我们以负值开始,以正值结束,因为图像的大小小于容器的大小。如果我们考虑最后一种情况(s=2),background-size 等于200%,百分比值将从200%-100%。我们以正值开始,以负值结束,因为图像的大小大于容器的大小

这证实了我们之前所说的:要将图像向左移动,如果尺寸小,我们需要负值,但如果尺寸大,我们需要正值(右侧也是如此)。


像素值与百分比值的关系

让我们定义一种基于像素值计算百分比值的方法,反之亦然(即在两者之间转换的公式)。为此,我们只需要考虑参考点

当使用像素值时,我们会考虑蓝线,我们会有background-position:X Y

当使用百分比值时,我们将考虑绿线,我们将拥有background-position:Px Py

公式如下:X + Px * Ws = Px * Wp 其中Ws 是图像的宽度,Wp 是容器的宽度(考虑高度的 Y 轴公式相同)。

我们将拥有X = Py * (Wp - Ws)。从这个公式中,我们可以验证前面解释的两点:

  • Wp = Ws时,公式不再有效,这证实了当图片大小与容器相同时,百分比值无效;因此像素值和百分比值之间没有关系。
  • XPxWp &gt; Ws 时符号相同,在 Wp &lt; Ws 时符号相反。这证实了百分比值的行为取决于图像的大小。

如果我们考虑background-size的百分比值,我们也可以用不同的方式表达公式。我们将有X = Px * Wp * (1-s)

这里有一个动画来说明上面的计算:

.b {
  width:200px;
  height:50px;
  margin:5px;
  border:1px solid;
  background:linear-gradient(to right,red,blue) no-repeat;
  background-size:calc(var(--s) * 100%) 100%;
  animation:percentage 2s linear infinite alternate;
}
.box.a {
  animation-name:pixel; 
}
@keyframes  percentage{
   from { background-position:-50%;}
   to { background-position:150%;}
}
@keyframes  pixel{
   from { background-position:calc(-0.5 * 200px * (1 - var(--s))) }
   to {  background-position:calc(1.5 * 200px * (1 - var(--s)));}
}
<div class="b" style="--s:0.5">
</div>
<div class="b a" style="--s:0.5">
</div>

<div class="b" style="--s:2">
</div>
<div class="b a" style="--s:2">
</div>

更改参考

在上述计算中,我们始终考虑图像的左上角和容器,以便将我们的逻辑应用于像素值或百分比值。可以通过向background-position 添加更多值来更改此引用。

默认情况下,background-position: X Y 等效于 background-position: left X top Y(位置在 X 来自 leftY 来自 top)。通过调整top 和/或left,我们可以更改参考以及图像的放置方式。以下是一些示例:

.b {
  width:150px;
  height:150px;
  display:inline-block;
  background:url(https://picsum.photos/70/70?image=1069) no-repeat;
  border:1px solid;
  position:relative;
}

body {
 margin:0;
}
<div class="b"></div>
<div class="b" style="background-position:left 0 bottom 0"></div>
<div class="b" style="background-position:right 0 bottom 0"></div>
<div class="b" style="background-position:right 0 top 0"></div>


<div class="b" style="background-position:right 10% top 30%"></div>
<div class="b" style="background-position:right 10% bottom 30%"></div>
<div class="b" style="background-position:right 10px top 20px"></div>
<div class="b" style="background-position:left 50% bottom 20px"></div>

很明显,对于X 值,我们只能使用leftright(水平位置),而对于Y 值,我们只能使用bottomtop(垂直位置) )。通过所有不同的组合,我们可以从逻辑上获得 4 个不同的角。

此功能对于优化某些计算也很有用。在 特殊情况 部分的示例中,我们进行了第一次计算以隐藏左侧的图像,然后进行另一个计算以将其隐藏在右侧。如果我们考虑更改参考,我们只需要进行一次计算。我们采用左侧的公式,右侧使用相同的公式。

这是新版本:

.b {
  width:200px;
  height:50px;
  margin:5px;
  border:1px solid;
  background:linear-gradient(to right,red,blue) no-repeat;
  background-size:calc(var(--s) * 100%) 100%;
  animation:change 4s linear infinite alternate;
}
@keyframes  change{
   from { 
     background-position:left  calc(var(--s)/(var(--s) - 1) * 100%) top 0
   }
   to { 
     background-position:right calc(var(--s)/(var(--s) - 1) * 100%) top 0
   }
}
<div class="b" style="--s:0.5">
</div>
<div class="b" style="--s:0.8">
</div>
<div class="b" style="--s:2">
</div>

对于s=0.5,我们将不再从-100%200% 进行动画处理,而是从left -100%right -100%

这是另一个使用像素值的示例,我们可以清楚地看到在更改参考时处理计算是多么容易:

.b {
  width:200px;
  height:200px;
  background:url(https://picsum.photos/100/100?image=1069) no-repeat;
  border:1px solid;
  background-repeat:no-repeat;
  animation:change 2s infinite linear;
}


@keyframes change{
  0%{background-position:left 20px top 20px;}
  25%{background-position:right 20px top 20px;}
  50%{background-position:right 20px bottom 20px;}
  75%{background-position:left 20px bottom 20px;}
  100%{background-position:left 20px top 20px;}
}
&lt;div class="b"&gt;&lt;/div&gt;

通过保持相同的引用来实现相同的动画会很棘手。因此,如果我们想做一个对称动画,我们会在一侧执行我们的逻辑,并通过更改引用在另一侧使用相同的逻辑。


结合像素值和百分比值

我们可以使用calc() 来进行一些涉及不同单位的复杂计算。例如,我们可以写width:calc(100px + 20% + 12em),浏览器将根据每个单元的工作方式计算计算值,并以像素值结束(对于这种情况)。

background-position 呢?如果我们写calc(50% + 50px),这将被评估为百分比值还是像素值?将像素值转换为百分比还是相反?

结果不会转换为像素值或百分比值,而是将两者一起使用! background-positioncalc()内部混合百分比和像素值时有一个特殊的行为,逻辑如下:

  1. 我们首先通过应用与百分比值相关的所有逻辑来使用百分比值来定位图像。
  2. 我们将 (1) 的位置作为参考,并通过应用与像素值相关的所有逻辑,使用像素值再次定位图像。

所以calc(50% + 50px) 的意思是:将图像居中,然后将其向左移动 50px。

这个功能可以简化很多计算。这是一个例子:

.b {
  width:200px;
  height:200px;
  display:inline-block;
  border:1px solid;
  background-image:
    linear-gradient(red,red),
    linear-gradient(red,red),
    linear-gradient(red,red),
    linear-gradient(red,red);
 background-size:20px 20px;
 background-position:
    calc(50% + 20px) 50%,
    calc(50% - 20px) 50%,
    50% calc(50% - 20px),
    50% calc(50% + 20px);
 background-repeat:no-repeat;
 transition:0.5s;
}
.b:hover {
  background-position:50%;
}
<div class="b"></div>
<div class="b" style="width:100px;height:100px;"></div>

要找到正确的百分比或像素值来放置上面的 4 个红色方块会很乏味,但是通过使用 calc() 混合这两者非常容易。

现在,假设我们有这样的东西:calc(10% + 20px + 30% + -10px + 10% + 20px)。浏览器将如何处理?

在这种情况下,浏览器将首先评估每个单元以获得简化形式calc(X% + Ypx),然后应用上述逻辑来定位图像。

calc(10% + 20px + 30% + -10px + 10% + 20px) 
calc((10% + 30% + 10%) + (20px + -10px +20px)) 
calc(50% + 30px)

.box {
  display:inline-block;
  width:200px;
  height:200px;
  background-image:url(https://picsum.photos/100/100?image=1069);
  border:1px solid;
  background-position:calc(10% + 20px + 30% + -10px + 10% + 20px) 0;
  background-repeat:no-repeat;
}
.alt {
  background-position:calc(50% + 30px) 0;
}
 
<div class="box"></div>
<div class="box alt"></div>

无论公式有多复杂,浏览器总是会分别计算百分比和像素值。


使用背景来源

这是另一个可以用来改变背景图像位置的重要属性。这个属性依赖于盒子模型,所以让我们快速提醒一下它是如何工作的:

每个元素内部都有 3 个不同的框:边框框、填充框和内容框。 background-origin 指定我们需要考虑哪个框来完成我们之前的所有逻辑。

这是一个不言自明的例子:

.b {
  display:inline-block;
  width:200px;
  height:200px;
  background:
    url(https://picsum.photos/100/100?image=1069) no-repeat,
    linear-gradient(to right,red,blue) bottom/100% 20% no-repeat;
  border:20px solid rgba(0,0,0,0.1);
  padding:20px;
  box-sizing:border-box;

  background-origin:border-box;
}
.p {
  background-origin:padding-box; /*the default value*/
}
.c {
  background-origin:content-box;
}
<div class="b"></div>
<div class="b p"></div>
<div class="b c"></div>

现在应该清楚了,当我们没有填充时content-box 等价于padding-box,当我们没有边框时border-box 等价于padding-box


使百分比表现不同

如果我们真的需要使图像的大小等于容器大小并使用像像素这样的百分比来移动它,我们可以考虑以下想法。

  • 使用伪元素作为背景层:

.b {
  width:200px;
  height:200px;
  border:1px solid;
  position:relative;
  z-index:0;
  overflow:hidden;
}
.b:before {
  content:"";
  position:absolute;
  top:0;
  left:0;
  width:100%;
  height:100%;
  z-index:-1;
  background:url(https://picsum.photos/200/200?image=1069);
  background-size:100% 100%;
  transition:1s;
}
.b:hover::before {
  transform:translate(100%,100%);
}
&lt;div class="b"&gt;&lt;/div&gt;

我们应该注意到 translate 考虑了伪元素的大小,但由于它与容器相同,我们不会有任何问题。我们也可以使用left/top,但是transform会有更好的性能。

  • 使用background-origin

诀窍是有填充,将原点限制为内容框,并使尺寸大于100% 以覆盖填充并让图像填充容器。

.b {
  width:200px;
  height:200px;
  outline:1px solid;
  padding:0 100px 100px 0;
  box-sizing:border-box;
  z-index:0;
  overflow:hidden;
  background:url(https://picsum.photos/200/200?image=1069) no-repeat;
  background-origin:content-box;
  background-size:200% 200%;
  transition:0.8s;
}

.b:hover {
  background-position:-200% -200%; 
  /* We use [0%,-200%] to cover [0%,100%]*/
}
&lt;div class="b"&gt;&lt;/div&gt;

在上面,我将填充设置为逻辑上的一半,因此我需要在background-size 中使用200% 来纠正。对于background-position,现在根据上面的解释很容易找到需要的值。

另一个例子:

.b {
  width:200px;
  height:200px;
  outline:1px solid;
  padding:50px;
  box-sizing:border-box;
  z-index:0;
  overflow:hidden;
  background:url(https://picsum.photos/200/200?image=1069) no-repeat;
  background-origin:content-box;
  background-size:200% 200%;
  background-position:50% 50%;
  transition:0.8s;
}

.b:hover {
  background-position:-150% -150%; 
  /* We use [50%,-150%] to cover [0%,100%]*/
}
&lt;div class="b"&gt;&lt;/div&gt;


请注意,emch 等其他单位的行为与 px 相同。他们被称为lengths

【讨论】:

【解决方案2】:

终于找到了不使用fixed measure或javascript的解决方案

body {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: space-around;
  align-items: center;
}

.button {
  display: flex;
  justify-content: center;
  align-items: center;
  text-decoration: none;
  color: white;
  font-weight: bold;
  width: 350px;
  height: 50px;
  transition: background 0.5s;
}


.button-percentage {
  background-image: radial-gradient(circle at 50% 50%,#2484c6,#1995c8 51%,#00bbce);
  background-repeat: repeat;
  background-size: 200% auto;
  background-position: 100% 0;
  transition: background-position .5s ease-in-out
}

.button-percentage:hover {
  background-position: 0 0;
}
&lt;a href="#" class="button button-percentage"&gt;In Percentage&lt;/a&gt;

【讨论】:

  • 所以我断定您甚至没有阅读我的答案 :) .. 我在第一个 sn-p 中给出了该解决方案 .. 您甚至不需要阅读还包含回答。
猜你喜欢
  • 1970-01-01
  • 2019-08-20
  • 1970-01-01
  • 2018-10-13
  • 1970-01-01
  • 1970-01-01
  • 2023-04-11
相关资源
最近更新 更多