剪切和遮罩在 CSS 中已经存在了一段时间,并且拥有相当不错的浏览器支持。我最近在一个项目中需要使用剪切技术来显示文本中链接上方的工具提示。
这些工具提示根据其内容有两种设计


您可能认为文本工具提示根本不需要任何剪切。可以在底部放置一个伪元素来添加小凹口,对吗?的确如此!因为工具提示的背景是纯色,所以实际上不需要 CSS 技巧之类的东西。
但第二种设计中对图像进行剪切才是有趣的地方……
以下是我开始任务时脑海中的思考过程。
想法 1:clip-path & polygon
CSS clip-path
属性允许我们使用百分比值定义自定义多边形,以创建所需的路径。
如果路径形状足够简单,此解决方案通常就足够了。在下面的演示中,我使用 calc()
值来确保剪切完全响应,而小三角形无论父元素如何拉伸,大小都保持不变。
.tooltip {
clip-path: polygon(
0% 0%, // Top left point
100% 0%, // Top right point
100% calc(100% - 10px), // Bottom right point
calc(50% + 10px) calc(100% - 10px), // Center right of the triangle
50% 100%, // Tip of the triangle
calc(50% - 10px) calc(100% - 10px), // Center left of the triangle
0% calc(100% - 10px) // Bottom left point
);
}
此解决方案非常简洁,但在我的情况下,它不够好,因为我没有直角三角形的凹口,而是自定义形状。
想法 2:clip-path 和 SVG
使用 SVG 路径似乎是一个不错的解决方案。首先,导出 SVG 剪切路径,然后使用 url(#clipPathId)
值在 CSS 中使用它。
查看下面的演示。您看到路径有任何问题吗?
箭头根据图像比例拉伸。由于小凹口是整个路径形状的一部分,因此它与路径矩形部分的大小一样拉伸。
想法 3:mask-image
现在,我用 CSS mask-image
属性在 CSS 中发现的一件事是:您可以组合遮罩层!把它想象成 CSS 中的 background-image
。您可以在单个元素上应用多个渐变或图像。现在,如果将所有这些层组合起来生成所需的最终遮罩会怎样?
这正是我们将在两层中使用的方式
- 一个大矩形,它覆盖整个块,除了底部的条带(以绿色显示)
- 一个箭头的图像(以粉红色显示)

有了这个解决方案,矩形可以根据工具提示的尺寸拉伸,而箭头将始终保持其固定大小。
下面所有代码和演示都没有前缀,并且演示使用的是 Autoprefixer。在我写这篇文章时,Edge、Chrome 和 Safari 需要前缀。
就像使用背景属性一样,我们将使用三个不同的遮罩属性来定义我们的两层
mask-image
:此属性允许我们用线性背景绘制矩形,并用内联 SVG 绘制箭头。mask-position
:矩形不需要位置(因为它从左上角开始),但箭头需要位于中心底部。mask-repeat
:我们需要避免重复两层;否则,线性渐变重复时会覆盖整个元素。
.tooltip {
mask-image:
linear-gradient(#fff, #fff), /* Rectangle */
url('data:image/svg+xml;utf8,'); /* Bottom arrow mask-position: */
0 0, /* Rectangle */
50% 100%; /* Bottom arrow */
mask-size:
100% calc(100% - 18px), /* Rectangle */
38px 18px; /* Bottom arrow */
mask-repeat: no-repeat;
}
瞧!更改工具提示的尺寸或替换图像,底部箭头将保持其原始比例。
更复杂的形状
让我们变得更花哨,更深入地研究这种技术。我从 iOS 上的 iMessage 应用中获得灵感,并尝试使用这种遮罩技术复制相同的工具提示。

我不得不绘制更多遮罩层来渲染每个圆角
- 四个圆圈,每个角一个(以红色显示)
- 一个水平矩形(以蓝色显示)
- 一个垂直矩形(以绿色显示)
- 一个用于箭头的 SVG(以黄色显示)

完整的代码会稍微长一些,因为我们需要绘制更多层,但逻辑保持不变。角落使用四个径向渐变绘制。要填充矩形,我们需要两个矩形(一个垂直,一个水平)如上所示。最后,我们的小箭头使用内联 SVG。
.tooltip {
--radius: 25px;
mask-image:
radial-gradient(#fff (var(--radius) - 1), #fff0 var(--radius)), /* Top left corner */
radial-gradient(#fff (var(--radius) - 1), #fff0 var(--radius)), /* Top right corner */
radial-gradient(#fff (var(--radius) - 1), #fff0 var(--radius)), /* Bottom left corner */
radial-gradient(#fff (var(--radius) - 1), #fff0 var(--radius)), /* Bottom right corner */
linear-gradient(#fff, #fff), /* Horizontal gradient */
linear-gradient(#fff, #fff), /* Vertical gradient */
url('data:image/svg+xml;utf8,'); /* Bottom right icon */
mask-position:
0 0, /* Top left corner */
100% 0, /* Top right corner */
0 100%, /* Bottom left corner */
100% 100%, /* Bottom right corner */
0 var(--radius), /* Horizontal gradient */
var(--radius) 0, /* Vertical gradient */
100% 100%; /* Bottom right icon */
mask-size:
(var(--radius) * 2) (var(--radius) * 2), /* Top left corner */
(var(--radius) * 2) (var(--radius) * 2), /* Top right corner */
(var(--radius) * 2) (var(--radius) * 2), /* Bottom left corner */
(var(--radius) * 2) (var(--radius) * 2), /* Bottom right corner */
100% calc(100% - #{var(--radius) * 2}), /* Horizontal gradient */
calc(100% - #{var(--radius) * 2}) 100%, /* Vertical gradient */
(39px / 2) (25px / 2); /* Bottom right icon */
mask-repeat: no-repeat;
}
如您所见,我们可以通过使用翻转版本的箭头并将其放置在不同的角落来创建箭头位于左侧或右侧的版本。这个技巧对没有图像的工具提示也很好用。但正如我在本文开头所说,如果您只有一个纯色背景要设置样式,您可能不需要那么多 CSS。
如果您想了解更多关于 CSS 中的剪切和遮罩的信息,CSS-Tricks 上有很多其他优秀的文章值得一看。
我建议对你的最后一个演示进行一个小优化,你可以考虑 CSS 变量以及“最远侧”,这将有助于你减少对半径变量的使用: https://codepen.io/t_afif/pen/yLMwOGX
console.log('HTML + CSS +JS ROCKS');
喜欢它
喜欢这种技术,但请注意,与
clip-path
mask-image
不同,虽然它在视觉上剪切了元素的一部分,但它仍然使它们可以被悬停/点击事件触发。很棒的主意,Louis。到目前为止,我主要使用多边形或 SVG 方法,但确实,当你需要调整元素大小时,最后一个方法非常有用。点赞。
见 https://codepen.io/xboxyan/pen/poeVrqB
如果你只是想做一个简单的三角形,你可以使用 css 的 border 属性(是的,这可能看起来很奇怪,但这很有道理)。
HTML
CSS
想象一下,每个边框必须具有相同的尺寸才能完美地工作。
https://codepen.io/stomperhk/pen/VwbLrbv
最后一个示例不是
mask-border
属性的完美用例吗?该属性允许像border-image
一样对所有 4 个角使用单个 SVG 图像?