使用 SVG 剪辑路径创建图像弹出效果

Avatar of Adrian Bece
Adrian Bece

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

几周前,我偶然发现了 这个很酷的弹出效果,由 Mikael Ainalem 创建。它展示了 CSS 中的 clip-path: path(),该属性在大多数现代浏览器中获得了 良好的支持。我想自己深入研究一下,以便更好地了解其工作原理。但在过程中,我发现 clip-path: path(); 存在一些问题,并最终找到了一种替代方法,我想在本文中与大家一起探讨。

如果您从未使用过 clip-path 或不熟悉它,它基本上允许我们根据剪辑路径 指定元素的显示区域,并隐藏落在剪辑路径之外的元素部分。

A rectangle with a pastel pattern, plus an unfilled star shape with a black border, equals a star shape with the pastel background pattern.
您可以将其想象成星星是饼干切割器,元素是饼干面团,结果是星星形状的饼干。

clip-path 的可能值包括 circleellipsepolygon,这些值将用例限制在这些特定形状。这就是新的 path 值的用武之地——它允许我们使用更灵活的 SVG 路径来创建各种超出基本形状的剪辑路径。

让我们利用我们对 clip-path 的了解,开始着手处理悬停效果。其基本思想是使人物的前景图像从彩色背景中弹出,并在元素悬停时放大。一个重要的细节是如何使前景图像动画(放大和向上移动)看起来与背景图像动画(仅放大)独立。

这种效果看起来很酷,但 path 值存在一些问题。首先,虽然我们提到支持总体上不错,但并不完美,并且在撰写本文时,其覆盖率 约为 82%。因此,请记住,移动设备支持目前仅限于 Chrome 和 Safari。

除了支持之外,path 的另一个更大、更奇怪的问题是,**它目前仅适用于像素值**,这意味着 它没有响应性。例如,假设我们放大页面。一开始,路径形状就开始裁剪内容。

这严重限制了 clip-path: path() 的用例数量,因为它只能用于固定大小的元素。响应式网页设计多年来一直是广泛接受的标准,因此看到一个不遵循该原则并专门使用像素单位的新 CSS 属性,这很奇怪。

我们将要做的就是使用标准的、广泛支持的 CSS 技术重新创建此效果,以便它不仅可以正常工作,而且真正具有响应性。

棘手的部分

我们希望任何超出 clip-path 的内容仅在图像的顶部可见。我们不能使用标准的 CSS overflow 属性,因为它会影响顶部和底部。

Photo of a young woman against a pastel floral pattern cropped to the shape of a circle.
使用 overflow-y: hidden,底部看起来不错,但图像在顶部被裁剪掉了,而那里应该可见溢出内容。

那么,除了 overflowclip-path 之外,我们还有哪些选择?好吧,让我们在 SVG 本身中使用 <clipPath><clipPath> 是一个 SVG 属性,它与新发布且无响应的 clip-path: path 不同。

SVG <clipPath> 元素

SVG <clipPath><path> 元素适应 SVG 元素的坐标系,因此它们天生就具有响应性。随着 SVG 元素的缩放,其坐标系也会缩放,并且根据 各种属性 保持其比例,这些属性涵盖了各种可能的用例。作为额外的好处,在 SVG 上使用 CSS 中的 clip-path 具有 95% 的浏览器支持,与 clip-path: path 相比增加了 13%。

让我们从设置 SVG 元素开始。我使用了 Inkscape 来创建基本的 SVG 标记和剪辑路径,以便于操作。完成此操作后,我通过添加自己的类属性更新了标记。

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 100 120" class="image">
  <defs>
    <clipPath id="maskImage" clipPathUnits="userSpaceOnUse">
      <path d="..." />
    </clipPath>
    <clipPath id="maskBackground" clipPathUnits="userSpaceOnUse">
      <path d="..." />
    </clipPath>
  </defs>
  <g clip-path="url(#maskImage)" transform="translate(0 -7)">
    <!-- Background image -->
    <image clip-path="url(#maskBackground)" width="120" height="120" x="70" y="38" href="..." transform="translate(-90 -31)" />
    <!-- Foreground image -->
    <image width="120" height="144" x="-15" y="0" fill="none" class="image__foreground" href="..." />
  </g>
</svg>
A bright green circle with a bright red shape coming out from the top of it, as if another shape is behind the green circle.
在 Inkscape 中创建的 SVG <clipPath> 元素。绿色元素表示将应用于背景图像的剪辑路径。红色是将应用于背景和前景图像的剪辑路径。

