使用百分比将 `background-image` 焦点定位在精确位置

Avatar of Jay Sitter
Jay Sitter

DigitalOcean 为您的旅程每个阶段提供云产品。 立即开始使用 $200 免费积分!

假设您有一个带有 `background-image` 的元素,其中图像的一部分可见,因为图像比元素本身更大。 其余部分被裁剪掉,位于元素之外。

现在您想要移动该 `background-image`,以便将元素的中心聚焦在图像的特定点上。 您还想用百分比值而不是像素值来完成。 我们需要动动脑筋。

这将涉及一些数学。

让我们使用此作为我们的图像,其中包含用于大小调整的标记

这是我们的元素,已应用了该 `background-image`。 注意我们只能看到图像的(顶部)左侧

查看 Pen 背景焦点:无位置 by Jay (@jsit) on CodePen.

现在假设我们要将图像上的特定点与该元素的中心对齐。 例如,距离左侧 300 像素的点。

由于我们要求的是以像素为单位的位置,因此非常简单。 在没有定义位置的情况下,`background-image` 以中心点位于 100 像素的位置“开始”,因此您需要将其向左移动 200 像素

查看 Pen 背景焦点:像素位置 by Jay (@jsit) on CodePen.

让我们将其规范化。

您用于 `background-position-x` 的 `x` 值是按如下方式计算的

(0.5 × [bounding box width]) - [x-coordinate]
             0.5 × 200px -              300px
                   100px -              300px = -200px

花点时间就能弄明白,但这并不难。 您可能可以直观地想明白这一点,而无需使用公式。

但是,如果要(或必须)将 `background-position-x` 表达为百分比怎么办? 应该不难吧? 让我们尝试使用百分比再次将自己居中在 300 像素处。 我们的 `background-position-x` 为 `-200px`,因此让我们将其转换为百分比:-200 / 800 = -25%,因此

查看 Pen 背景焦点:百分比(第一次尝试) by Jay (@jsit) on CodePen.

嗯,根本没有用。 也许我们需要使用正值?

查看 Pen 背景焦点:百分比(第二次尝试) by Jay (@jsit) on CodePen.

这好多了,但它居中在…… 250 像素? 如何将其作为边界框宽度的百分比:300 / 200 = 150%。 这不可能正确…

查看 Pen 背景焦点:百分比(第三次尝试) by Jay (@jsit) on CodePen.

是的,这不对。

让我们退一步。 如果我们这样做会怎么样?

查看 Pen 背景焦点:百分比(第四次尝试) by Jay (@jsit) on CodePen.

感觉有点道理;`background-position-x: 100%;` 使 `background-image` 靠右对齐,并在 700 像素处居中,即图像宽度 7/8。 但是,如果我们要将其居中在 100% 处? 我想我们必须做… 9/8?

查看 Pen 背景焦点:百分比(第五次尝试) by Jay (@jsit) on CodePen.

此时,我不惊讶它没有用。

这感觉不是正确的方向。 让我们退一步。

规范怎么说?

例如,对于 ‘0% 0%’ 的值对,图像的左上角与(通常)框的填充边的左上角对齐。 ‘100% 100%’ 的值对将图像的右下角放置在区域的右下角。 对于 ‘75% 50%’ 的值对,图像上横跨 75% 纵向 50% 的点将放置在区域上横跨 75% 纵向 50% 的点。

也许我们可以反向工程它。

在最后一个 112.5% 中,它将 `background-image` 上横跨 112.5% 的点与边界框上横跨 112.5% 的点对齐。 感觉有点道理。 规范似乎写得容易,只有三个值:0%、50% 和 100%。 任何其他值都不那么直观。

使用 `background-position-x: 0;`,我们关注的是 100 像素,即 12.5%。 使用 `background-position-x: 100%;`,我们关注的是 700 像素,即 87.5%。 那么 `background-position-x: 50%;` 究竟是什么样呢?

