CSS 如何实现羽化效果?

语言: CN / TW / HK

theme: fancy highlight: atom-one-dark-reasonable


本文正在参加「金石计划 . 瓜分6万现金大奖」

欢迎关注我的公众号:前端侦探

最近碰到这样一个问题,在一张封面上直接显示书名,可能会存在书名看不太清楚的情况(容易受到背景干扰),如下

image-20221210163633853

为了解决这个问题,设计师提了一个“究极”方案,将书名背后的图片模糊一下,这个在 CSS 中很好实现,仅需backdrop-filter即可

css .name{ backdrop-filter: blur(10px); }

当然,现在模糊是模糊了,但是边缘过于“断层”,导致书名和封面有些“格格不入”,效果如下

image-20221210163925282

如果能够将边缘羽化一下,虚化边缘效果,就可以很好地将书名融入到背景当中

羽化是photoshop术语,羽化原理是令选区内外衔接部分虚化,起到渐变的作用从而达到自然衔接的效果,是ps及其其它版本中的处理图片的重要工具

这是设计最终羽化后的效果,既能保证文字清晰可见,又能避免太过突兀,如下

image-20221210165427443

设计师实现这个很简单,用橡皮擦擦一下就可以了,那么 CSS 呢?下面来探讨一下 CSS 如何实现羽化的效果。

一、羽化的原理

羽化其实就是将边缘变得模糊,在 CSS 中其实就是创建一个边缘模糊的遮罩(mask),也就是需要一种半透明的渐变。

关于遮罩,这里简单介绍一下,基本语法很简单,和background的语法基本一致

css .content{ -webkit-mask: '遮罩图片' ; } /*完整语法*/ .content{ -webkit-mask: '遮罩图片' [position] / [size] ; }

这里的遮罩图片和背景的使用方式基本一致,可以是PNG图片SVG图片、也可以是渐变绘制的图片,同时也支持多图片叠加

遮罩的原理很简单,最终效果只显示不透明的部分,透明部分将不可见,半透明类推

img

先举个圆形的例子,这个比较简单,因为可以直接通过径向渐变创建

比如,下面有一个头像,现在直接放在背景上非常突兀

image-20221210172243765

我们可以将这个头像通过径向渐变绘制出了一个从不透明到透明的遮罩,达到和背景融合的效果

css .head{ -webkit-mask: radial-gradient(closest-side circle,#000 60%, transparent 100%); }

原理是这样的

image-20221210173118913

最后效果如下

image-20221210172534020

当然这是圆形的,如果是矩形的呢?

二、矩形的羽化原理

根据上面的分析,如果希望羽化矩形边缘,需要创建这一个遮罩

image-20221210174740599

那么问题来了,如何创建这一个边缘模糊的矩形呢?貌似没办法直接通过渐变来实现,而且还需要是尺寸自适应的(自动跟随容器尺寸)

如果单纯看这样一个矩形,还是很容易实现的,通过box-shadow即可实现

css .shadow{ width: 200px; height: 200px; background:black; border-radius:10px; box-shadow:0 0 5px black, 0 0 10px black, 0 0 15px black }

根据需求,可以多叠加几层box-shadow,这里叠加了3层,效果如下

image-20221210175454541

但是问题又来了,这样一个 dom 无法直接用作mask-image,那如何处理呢?

三、通过 SVG foreignObject 转换成图片

上面提到,通过 CSS 阴影可以很轻松的实现我们说需要的效果,但可惜现在还是 dom 阶段,所以需要将这个 dom 转换成图像。

在这里,需要借助 SVG 中的foreignObject元素,通过这个元素,可以将 HTML嵌入到SVG中,轻松实现 dom 转图片的效果

有兴趣的可以参考之前这几篇文章

  • CSS、SVG、Canvas对特殊字体的绘制与导出
  • CSS & SVG foreignObject 实现文字镂空波浪动画

原理如下img

回到这里,我们仅需要将上面的结果放到foreignObject元素中,由于需要自适应尺寸,这里的body宽高都是100%,如下

html <svg xmlns="http://www.w3.org/2000/svg"> <foreignObject width="100%" height="100%"> <body class="wrap" xmlns="http://www.w3.org/1999/xhtml"> <style> .wrap{ box-sizing: border-box; margin: 0; height: 100%; padding: 10px; } .shadow{ height: 100%; background:black; border-radius:10px; box-shadow:0 0 5px black, 0 0 10px black, 0 0 15px black } </style> <div class="shadow"></div> </body> </foreignObject> </svg>

这样就得到了一个宽高自适应的SVG图像,无论宽高怎么变化,都是撑满的

image-20221210180715580

别看这么多标签,这其实已经是一张图片,可以直接使用,接下来看看如何运用

四、矩形的羽化

其实上面得到的 SVG可以直接当成一个图片资源,正常使用了,就像这样

css .name{ -webkit-mask: url('./fearher.svg') }

不过,也可以将这个SVG图片转换成内联形式,减少资源依赖,转换后仍然保持自适应特性

这里推荐张鑫旭老师的SVG在线压缩合并工具

转换后就是这个样子

css .name{ -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'%3E%3CforeignObject width='100%25' height='100%25'%3E%3Cbody class='wrap' xmlns='http://www.w3.org/1999/xhtml'%3E%3Cstyle%3E.wrap%7Bbox-sizing:border-box;margin:0;height:100%25;padding:10px%7D.shadow%7Bheight:100%25;background:%23000;border-radius:10px;box-shadow:0 0 5px %23000,0 0 10px %23000,0 0 15px %23000%7D%3C/style%3E%3Cdiv class='shadow'/%3E%3C/body%3E%3C/foreignObject%3E%3C/svg%3E") }

效果如下

Kapture 2022-12-10 at 18.23.20

而且由于尺寸是动态的,换个书名也能完美适应

image-20221210221842455

最后再来对比一下,下面哪个一眼看上去效果最好

image-20221210183113086

完整代码可以查看以下任意链接

五、总结一下

以上就是本文全部内容了,一个还不错的绘制小技巧,最后来回顾一下一些实现要点

  1. 羽化其实就是将边缘变得模糊,在 CSS 中其实就是创建一个边缘模糊的遮罩,也就是需要一种半透明的渐变。
  2. 圆形的边缘很好羽化,因为径向渐变可以直接绘制
  3. 矩形的边缘就稍微复杂点,因为不能直接通过渐变绘制
  4. 边缘模糊在 CSS 中很好实现,用 box-shadow 就行了
  5. 可以通过 SVG foreignObject 将 dom 转换成图片
  6. SVG 转换成内联形式,好处是减少资源依赖,转换后仍然保持自适应特性

当然本文最重要的一点是,如何在 HTML 严重受限的背景上绘制一些常见的图形,以后碰到类似的需求也可以朝这个方向去考虑,最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤

欢迎关注我的公众号:前端侦探