以下是 Ryan Scherf 的客座文章。Ryan 找到了一种巧妙的方法,可以使头像呈现出粗糙、不规则、多变的边缘。有点像它们是用剪刀剪出来的,而剪刀使用者不太擅长使用剪刀。好处在于,这本身就是一种渐进增强技术,并且可以通过纯 CSS 来实现。
对于像 Quirky 这样创意十足且充满活力的品牌,我们一直在思考如何将这种氛围带到网络上。在整个网站中,某些元素都具有“手绘”的外观。如果不使用大量图片,很难获得这种手绘效果。通过一些简单的三角函数和对 CSS clip-path
的基本了解,我们能够相对轻松且高效地实现这一点。

为什么不使用图像蒙版?
例如,在 SVG 中定义的蒙版
img {
mask: url(mask.svg) top left / cover;
}
mask
属性可以引用外部 SVG 或通过 ID 在文档中定义的 SVG。
但是,如果您希望每个显示的头像都有一个唯一的形状,而不是相同的形状怎么办?您可以以编程方式生成许多不同的 SVG 形状来应用。但是,我们可以通过使用 (S)CSS 生成 clip-path
来实现相同的效果并获得这种数学生成。
浏览器的支持情况如何?
当与形状值(如 polygon()
)一起使用时,clip-path
的浏览器支持情况如下:Chrome 24+、Safari 7+、Opera 25+、iOS 7.1+、Android 4.4+。Firefox 仅支持使用在 SVG 中定义的路径的 clip-path
(我们将介绍这一点)。IE 目前尚不支持。
您需要使用 -webkit-clip-path
,因为这是目前唯一支持的方式,但最好也添加 clip-path
。如果 IE 或 Firefox 开始以这种方式支持它,它可能会取消前缀。
简而言之,剪辑路径
您可以为 CSS 剪辑使用一些不同的形状值,但在我们的案例中,polygon
形状是最好的,因为它为我们提供了最多的点和灵活性来创建手绘效果。
您为 polygon()
提供一个 X、Y 点值的列表,例如:<x0> <y0>, <x1> <y1>, ... <xn> <yn>
。这将按照顺序围绕您的点绘制一条路径,并裁剪新创建形状**外部**的任何内容。
/*
This will create a Hexagon, with the first
point being the top tip of the shape
*/
.hexagon {
clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
}
这是一个简单的示例
查看 CodePen 上 Chris Coyier (@chriscoyier) 编写的 使用 clip-path 绘制六边形。
不那么可怕的数学
我们的六边形非常酷,但它还没有真正达到粗略的效果。它非常僵硬——线条太少。将手绘形状视为一系列连接两个点的短线是最好的方法。我们拥有的点越多,创建的短线就越多。实际上,如果点数足够多,我们可以使 polygon
形状变得非常平滑,从而模拟 circle
。
这是一个使用 200 个点的示例
查看 CodePen 上 Chris Coyier (@chriscoyier) 编写的 200 个点。
这些点从哪里来?
这里需要用到一点数学知识。也许你在高中时学过三角函数?在这个课程中,你学习到的一个基本概念是关于**单位圆**的。基本上,有一组公式(给定 pi)可以生成圆周上的任意数量的点。

如果我们将这些线段连接起来,我们会得到一个看起来像这样的形状