查看 Pen 背景焦点:百分比(第六次尝试) by Jay (@jsit) on CodePen.

50% 有点像我们的“锚点”; 这是我们想要聚焦的点以及相应的 `background-position-x` 值相等的地方。

假设我们要关注 700 像素或 87.5%。 我们从中心向右走 100%:50% + 50%。

查看 Pen 背景焦点:百分比(第四次尝试) by Jay (@jsit) on CodePen.

将 `background-position-x` 设置为 `100%`,我们绑定框的中心已从图像的中心“平移”,朝最右边边缘平移了 3/4(从 400 像素到 700 像素)。 如果我们要“平移”到最右边边缘,我们需要向右平移那额外的 1/4,即 200 像素。 1/4 是 3/4 的 1/3,因此我们需要比刚才多走三分之一,或者从中心走 66.667%

查看 Pen 背景焦点:百分比(第七次尝试) by Jay (@jsit) on CodePen.

哇! 因此,要将 `background-image` 的最右边边缘聚焦,该 `background-image` 的大小是我们绑定框的 4 倍,我们需要设置 `background-position-x: 116.667%;`。

我们到底该怎么弄明白呢?

它与我们可能期望的 100% 的“差”是 16.667%。 因此,如果我们要关注最初的目标 300 像素(或 37.5%),我们会…… 添加 16.667% 吗? 这不可能有效

查看 Pen 背景焦点:百分比(第八次尝试) by Jay (@jsit) on CodePen.

不行。

如果我们要关注最左边边缘,我们可能会从 0% 中减去 16.667%,对吧? 这听起来可能是对的。

查看 Pen 背景焦点:百分比(第九次尝试) by Jay (@jsit) on CodePen.

太棒了!

要将焦点定位在 100% 或 0% 处,您必须“超出”这些值,从中心测量时。

因此,如果我们要关注 0%,即“从背景图像最左边边缘向左走 100%”,我们必须从 50% 中减去 66.667%。 如果我们要关注 100%,即“从背景图像最左边边缘向右走 100%”,我们必须将 66.667% 添加到 50% 中。

我可能期望从 50% 中添加或减去 50%,以达到这些边缘: “从中心走多远”到“我的 `background-position-x` 值应该是什么”的 1:1 比例。 但相反,我们必须使用 4:3 的比例。 换句话说,我们必须使用一个“远离中心”的值,这个值是其四分之三倍。

事情变得有点复杂了,因此让我们引入一些术语

  • c:从背景图像最左边边缘测量的所需焦点(以百分比计)
  • z:缩放因子(背景宽度 ÷ 边界框宽度)
  • p:`background-position-x`,用于在给定 z 的情况下将焦点定位在 c 上

因此,我们将焦点的距离从中心(c − 50)乘以 4/3,然后将结果添加到“中心”,即 50。

如果要关注 600 像素(或 75%)处的点,我的 `background-position-x` 值应该是

(75% − 50%) × 4/3 + 50% = 83.333%

是的,这听起来可能有效! 拜托拜托拜托

查看 Pen 背景焦点:百分比(第十次尝试) by Jay (@jsit) on CodePen.

太棒了!

如果要关注 200 像素(或 25%)处的点,您将执行以下操作

(25% − 50%) × 4/3 + 50% = 16.667%

查看 Pen 背景焦点:百分比(第十一次尝试) by Jay (@jsit) on CodePen.

哇。

让我们将其概括

(c − 50%) × 4/3 + 50% = p

那么为什么是 4/3 呢?4 是我们 background-image 宽度与边界框宽度的比例;而 3 是…… 比 4 小 1。就这么简单吗?让我们试试一个更大的 background-image,这次宽度为 1000px,是边界框宽度的 5 倍。我们再次尝试将焦点放在 200px 的点上。这里我们的公式将是

(20% − 50%) × 5/4 + 50% = 12.5%

查看 CodePen:背景焦点:百分比(第 12 次尝试) by Jay (@jsit) on CodePen.

我的天啊,它真的起作用了!

