在线性梯度上使用具有背景位置的百分比值 [英] Using percentage values with background-position on a linear-gradient

查看:30
本文介绍了在线性梯度上使用具有背景位置的百分比值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

body {最小高度:100vh;显示:弯曲;弹性方向:列;justify-content:空间环绕;对齐项目:居中;}.按钮 {显示:弯曲;证明内容:中心;对齐项目:居中;文字装饰:无;白颜色;字体粗细:粗体;宽度:350 像素;高度:50px;边框:1px 纯绿色;过渡:背景0.5s;背景重复:不重复;背景图像:线性渐变(向左,#2484c6,#1995c8 51%,#00bbce),线性渐变(向右,#2484c6 0%,#1995c8 51%,#00bbce 76%);}.按钮像素{背景位置:-350px 0px,0px 0px;}.按钮像素:悬停{背景位置:0px 0px,350px 0px;}.button-percentage {背景位置:-100% 0px,0px 0px;}.按钮百分比:悬停{背景位置:0% 0px,100% 0px;}

<a href="#" class="button button-pixel">在像素中</a><a href="#" class="button button-percentage">按百分比</a>

解决方案

TL;DR

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

body {显示:弯曲;弹性方向:列;justify-content:空间环绕;对齐项目:居中;最小高度:90vh;}.按钮 {文字装饰:无;白颜色;字体粗细:粗体;宽度:350 像素;高度:50px;文本对齐:居中;过渡:背景0.5s;背景重复:不重复;背景图:线性梯度(向左,#2484c6,#1995c8 51%,#00bbce),线性梯度(向右,#2484c6 0%,#1995c8 51%,#00bbce 76%);背景大小:200% 100%;/*改变了这个*/}.按钮像素{背景位置:-350px 0px,0px 0px;}.按钮像素:悬停{背景位置:0px 0px,350px 0px;}.button-percentage {背景位置:100% 0px,0px 0px;}.按钮百分比:悬停{背景位置:0% 0px,100% 0px;}

<a href="#" class="button button-pixel">像素</a><a href="#" class="button button-percentage">百分比</a>


背景位置如何工作?

让我们使用经典图像来解释 background-position 的工作原理.

使用像素值时,无论大小如何,参考都是图像的上/左角.这就像将 top/left 与定位元素一起使用:

.b {宽度:200px;高度:200px;背景:url(https://picsum.photos/100/100?image=1069)不重复;边框:1px 实心;背景位置:0 0;位置:相对;动画:后退5s无限线性交替;}.b:之前{内容:"";位置:绝对;顶部:0;左:0;宽度:10px;高度:10px;背景:红色;动画:改变5s无限线性交替;}@keyframes 返回{to{background-position:200px 200px;}}@keyframes 改变{to{top:200px;左:200px;}}

<div class="b"></div>

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

.b {宽度:200px;高度:200px;背景:url(https://picsum.photos/100/100?image=1069)不重复;边框:1px 实心;背景位置:0% 0%;位置:相对;动画:后退3s无限线性交替;}.b:之前{内容:"";位置:绝对;顶部:0;左:0;宽度:10px;高度:10px;背景:红色;动画:变化3s无限线性交替;}@keyframes 返回{to{background-position:100% 100%;}}@keyframes 改变{to{top:200px;左:200px;}}

<div class="b"></div>

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

首先,我们考虑图像以找到我们将用于放置图像的参考点.考虑到 图像的大小(如定义用绿线).然后,考虑到容器的大小,我们将该点放在容器内,距离左上角30% 30%..p>

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

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

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

.b {宽度:200px;高度:200px;背景:url(https://picsum.photos/200/200?image=1069) 不重复;边框:1px 实心;背景位置:0% 0%;位置:相对;动画:后退5s无限线性交替;}.b:之前{内容:"";位置:绝对;顶部:0;左:0;宽度:10px;高度:10px;背景:红色;动画:改变5s无限线性交替;}@keyframes 返回{to{background-position:100% 100%;}}@keyframes 更改为{top:100%;左:100%;}}

<div class="b"></div>


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

如果我们参考

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

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

.b {宽度:200px;高度:200px;显示:内联块;边框:1px 实心;背景:url(https://picsum.photos/100/100?image=1069) -50% 0/100px 100px 无重复;}.一个{背景:url(https://picsum.photos/100/100?image=1069) -50px 0/100px 100px 无重复;}

<div class="b"></div><div 类="b a"></div>

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

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

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

.b{宽度:200px;高度:200px;边框:1px 实心;背景:url(https://picsum.photos/300/300?image=1069) -50% 0/50px 50px 无重复;动画:改变2s线性无限交替;}@keyframes 改变{{背景尺寸:300px 300px}}

<div class="b"></div>

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

.b {宽度:200px;高度:200px;边框:1px 实心;背景:线性渐变(向右,红色,蓝色)-50% 0/50px 150px 无重复;动画:改变2s线性无限交替;}@keyframes 改变{{背景尺寸:300px 150px}}

<div class="b"></div>

大值(> 100%)

与之前的逻辑相同:如果我们在 150% 0 处定义背景,那么我们认为我们的参考点是从左边缘开始的 150%(或 50% 从右边缘开始),然后我们将它放置在距离容器左边缘 150% 的位置.

.b {宽度:200px;高度:200px;边框:1px 实心;背景:url(https://picsum.photos/100/100?image=1069) 150% 0/100px 100px 无重复;}

<div class="b"></div>

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

.b {宽度:200px;高度:200px;边框:1px 实心;背景:url(https://picsum.photos/300/300?image=1069) 150% 0/50px 50px 无重复;动画:改变2s无限线性交替;}@keyframes 改变 {{背景尺寸:300px 300px}}

<div class="b"></div>


特殊情况

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

让我们考虑下图:

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

p * Wp = p * Ws + Ws <=>p = Ws/(Wp - Ws) 其中 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 -->p' = 1 + s/(1 - s) = 1/(1 - s).

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

.b {宽度:200px;高度:50 像素;边距:5px;边框:1px 实心;背景:线性渐变(向右,红色,蓝色)不重复;背景尺寸:计算(var(--s)* 100%)100%;动画:改变4s线性无限交替;}@keyframes 改变{from {/*向左隐藏*/背景位置:计算(var(--s)/(var(--s)-1)* 100%)}to {/*向右隐藏*/背景位置:计算(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:XY

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

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

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

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

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

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

.b {宽度:200px;高度:50 像素;边距:5px;边框:1px 实心;背景:线性渐变(向右,红色,蓝色)不重复;背景尺寸:计算(var(--s)* 100%)100%;动画:百分比2s线性无限交替;}.box.a {动画名称:像素;}@keyframes 百分比{来自{背景位置:-50%;}到{背景位置:150%;}}@keyframes 像素{从{背景位置:计算(-0.5 * 200px *(1 - var(--s)))}到 { 背景位置:计算(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: XY 等价于 background-position: left X top Y (位置在 XleftYtop).通过调整 top 和/或 left 我们改变了参考和图像的放置方式.以下是一些示例:

.b {宽度:150px;高度:150px;显示:内联块;背景:网址(https://picsum.photos/70/70?image=1069)不重复;边框:1px 实心;位置:相对;}身体 {边距: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 {宽度:200px;高度:50 像素;边距:5px;边框:1px 实心;背景:线性渐变(向右,红色,蓝色)不重复;背景尺寸:计算(var(--s)* 100%)100%;动画:改变4s线性无限交替;}@keyframes 改变{从 {背景位置:左 calc(var(--s)/(var(--s) - 1) * 100%) 前 0}到 {背景位置:右 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%右 -100%.

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

.b {宽度:200px;高度:200px;背景:url(https://picsum.photos/100/100?image=1069)不重复;边框:1px 实心;背景重复:不重复;动画:改变2s无限线性;}@keyframes 改变{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;}}

<div class="b"></div>

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


结合像素值和百分比值

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

背景位置呢?如果我们写calc(50% + 50px),会计算成百分比值还是像素值?将像素值转换为百分比还是相反?

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

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

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

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

.b {宽度:200px;高度:200px;显示:内联块;边框:1px 实心;背景图:线性梯度(红色,红色),线性梯度(红色,红色),线性梯度(红色,红色),线性梯度(红色,红色);背景尺寸:20px 20px;背景位置:计算(50% + 20px)50%,计算(50% - 20px) 50%,50% 计算(50% - 20px),50% 计算(50% + 20px);背景重复:不重复;过渡:0.5s;}.b:悬停{背景位置: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),然后应用上述逻辑来定位图像.

计算(10% + 20px + 30% + -10px + 10% + 20px)计算((10% + 30% + 10%) + (20px + -10px +20px))计算(50% + 30px)

.box {显示:内联块;宽度:200px;高度:200px;背景图片:网址(https://picsum.photos/100/100?image=1069);边框:1px 实心;背景位置:计算(10% + 20px + 30% + -10px + 10% + 20px)0;背景重复:不重复;}.alt {背景位置:计算(50% + 30px)0;}

<div class="box"></div><div class="box alt"></div>

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


使用背景来源

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

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

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

.b {显示:内联块;宽度:200px;高度:200px;背景:网址(https://picsum.photos/100/100?image=1069)不重复,线性渐变(向右,红色,蓝色)底部/100% 20% 无重复;边框:20px 实心 rgba(0,0,0,0.1);填充:20px;框尺寸:边框框;背景来源:边框框;}.p {背景来源:填充框;/*默认值*/}.C {背景来源:内容框;}

<div class="b"></div><div class="b p"></div><div class="b c"></div>

现在应该清楚了,当我们没有padding的时候content-box等价于padding-box,而当我们没有border的时候border-box 等价于 padding-box.


使百分比表现不同

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

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

.b {宽度:200px;高度:200px;边框:1px 实心;位置:相对;z-索引:0;溢出:隐藏;}.b:之前{内容:"";位置:绝对;顶部:0;左:0;宽度:100%;高度:100%;z-索引:-1;背景:网址(https://picsum.photos/200/200?image=1069);背景尺寸:100% 100%;过渡:1s;}.b:hover::before {变换:翻译(100%,100%);}

<div class="b"></div>

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

  • 使用背景来源

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

.b {宽度:200px;高度:200px;轮廓:1px 实心;填充:0 100px 100px 0;框尺寸:边框框;z-索引:0;溢出:隐藏;背景:url(https://picsum.photos/200/200?image=1069) 不重复;背景来源:内容框;背景大小:200% 200%;过渡:0.8s;}.b:悬停{背景位置:-200% -200%;/* 我们使用 [0%,-200%] 覆盖 [0%,100%]*/}

<div class="b"></div>

在上面,我把填充的大小缩小了一半,所以我需要在 background-size 中使用 200% 来纠正.对于background-position,现在根据上面的解释很容易找到需要的值.

另一个例子:

.b {宽度:200px;高度:200px;轮廓:1px 实心;填充:50px;框尺寸:边框框;z-索引:0;溢出:隐藏;背景:url(https://picsum.photos/200/200?image=1069) 不重复;背景来源:内容框;背景大小:200% 200%;背景位置:50% 50%;过渡:0.8s;}.b:悬停{背景位置:-150% -150%;/* 我们使用 [50%,-150%] 覆盖 [0%,100%]*/}

<div class="b"></div>



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

Is there any way to make background-position take percentage values? Currently my button only works with an explicit values for width and background-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>

解决方案

TL;DR

All the percentage values used with background-position are equivalent when using a gradient as background, so you won't see any difference. You need to specify a background-size different from the container 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>


How does background position work?

Let's use a classic image in order to explain how background-position works.

When using pixel values, the reference is the top/left corner of the image, whatever the size is. It's like using top/left with a positioned element:

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

<div class="b"></div>

When using percentage values, the reference is different from when you use pixel values; it's no longer the top/left corner:

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

<div class="b"></div>

In this case, we need to consider two parameters: the size of the container AND the size of the image. Here is an illustration of how it works (I took a background-position equal to 30% 30%):

First, we consider the image to find the reference point we will use in order to place the image. It's the point inside the image that is positioned at 30% 30% from the top/left corner considering the size of the image (like defined with the green lines). Then, we place that point inside the container at 30% 30% from the top/left corner considering the size of the container.

From this logic, we can clearly identify some trivial cases like

                50% 50% (center)                   100% 100% (bottom right)                   100% 0% (top right)

Now it's clear that if the size of the image is equal to the size of the container then nothing will happen simply because all the positions are equivalent. The top/left of the image is already at the top/left (0% 0%) of the container, the center is already at the center (50% 50%) etc.

.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%;}}

<div class="b"></div>


The above logic is the same when applied to gradients since gradients are considered images, and by default, if you don't specify a background-size, a gradient's size will be the size of its container, unlike when using an image.

If we refer to the specification of the background-size, we can see how your problem arises:

If both values are auto then the intrinsic width and/or height of the image should be used, if any, the missing dimension (if any) behaving as auto as described above. If the image has neither an intrinsic width nor an intrinsic height, its size is determined as for contain.

And:

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.

And also:

A bitmap image (such as JPG) always has intrinsic dimensions and proportions.

CSS <gradient>s have no intrinsic dimensions or intrinsic proportions.ref

An image always has intrinsic values, so in most cases it won't have the same size as its container, so background-position with percentage units will have an effect. But gradients don't have intrinsic values, thus a gradient's dimensions will be equal to the size of its container, and background-position with percentage values will never work unless we specify a background-size different from its container's dimensions.



More in-depth

We saw in the above examples how background-size works when using values between 0% and 100%, but what about using negative values or a value bigger than 100%? The logic is the same, but finding the reference point will be more tricky.

Negative values (< 0%)

Let's suppose we want to place a background at -50% 0. In this case the reference point will be outside the image. Here is an example:

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

<div class="b"></div>

As we can see in the illustration, we first consider -50% of the image, which is -50px, in order to define our reference point (i.e., -50px from the left edge of the image). Then we place that point at -50% considering the size of the container (-100px from the left edge of the container). Then we draw the image, and we obtain the above result. Only 100px of the image is visible.

We may also notice that negative percentage values will behave the same as negative fixed values when the size of the image is smaller than the size of the container (both will shift the image to the left). In this case -50% 0 is the same as -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>

If for example we increase the image size to 150px 150px, -50% 0 will be the same as -25px 0.

When we make the size bigger than the container, negative values will start shifting the image to the right (like with positive pixel values), which is logical since the 50% of the image will increase while the 50% of the container will remain the same.

If we consider the previous illustration, it's like increasing the top green line until it's bigger than the bottom one. So the sign only is not enough to know how the background image will be shifted; we need to also consider the size.

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

The same will logically happen for gradients:

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

Big values (> 100%)

Same logic as previously: if we define a background at 150% 0, then we consider our reference point 150% from the left edge (or 50% from the right edge), then we place it 150% from the left edge of the container.

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

<div class="b"></div>

In this case, 150% 0 is equivalent to 150px 0, and if we start increasing the background size we will have the same behavior as previously demonstrated:

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

<div class="b"></div>


Special cases

Using values outside the range [0% 100%] allows us to hide the background image, but how do we find the exact values in order to completely hide the image?

Let's consider the below illustration:

Our image has a width Ws and the container a width Wp and we need to find the value of p. From the figure we can obtain the following formula:

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

If the container size is 200px and the image is 100px then p is 1 so 100% (we add of course the negative sign and it's -100%).

We can make this more generic if we consider percentage values with background-size instead of fixed values. Suppose the background-size is S%. Then we will have Ws = Wp * s (s in [0,1] and S=s*100%), and the formula will be

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

Adding the negative sign it will be p = s / (s - 1).

Now if we want to hide the image on the right side, we do the same logic on the right (we consider a mirror of the previous illustration), but since we will always consider the left edge to find the percentage we need to add 100%.

The new percentage p'% is 100% + p%, and the formula will be p' = 1 + p --> p' = 1 + s / (1 - s) = 1 / (1 - s).

Here is an animation to illustrate the above calculation:

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

Let's calculate some values:

When s=0.5, we have a background-size equal to 50%, and the percentage values will be from -100% to 200%. In this case, we started with a negative value and ended with a positive one because the size of the image is smaller than the size of the container. If we consider the last case (s=2) the background-size is equal to 200%, and the percentage values will be from 200% to -100%. We started with a positive value and ended with a negative one because the size of the image is bigger than the size of the container.

This confirms what we said previously: to shift an image to the left we need negative values if the size is small, but we need positive values if the size is big (same thing for the right).


Relation between pixel and percentage values

Let's define a way to calculate percentage values based on pixel values, or vice versa (i.e. the formula to convert between both). To do this we simply need to consider the reference points

When using pixel values, we will consider the blue lines and we will have background-position:X Y

When using percentage values, we will consider the green lines and we will have background-position:Px Py.

The formula will be like follow : X + Px * Ws = Px * Wp where Ws is the width of the image and Wp is the width of the container (same formula for the Y axis considering height).

We will have X = Py * (Wp - Ws). From this formula we can validate two points as explained previously:

  • When Wp = Ws, the formula is no longer valid, which confirms that percentage values have no effect when the size of the image is the same as the container; thus there is no relation between pixel and percentage values.
  • X and Px will have the same sign when Wp > Ws and will have opposite sign when Wp < Ws. This confirms that percentage value behave differently depending the size of the image.

We can also express the formula differently if we consider percentage value of background-size. We will have X = Px * Wp * (1-s).

Here is an animation to illustrate the above calculation:

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


Changing the reference

In the above calculations, we always considered the top/left corner of the image and the container in order to apply our logic either for pixel values or percentage values. This reference can be changed by adding more values to background-position.

By default background-position: X Y is equivalent to background-position: left X top Y (position at X from the left and at Y from the top). By adjusting top and/or left we change the reference and how the image is placed. Here are some examples:

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

It's clear that for the X value we can only use left and right (the horizontal position) and with the Y value we can only use bottom and top (the vertical position). With all the different combinations we can logically obtain the 4 different corners.

This feature is also useful in order to optimize some calculation. In the example of the special cases section, we did a first calculation to hide the image on the left then another one to hide it on the right. If we consider changing the reference we only need to do one calculation. We take the formula used for the left side and we use the same on the right side.

Here is the new version:

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

For s=0.5 we will no more animate from -100% to 200% BUT it will be from left -100% to right -100%.

Here is another example using pixel values where we can clearly see how easy is to deal with the calculation when changing the reference:

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

<div class="b"></div>

It would be tricky to achieve the same animation by keeping the same reference. So if we want to do a symmetrical animation we do our logic on one side and use the same on the other side by changing the reference.


Combining pixel and percentage values

We can use calc() in order to do some complex calculation that involves different units. For example, we can write width:calc(100px + 20% + 12em) and the browser will calculate the computed value considering how each unit works and we will end with a pixel value (for this case).

What about background-position? If we write calc(50% + 50px), will this be evaluated to a percentage value or a pixel value? will the pixel value be converted to percentage or the opposite?

The result will not be converted to a pixel value or a percentage value, but rather both will be used together! background-position has a special behavior when mixing percentage and pixel values inside calc() and the logic is as follows:

  1. We first use the percentage value to position the image by applying all the logic related to percentage values.
  2. We consider the position of (1) as the reference and we use the pixel value to position the image again by applying all the logic related to pixel values.

So calc(50% + 50px) means: center the image, then shift it by 50px to the left.

This feature can simplify a lot of calculation. Here is an example:

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

It would be tedious to find the correct percentage or pixel values to place the 4 red squares like above, but by mixing both using calc() it is pretty easy.

Now, let's suppose we have something like this: calc(10% + 20px + 30% + -10px + 10% + 20px). How will the browser handle this?

In such case, the browser will first evaluate each unit to obtain the simplified form calc(X% + Ypx) then apply the above logic to position the image.

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>

Whatever the complexity of the formula is, the browser will always evaluate percentage and pixel values separately.


Using background-origin

Here is another important property that can be used to alter the position of background image. This property relies on the box model so lets get a quick reminder about how that works:

Each element has 3 different boxes inside it: border-box, padding-box and the content-box. background-origin specifies which box we need to consider in order to do all our previous logic.

Here is a self-explanatory example:

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

It should be clear now that when we don't have padding content-box is equivalent to padding-box, and when we don't have border border-box is equivalent to padding-box.


Make percentage behave differently

In case we really need to have the size of the image equal to the container size and move it using percentage like pixel we can consider the below ideas.

  • Using pseudo element as a background layer:

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

<div class="b"></div>

We should note that translate consider the size of the pseudo element but since it's the same as the container we won't have any issue. We can also useleft/top but transform will have better performance.

  • Using background-origin

The trick is to have padding, restrict the origin to content-box and make the size bigger than 100% to cover the padding and have the image fill the container.

.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%]*/
}

<div class="b"></div>

In the above, I made the padding half the size so logically I need to use 200% in background-size to rectify. For the background-position, it's now easy to find the needed value based on the above explanation.

Another example:

.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%]*/
}

<div class="b"></div>



Note that other units like em, ch, etc, behave the same as px. They are called lengths.

这篇关于在线性梯度上使用具有背景位置的百分比值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