移动背景

Avatar of Saleh Mubashar
Saleh Mubashar

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

我们通常认为背景图片是纹理或提供易于阅读内容对比的东西 - 换句话说,不是真正的内容。 如果是内容,您可能会使用 <img>,无障碍和其他东西。

但有时,背景图片的 位置比例 可能介于内容和装饰之间。 上下文为王,对吧? 如果我们更改背景图片的位置,它可能会传达更多上下文或体验。

怎么会这样? 让我们看看我看到的几个例子。

在开始之前,我要提醒大家,这些演示中,用于装饰的图片和用作内容的图片之间有一条细线。 区别在于无障碍方面,背景不会向屏幕阅读器宣布。 如果您的图片真的是图片,那么也许可以考虑使用 <img> 标签以及适当的 alt 文本。 同时,当我们谈论无障碍时,最好 考虑用户的运动偏好

显示更多!

Chris Coyier 几年前就展示了这个简洁的小演示。

该演示非常实用,因为它是一种在内容中显示广告的巧妙方法。 您有销售宣传和引人入胜的图片来补充它。

我认为大多数广告的最大限制是有限的空间。 我不知道您是否曾经需要在页面上放置广告,但我曾经这样做过,并且通常要求广告商提供符合确切像素尺寸的图片,以便资产适合空间。

但 Chris 的演示消除了空间问题。 将鼠标悬停在图片上,观察它移动和缩放。 用户实际上比图片处于原始位置时获得 更多 产品上下文。 这不是双赢吗? 广告商可以创建醒目的图片,而不会影响上下文。 同时,用户从新显示的图片部分获得了一些额外的价值。

如果您查看演示的标记,您会注意到它几乎与您预期的一样。 以下是简略版本

<div class="ad-container">
  <a href="#" target="_blank" rel="noopener">
    <!-- Background image container  -->
    <div class="ad-image"></div>
  </a> 
  <div class="ad-content">
    <!-- Content -->
  </div>
</div>

我们可能会对语义略微挑剔,但这并不是重点。 我们有一个容器,其中包含一个链接到背景图片的 <div> 和另一个包含内容的 <div>

至于样式,重要的部分在这里

.container {
  background-image: url("/path/to/some/image.png");
  background-repeat: no-repeat;
  background-position: 0 0;
  height: 400px;
  width: 350px;
}

不错吧? 我们为容器提供一些尺寸,并为其设置一个不重复且由其左下角边缘定位的背景图片。

真正的技巧在于 JavaScript。 我们将用它来获取鼠标位置和容器的偏移量,然后将该值转换为适当的比例以设置 background-position。 首先,让我们监听 .container 元素上的鼠标移动。

let container = document.querySelector(".container");
container.addEventListener("mousemove", function(e) {
    // Our function
  }
);

从这里,我们可以使用容器的 offsetXoffsetY 属性。 但我们不会直接使用这些值,因为 X 坐标的值小于我们需要的值,而 Y 坐标的值更大。 我们需要进行一些操作,才能找到可以作为乘数使用的常数。

这有点摸索,但我发现 1.320.455 分别适用于 X 和 Y 坐标。 我们将偏移量乘以这些值,在结果中追加一个 px 单位,然后将其应用于 background-position 值。

let container = document.querySelector(".container");
container.addEventListener("mousemove", function(e) {
    container.style.backgroundPositionX = -e.offsetX * 1.32 + "px";
    container.style.backgroundPositionY = -e.offsetY * 0.455 + "px";
  }
);

最后,如果用户离开图片容器,我们还可以将背景位置重置回原始位置。

container.addEventListener("mouseleave", function() {
    container.style.backgroundPosition = "0px 0px";
  }
);

由于我们是在 CSS-Tricks 上,我要提议,我们本可以使用纯 CSS 中的少量悬停过渡来完成更便宜的版本。

绘制更宏伟的画面

毫无疑问,您去过一些在线服装店或其他地方,遇到了老式的悬停缩放功能。