回到我们的公式,使用可变的背景与边界框比例

(c − 50%) × z/(z − 1) + 50% = p

让我们把它翻译成英文

给定 background-image 上位置为 c 的点…

  1. 其中 c 表示为图像宽度的百分比
  2. 其中 c 预计位于背景图像边界框的水平中心
  3. 并且 background-image 的宽度是其边界框的 z 倍

正确的 background-position-x p(以百分比表示)由以下公式给出

(c − 50%) × z/(z − 1) + 50% = p

我们能更进一步推广这个公式吗?

如果我们想将 background-image 上 25% 的点与边界框上 75% 的点对齐,会怎么样?天啊!

让我们重新看一下原始公式

(c − 50%) × z/(z − 1) + 50% = p

现在让我们引入一些新术语

  • b:从边界框最左侧边缘开始的所需焦点位置(以百分比表示)。之前我们假设这个位置始终为 50%,这样边界框的中心就会与 background-image 中的目标对齐。
  • d:background-image 的焦点位置(以百分比表示),与边界框的中点对齐,以使 background-image 上的 c 与边界框上的 b 对齐;如果 background-image 上的 d 与边界框的 50% 对齐,那么 background-image 上的 c 将与边界框上的 b 对齐。

让我们这样想

想要将 background-image 上的 c 位置与边界框上的 b 位置对齐,就等同于想要将 background-image 上的另一个位置 d 与边界框的中心对齐 - 我们已经知道如何做到这一点。那么我们能找到一种方法从 c、b 和 z 推导出 d(我们需要在 50% 位置的点)吗?当然可以!

对于我们的 800px 宽的 background-image,在一个 200px 宽的边界框中(z = 4),如果我们想将边界框的最右侧边缘(b = 100%)与图像中 600px(c = 75%)的位置对齐,我们希望边界框的中心与 500px(d = 62.5%)的点对齐。

我们如何从 c(75%)得到 d(62.5%)?-12.5% 的差异从哪里来?

嗯,我们的 b 是 100%,比我们之前“默认”的 b(50%)高 50%。而 12.5% 是这个值的 1/4;1/4 是我们 z(4)的倒数。我们的 d 是从这里来的吗?这将是

d = c + (50% - b)/z

看起来很有希望。现在我们可以将 d 代入原始公式中的 c

(d − 50%) × z/(z − 1) + 50% = p

或者

(c + (50% − b)/z - 50%) × z/(z − 1) + 50% = p

哇!让我们测试一下。让我们尝试将 background-image 中 25% 的位置(200px)与边界框中 75% 的位置对齐。这将是

p = (25% + (50% - 75%)/4 - 50%) × 4/(4 - 1) + 50%
p = -31.25% × 1.333 + 50%
p = 8.333%

查看 CodePen:背景焦点:百分比(第 13 次尝试) by Jay (@jsit) on CodePen.

难以置信!让我们再确认一下。background-image 中 87.5% 的点(700px)与边界框中 33.333% 的位置对齐会怎么样?

p = (87.5% + (50% - 33.333%)/4 - 50%) × 4/(4 - 1) + 50%
p = 41.6667% × 1.333 + 50%
p = 105.555%

查看 CodePen:背景焦点:百分比(第 14 次尝试) by Jay (@jsit) on CodePen.

对我来说看起来已经足够好了!

我确信对于某些类型的人来说,这里面有一些直观的道理,但我不是其中之一。

让我们创建一个 Sass 函数,它将为我们完成所有这些荒谬的数学运算。

查看 CodePen:背景焦点:百分比(最终 Sass 函数) by Jay (@jsit) on CodePen.

我的头都晕了。

当我开始着手解决这个问题时,我并没有预料到它会如此困难,但这是一段奇妙的旅程。我希望引导你了解我的思维过程能够让你有所启发,并且你可能会在某些时候发现我们的 Sass 函数很有用。

本文中嵌入的所有 CodePen 都可以在这个集合中找到。