此标记可以轻松地重复用于其他背景和前景图像。我们只需要替换 image 元素内部 href 属性中的 URL。

现在我们可以处理 CSS 中的悬停动画。我们可以使用转换和过渡,确保前景居中,然后在悬停时缩放和移动内容。

.image {
  transform: scale(0.9, 0.9);
  transition: transform 0.2s ease-in;
}

.image__foreground {
  transform-origin: 50% 50%;
  transform: translateY(4px) scale(1, 1);
  transition: transform 0.2s ease-in;
}

.image:hover {
  transform: scale(1, 1);
}

.image:hover .image__foreground {
  transform: translateY(-7px) scale(1.05, 1.05);
}

这是上述 HTML 和 CSS 代码的结果。尝试调整屏幕大小和更改 SVG 元素的尺寸,以查看效果如何随屏幕大小缩放。

看起来很棒!但是,我们还没有完成。我们仍然需要解决一些问题,这些问题是在我们将标记从 HTML 图像元素更改为 SVG 元素后出现的。

SEO 和可访问性

内联 SVG 元素 不会被搜索爬虫索引。如果 SVG 元素是内容的重要组成部分,则您的页面 SEO 可能会受到影响,因为这些图像可能不会被抓取。

我们需要使用常规 <img> 元素的其他标记,该元素使用 CSS 隐藏。以这种方式声明的图像会自动被爬虫抓取,并且我们可以在 图像站点地图 中提供这些图像的链接,以确保爬虫能够找到它们。我们使用 loading="lazy",它允许浏览器决定是否应延迟加载图像。

我们将两个元素都包装在 <figure> 元素中,以便 标记反映这两个图像之间的关系 并将其组合在一起。

<figure>
  <!-- SVG element -->
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 100 120" class="image">
     <!-- ... -->
  </svg>
  <!-- Fallback image -->
  <img src="..." alt="..." loading="lazy" class="fallback-image" />
</figure>

我们还需要解决此效果的一些可访问性问题。更具体地说,我们需要为那些喜欢在没有动画的情况下浏览网页的用户以及使用屏幕阅读器浏览网页的用户进行改进。

使 SVG 元素可访问需要 大量额外的标记。此外,如果我们想删除过渡,则必须覆盖相当多的 CSS 属性,如果我们的选择器特异性不一致,这可能会导致问题。幸运的是,我们新添加的常规图像具有内置的出色可访问性功能,可以轻松地作为那些在没有动画的情况下浏览网页的用户替代方案。

<figure>
  <!-- Animated SVG element -->
  <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -10 100 120" class="image" aria-hidden="true">
    <!-- ... -->
  </svg>

  <!-- Fallback SEO & a11y image -->
  <img src="..." alt="..." loading="lazy" class="fallback-image" />
</figure>

我们需要通过添加 aria-hidden="true" 来隐藏辅助设备中的 SVG 元素,并且我们需要更新 CSS 以包含 prefers-reduced-motion 媒体查询。我们 包含性地隐藏 了没有减少运动偏好的用户的回退图像,同时将其保留供屏幕阅读器等辅助设备使用。

@media (prefers-reduced-motion: no-preference) {
.fallback-image {
  clip: rect(0 0 0 0); 
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap; 
  width: 1px;
  } 
}

@media (prefers-reduced-motion) {
  .image {
    display: none;
  }
}

以下是改进后的结果。

请注意,这些改进不会改变没有设置 prefers-reduced-motion 偏好或未使用屏幕阅读器的用户的效果外观和行为。

总结

开发人员对 clip-path CSS 属性的 path 选项以及新的样式可能性感到兴奋,但许多人发现这些值仅支持像素值后感到不快。这不仅意味着该功能没有响应性,而且严重限制了我们想要使用它的用例数量。

我们将一个有趣的图像弹出悬停效果(使用 clip-path: path)转换为一个 SVG 元素,该元素利用 <clipPath> SVG 元素的响应性来实现相同的效果。但在这样做的过程中,我们引入了一些 SEO 和可访问性问题,我们通过一些额外的标记和回退图像解决了这些问题。

感谢您抽出时间阅读本文!请告诉我这种方法是否让您想出了如何实现自己的效果,以及您是否有任何关于以不同方式处理此效果的建议。