这种模式已经存在了很长时间(Dylan Winn-Brown 在 2016 年 分享了他的方法),但这仅仅证明了(我希望)它的实用性。 用户在放大时获得更多上下文,并能更好地了解毛衣的缝合或其他东西。

这分为两部分:容器放大镜。 容器是我们唯一需要在标记中包含的东西,因为我们将在用户交互期间注入放大镜元素。 所以,请看我们的 HTML 代码!

<div class="container"></div>

在 CSS 中,我们将创建 widthheight 变量来存储放大镜本身的尺寸。 然后,我们将为 .container 提供一些形状和 background-image

​​:root {
​​  --magnifer-width: 85;
​​  --magnifer-height: 85;
​​}

.container {
  width: 500px;
  height: 400px;
  background-size: cover;
  background-image: url("/path/to/image.png");
  background-repeat: no-repeat;
  position: relative;
}

在我们看到放大镜之前,我们已经知道它的某些信息,并且可以预先定义这些样式,特别是之前为 .maginifierwidthheight 定义的变量

.magnifier {
  position: absolute;
  width: calc(var(--magnifer-width) * 1px);
​​  height: calc(var(--magnifer-height) * 1px);
​​  border: 3px solid #000;
​​  cursor: none;
​​  background-image: url("/path/to/image.png");
​​  background-repeat: no-repeat;
}

这是一个绝对定位的小方块,它使用与 .container 相同的背景图片文件。 请注意,calc 函数仅用于将变量中的无单位值转换为像素。 您可以根据需要随意安排它,以便消除代码中的重复。

现在,让我们转向将所有这些内容整合在一起的 JavaScript。 首先,我们需要访问之前定义的 CSS 变量。 我们稍后将在多个地方使用它。 然后,我们需要获取容器内的鼠标位置,因为这是我们将用于放大镜背景位置的值。

​​// Get the css variables
​​let root = window.getComputedStyle(document.documentElement);
​​let magnifier_width = root.getPropertyValue("--magnifer-width");
​​let magnifier_height = root.getPropertyValue("--magnifer-height");

let container = document.querySelector(".container");
let rect = container.getBoundingClientRect();
let x = (e.pageX - rect.left);
let y = (e.pageY - rect.top);

// Take page scrolling into account
x = x - window.pageXOffset;
y = y - window.pageYOffset;

我们基本上需要在 .container 上添加一个 mousemove 事件监听器。 然后,我们将使用 event.pageXevent.pageY 属性来获取鼠标的 X 或 Y 坐标。 但是要获得鼠标在元素上的 精确 相对位置,我们需要从上面 JavaScript 获得的鼠标位置中减去父元素的位置。 一个“简单”的方法是使用 getBoundingClientRect(),它返回元素的大小及其相对于视窗的位置。

请注意,我将滚动考虑在内。 如果有溢出,减去窗口 pageXpageY 偏移量将确保效果按预期运行。

我们将首先创建放大镜 div。 接下来,我们将创建一个 mousemove 函数并将其添加到图片容器中。 在此函数中,我们将为放大镜提供一个 class 属性。 我们还将计算鼠标位置,并为放大镜提供我们之前计算的 left 和 top 值。

让我们在 .container 上听到 mousemove 事件时构建 magnifier

// create the magnifier
let magnifier = document.createElement("div");
container.append(magnifier);

现在,我们需要确保它有一个类名,我们可以将其限定到 CSS