仍然有点僵硬,但看起来更像手绘了。
更多点!
我们知道如何使用 clip-path: polygon()
创建六边形和圆形,那么我们如何使它看起来像手绘的呢?
- 调整点数(点数越多,线段长度越短)
- 添加一些 X 和 Y 偏差(因此线段不是均匀的)
让我们在 SCSS 中引入它,并创建一个函数来为我们完成繁重的工作。我们将使用
random()
cos()
sin()
最相关的数学公式是
/*
To generate an arbitrary points on
the unit circle at angle t
*/
$x: cos(t);
$y: sin(t);
将其放入正确的语法中看起来像这样
$w: 160px // Avatar width
$n: 60; // Number of points on the circle
@function sketchAvatar() {
$points: ();
@for $i from 0 through $n {
$points: append($points, ($w / 2) * (1 + cos((2 * pi() * $i / $n))) ($w / 2) * (1 + sin((2 * pi() * $i / $n))), comma);
}
@return $points;
}
这有点复杂。发生的情况是我们从形状的顶部中间开始,并为 60 个等间距的点生成围绕圆圈的一系列点集。
将所有内容与偏差结合起来
上面的代码仍然生成相当平淡且统一的多边形,因此我们必须添加偏差。我们只需在任何方向调整点,即可获得我们正在寻找的偏移效果。$lower
和 $upper
偏差数字可以是任何值,具体取决于您要实现的外观。
$w: 120px; // Overall width
@function sketchAvatar() {
$n: 60; // Number of points
$lower: -80; // Lower variance
$upper: 80; // Upper variance
$points: ();
@for $i from 0 through $n {
$points: append($points, ($w / 2) * (1 + cos((2 * pi() * $i / $n))) + (rand($lower, $upper) / 100) ($w / 2) * (1 + sin((2 * pi() * $i / $n))), comma);
}
@return $points;
}
我们做到了!使用 CSS clip-path: polygon()
创建粗略、独特的头像。
查看 CodePen 上 Chris Coyier (@chriscoyier) 编写的 粗略头像。
在 Firefox 中使其生效
Chris 在这里!我认为既然 Firefox 不支持这种方式,但支持 SVG 语法,我们也许可以对其进行某种程度的 polyfill。
.avatar {
clip-path: polygon( ... ) /* Firefox: nope */
clip-path: url(#clip); /* Firefox: yep */
}
因此,对于每个头像,我…
- 在 CSS 中的伪元素(具有有效伪元素的元素(如父 div)的 content 属性)中输出多边形点。
- 使用 JavaScript 提取该值。
- 重新格式化点以匹配 SVG 格式(例如,不带“px”)。
- 在路径上注入一个新的
<svg>
,并准备好<clipPath>
。
$(".user").each(function(i) {
var path = window.getComputedStyle(this, ':after').getPropertyValue('content');
// clean house
svgPolygonPoints =
path
.replace(/px/g, "")
.replace(/polygon/, "")
.replace(/\(/, "")
.replace(/\)/, "")
.replace(/\;/g, "")
.replace(/"/g, "")
.replace(/\'/g, "");
// To get this to actually work, create a <div> instead with this inside, see below.
var svg = $("<svg width='0' height='0'>")
.append("<defs><clipPath id='clip-" + (i+1) +"'><polygon points='" + svgPolygonPoints +"' /></clipPath></defs>");
$("body").append(svg);
});
它不起作用!哈哈。即使您强制重新绘制头像,由于某些原因,它也不喜欢注入的 SVG。 查看 Amelia 的解决方案
它基本上就像
.user:nth-child(1) {
clip-path: polygon(120.04px 60px ...);
}
变成
<svg width="0" height="0">
<defs>
<clippath id="clip-1">
<polygon points="120.04 60, ... "></polygon>
</clippath>
</defs>
</svg>
很有趣,但对于您可以轻松使用透明图片获得的效果来说,它仍然过于复杂。
但是,如果您希望所有头像都保持一致,您真的可以指望所有用户都上传正确遮罩图像的透明图片吗?您是否希望每次上传自己的头像时都要麻烦地确保它以预期的方式进行遮罩?此解决方案实际上更方便。
我同意……一个有趣的概念证明……但很复杂……对于当今实现全浏览器支持的标准,透明图片是最好的……但对于未来,这是值得关注的事情……
还有 CSS 蒙版……看起来很有希望……但 Firefox 仅支持内联 SVG 作为蒙版源,而其他浏览器(Chrome、Opera、Safari)可以使用 PNG 或 JPG 格式的图像蒙版……IE 仍然完全不支持……(来源:https://caniuse.cn/#feat=css-masks)……
有趣的文章……感谢分享……
一个可能的用例是使用相同的创建的多边形作为图片上的 CSS 形状,使一些文本围绕它流动,而使用透明图片则无法做到这一点。
就像这样
查看 Karsten Buckstegge 在 CodePen 上的 Pen MYYWrg (@MrBambule)。
我喜欢 CSS Tricks 这里卷土重来。有一段时间,这里的事情变得非常严肃,没有那么多有趣但仅略微有用的文章。很高兴技巧又回来了! :)
它不起作用是因为你使用了 JQuery。JQuery 不知道如何在 SVG 命名空间中创建元素,或者哪些元素应该在 SVG 命名空间中。这意味着它为你创建了一堆在 DOM 检查器中看起来正确的元素——它们具有正确的标签名称——但实际上它们在 DOM 中都是
HTMLUnknownElement
。因此你实际上不能将它们用作 SVG 内容。相反,当你将该标记复制粘贴到一个单独的文件中时,该文件将被 HTML5 解析器读取,该解析器将识别所有 <svg> 元素及其子元素为,嗯,
SVGElement
。你有两个选项
document.createElementNS("www.w3.org/2000/svg", "svg")
创建 SVG 元素(及其子元素也是如此)。.innerHTML
属性或.append()
JQuery 方法,(c) 从容器 div 中提取 SVG 元素。我在这里使用选项 2。它使剪辑路径在 Firefox 中工作
http://codepen.io/AmeliaBR/pen/qEBxXW
不错!我今天学到的最重要的事情。
…此外,刚刚更新了 Pen,以便它仅在实际使用 SVG 元素时才注入它们。
几乎,无论如何。如果使用最终的剪辑路径多边形函数(目前是假设的),或者如果识别 webkit-clip-path 属性(Chrome 或 Opera),它不会创建 SVG。但它仍然为 Internet Explorer 创建它们,后者将剪辑路径的基本 url 形式识别为有效的样式声明,但不会将其应用于非 SVG 元素。
这很酷。不幸的是,99.9% 的用户甚至不会注意到或关心边缘看起来不同。因此,你也可以用图像/SVG 的简单方法来做。
不错。非常有趣,不幸的是我从来不擅长数学,但这看起来并不难。
感谢分享。
svgPolygonPoints 被添加到全局窗口对象中。不太好。
顺便说一句,如果你不知道,有一个跨浏览器剪辑路径生成器 http://cssplant.com/clip-path-generator
或者你可以尝试 http://bennettfeely.com/clippy 咳咳 咳咳
不错的帖子,很好的评论。我喜欢计算图像,尤其是在三角函数和随机性发挥作用时。
我认为这在 Windows 8 上的 Chrome 上不起作用。我只是在 CodePen 上将一个图像拖到另一个图像上,它完全重叠了。
喜欢它.. 好的编码.. 在这样的解释之后,你让这变得很容易!