// run the function on `mousemove`
container.addEventListener("mousemove", (e) => {
  magnifier.setAttribute("class", "magnifier");
}

我之前展示的示例视频将放大镜定位在容器外部。 我们将保持简单,将其叠加在容器之上,而不是随着鼠标移动而移动。 我们将使用 if 语句仅在 X 和 Y 值 大于等于 零,并且 小于 容器的宽度或高度时设置放大镜的位置。 这应该可以将其保持在范围内。 请务必从 X 和 Y 值中减去放大镜的宽度和高度。

// Run the function on mouse move.
container.addEventListener("mousemove", (e) => {
  magnifier.setAttribute("class", "magnifier");

  // Get mouse position
  let rect = container.getBoundingClientRect();
  let x = (e.pageX - rect.left);
  let y = (e.pageY - rect.top);
  
  // Take page scrolling into account
  x = x - window.pageXOffset;
  y = y - window.pageYOffset;

  // Prevent magnifier from exiting the container
  // Then set top and left values of magnifier
  if (x >= 0 && x <= container.clientWidth - magnifier_width) {
    magnifier.style.left = x + "px";
  }
  if (y >= 0 && y <= container.clientHeight - magnifier_height) {
    magnifier.style.top = y + "px";
  }
});

最后但并非最不重要的一点是……我们需要稍微调整一下放大镜的背景图片。 关键是,用户可以根据鼠标悬停的位置获得背景图片的 更大 视图。 因此,让我们定义一个放大镜,我们可以用它来放大图像。 然后,我们将为背景图片的宽度和高度定义变量,以便我们有一些参考来进行缩放,并为 .magnifier 样式设置所有这些值

// Magnifier image configurations
let magnify = 2;
let imgWidth = 500;
let imgHeight = 400;

magnifier.style.backgroundSize = imgWidth * magnify + "px " + imgHeight * magnify + "px";

让我们获取放大镜图片的 X 和 Y 坐标,并将其应用于 .magnifier 元素的 background-position。 与之前放大镜位置一样,我们需要使用 CSS 变量从 X 和 Y 值中减去放大镜的宽度和高度。

// the x and y positions of the magnifier image
let magnify_x = x * magnify + 15;
let magnify_y = y * magnify + 15;

// set backgroundPosition for magnifier if it is within image
if (
  x <= container.clientWidth - magnifier_width &&
  y <= container.clientHeight - magnifier_height
) {
  magnifier.style.backgroundPosition = -magnify_x + "px " + -magnify_y + "px";
}

瞧!

让它更具电影感

您是否见过 肯·伯恩斯效果? 它是一种经典且永恒的东西,其中图片大于它所在的容器,然后缓慢地像蜗牛一样滑动和缩放。 几乎世界上每一部纪录片似乎都将其用于图片静止画面。 如果您有 Apple TV,那么您一定在屏幕保护程序上看到过它。

如果您想更好地了解它,有很多 CodePen 上的示例

您会看到,有多种方法可以实现这一点。 有些使用 JavaScript。 其他是 100% CSS。 我相信 JavaScript 方法对于某些用例来说是不错的,但如果目标只是微妙地缩放图片,CSS 完全适用。

我们可以使用多个背景而不是一个背景来稍微增加趣味性。 或者,更好的方法是,如果我们将规则扩展到使用元素而不是背景图片,我们可以将相同的动画应用于所有背景,并使用一些 animation-delay 来交错效果。

当然,有很多方法可以实现这一点! 它当然可以使用 Sass 和/或 CSS 变量进行优化。 heck,也许您可以使用单个 <div> 来实现。 如果是这样,请在评论中分享!

奖励:让它更身临其境

我不知道还有什么比 Sarah Drasner 的 “万圣节快乐” 代码笔 更酷了…… 而且那是 2016 年的!这是一个很好的例子,它分层背景并在不同的速度下移动它们,从而创造出一种近乎电影般的体验。天哪,太酷了!

GSAP 是那里的主要驱动因素,但我认为我们可以做一个简化的版本,它只是以不同的速度将每个背景层从左到右平移。当然,不那么酷,但肯定是最基本体验。必须确保每个背景图像的开始和结束一致,以便在动画重复时无缝重复。


目前就这些了!很酷的是,我们可以将背景用于比纹理和对比度更多的东西。我绝对相信,我们可以将大量的其他巧妙交互应用于背景。Temani Afif 就做了类似的事情,为链接创建了一堆很酷的悬停效果。您有什么想法吗?在评论中与我分享!