原文地址:CSS mask 实现鼠标跟随镂空效果_前端开发博客的博客-CSDN博客
点击下方星标本公众号,实用前端技术文章及时了解
偶然在某思看到这样一个问题,如何使一个div的部分区域变透明而其他部分模糊掉?,最后实现效果是这样的:
进一步,还能实现任意形状的镂空效果:
鼠标经过的地方清晰可见,其他地方则是模糊的。
可能一开始无从下手,不要急,可以先从简单的、类似的效果开始,一步一步尝试,一起看看吧。
一、普通半透明的效果
比如平时开发中碰到更多的可能是一个半透明的效果,有点类似于探照灯(鼠标外面的地方是半透明遮罩,看起来会暗一点)。如下:
那先从这种效果开始吧,假设有这样一个布局:
<divclass="wrap"id="img">
<imgclass="prew"src="/large/008i3skNgy1gubr2sbyqdj60xa0m6tey02.jpg">
</div>
那么如何绘制一个镂空的圆呢?先介绍一种方法。
其实很简单,只需要一个足够大的投影就可以了,原理如下:
这里可以用伪元素::before来绘制,结构更加精简。用代码实现就是:
.wrap::before{
content:'';
position:absolute;
width:100px;
height:100px;
border-radius:50%;
left:50%;
top:50%;
transform:translate(-50%,-50%);/*默认居中*/
box-shadow:000999vwrgba(0,0,0,.5);/*足够大的投影*/
}
可以得到这样的效果:
二、借助 CSS 变量传递鼠标位置
按照以往的经验,可能会在 js 中直接修改元素的 style 属性,类似这样
img.addEventListener('mousemove',(ev)=>{
img.style.left='...';
img.style.top='...';
})
但是这样交互与业务逻辑混杂在一起,不利于后期维护。其实,我们只需要鼠标的坐标,在 CSS 中也能完全实现跟随的效果。
这里借助 CSS 变量,那一切就好办了!假设鼠标的坐标是[--x,--y](范围是[0, 1]),那么遮罩的坐标就可以使用calc计算了。
.wrap::before{
left:calc(var(--x)*100%);
top:calc(var(--y)*100%);
}
然后鼠标坐标的获取可以使用 JS 来计算,也比较容易,如下:
img.addEventListener('mousemove',(ev)=>{
img.style.setProperty('--x',ev.offsetX/ev.target.offsetWidth);
img.style.setProperty('--y',ev.offsetY/ev.target.offsetHeight);
})
这样,半透明效果的镂空效果就完成了。
完整代码可以访问:https://codepen.io/xboxyan/pen/VwzRaNZ
三、渐变也能实现半透明的效果
除了上述阴影扩展的方式,CSS 径向渐变也能实现这样的效果。
绘制一个从透明到半透明的渐变,如下:
.wrap::before{
content:'';
position:absolute;
width:100%;
height:100%;
left:0;
top:0;
background:radial-gradient(circleatcenter,transparent50px,rgba(0,0,0,.5)51px);
}
可以得到这样的效果:
然后,把鼠标坐标映射上去就可以了。从这里就可以看出 CSS 变量的好处,无需修改 JS,只需要在CSS中修改渐变中心点的位置就可以实现了
.wrap::before{
background:radial-gradient(circleatcalc(var(--x)*100%)calc(var(--y)*100%),transparent50px,rgba(0,0,0,.5)51px);
}
四、背景模糊的效果尝试
CSS 中有一个专门针对背景(元素后面区域)的属性:backdrop-filter。
backdrop-filter链接:/zh-CN/docs/Web/CSS/backdrop-filter
使用方式和filter完全一致!
backdrop-filter:blur(10px);
下面是 MDN 中的一个示意效果:
backdrop-filter是让当前元素所在区域后面的内容模糊,要想看到效果,需要元素本身半透明或者完全透明;而filter是让当前元素自身模糊。
有兴趣的可以查看这篇文章:CSS backdrop-filter简介与苹果iOS毛玻璃效果 « 张鑫旭-鑫空间-鑫生活 ()
文章链接:/wordpress//11/css-backdrop-filter/
需要注意的是,这种模糊与背景的半透明度没有任何关系,哪怕元素本身是透明的,仍然会有效果。例如下面是去除背景后的效果 ,整块都是模糊的。
如果直接运用到上面的例子会怎么样呢?
1. 阴影实现
在上面第一个例子中添加backdrop-filter
.wrap::before{
content:'';
position:absolute;
width:100px;
height:100px;
border-radius:50%;
left:50%;
top:50%;
transform:translate(-50%,-50%);/*默认居中*/
box-shadow:000999vwrgba(0,0,0,.5);/*足够大的投影*/
backdrop-filter:blur(5px)
}
得到效果如下:
可以看到圆形区域是模糊的,正好和希望的效果相反。其实也好理解,只有圆形区域才是真实的结构,外面都是阴影,所以最后作用的范围也只有圆形部分。
2. 渐变实现
现在在第二个例子中添加backdrop-filter
.wrap::before{
content:'';
position:absolute;
width:100%;
height:100%;
left:0;
top:0;
background:radial-gradient(circleatcalc(var(--x)*100%)calc(var(--y)*100%),transparent50px,rgba(0,0,0,.5)51px);
backdrop-filter:blur(5px)
}`
效果如下:
已经全部都模糊了,只是圆形区域外暗一些。由于::before的尺寸占据整个容器,所以整个背后都变模糊了,圆形外部比较暗是因为半透明渐变的影响。
总之还是不能满足我们的需求,需要寻求新的解决方式。
五、CSS MASK 实现镂空
与其说是让圆形区域不模糊,还不如说是把那块区域给镂空了。就好比之前是一整块磨砂玻璃,然后通过CSS MASK打了一个圆孔,这样透过圆孔看到后面肯定是清晰的。
可以对第二个例子稍作修改,通过径向渐变绘制一个透明圆,剩余部分都是纯色的遮罩层,示意如下:
用代码实现就是
.wrap::before{
content:'';
position:absolute;
width:100%;
height:100%;
left:0;
top:0;
-webkit-mask:radial-gradient(circleatcalc(var(--x,.5)*100%)calc(var(--y,.5)*100%),transparent50px,#00051px);
background:rgba(0,0,0,.3);
backdrop-filter:blur(5px)
}
这样就实现了文章开头的效果:
完整代码可以查看:https://codepen.io/xboxyan/pen/porpoXJ
六、CSS MASK COMPOSITE 实现更丰富的镂空效果
除了使用径向渐变绘制遮罩层以外,还可以通过 CSSMASKCOMPOSITE(遮罩合成)的方式来实现。
CSS MASK COMPOSITE链接:/en-US/docs/Web/CSS/mask-composite
标准关键值如下(firefox支持):
/*Keywordvalues*/
mask-composite:add;/*叠加(默认)*/
mask-composite:subtract;/*减去,排除掉上层的区域*/
mask-composite:intersect;/*相交,只显示重合的地方*/
mask-composite:exclude;/*排除,只显示不重合的地方*/
遮罩合成是什么意思呢?可以类比 photoshop 中的形状合成,几乎是一一对应的。
-webkit-mask-composite与标准下的值有所不同,属性值非常多,如下(chorme 、safari 支持)
-webkit-mask-composite链接:/en-US/docs/Web/CSS/-webkit-mask-composite
-webkit-mask-composite:clear;/*清除,不显示任何遮罩*/
-webkit-mask-composite:copy;/*只显示上方遮罩,不显示下方遮罩*/
-webkit-mask-composite:source-over;
-webkit-mask-composite:source-in;/*只显示重合的地方*/
-webkit-mask-composite:source-out;/*只显示上方遮罩,重合的地方不显示*/
-webkit-mask-composite:source-atop;
-webkit-mask-composite:destination-over;
-webkit-mask-composite:destination-in;/*只显示重合的地方*/
-webkit-mask-composite:destination-out;/*只显示下方遮罩,重合的地方不显示*/
-webkit-mask-composite:destination-atop;
-webkit-mask-composite:xor;/*只显示不重合的地方*/
是不是一脸懵?这里做了一个对应的效果图,如果不太熟练,使用的时候知道有这样一个功能,然后对着找就行了。
回到这里,可以绘制一整块背景和一个圆形背景,然后通过遮罩合成排除(mask-composite: exclude)打一个孔就行了,实现如下:
.wrap::before{
content:'';
position:absolute;
width:100%;
height:100%;
left:0;
top:0;
-webkit-mask:url("data:image/svg+xml,%3Csvgwidth="50"height="50"viewBox="005050"fill="none"xmlns="/2000/svg"%3E%3Ccirclecx="25"cy="25"r="25"fill="%23C4C4C4"/%3E%3C/svg%3E"),linear-gradient(red,red);
-webkit-mask-size:50px,100%;
-webkit-mask-repeat:no-repeat;
-webkit-mask-position:calc(var(--x,.5)*100%+var(--x,.5)*100px-50px)calc(var(--y,.5)*100%+var(--y,.5)*100px-50px),0;
-webkit-mask-composite:xor;/*只显示不重合的地方,chorem、safari支持*/
mask-composite:exclude;/*排除,只显示不重合的地方,firefox支持*/
background:rgba(0,0,0,.3);
backdrop-filter:blur(5px)
}
需要注意-webkit-mask-position中的计算,这样也能很好的实现这个效果:
完整代码可以查看:https://codepen.io/xboxyan/pen/ExvMpQB
你可能已经发现,上述例子中的圆是通过 svg 绘制的,还用到了遮罩合成,看着好像更加繁琐了。其实呢,这是一种更加万能的解决方式,可以带来无限的可能性。比如我需要一个星星⭐️的镂空效果,很简单,先通过一个绘制软件画一个。
然后把这段 svg 代码转义一下,这里推荐使用张鑫旭老师的SVG在线压缩合并工具。
链接:/sp/svgo/
替换到刚才的例子中就可以了。
.wrap::before{
content:'';
position:absolute;
width:100%;
height:100%;
left:0;
top:0;
-webkit-mask:url("data:image/svg+xml,%3Csvgwidth="96"height="91"viewBox="009691"fill="none"xmlns="/2000/svg"%3E%3Cpathd="M480l11.22634.55h36.327l-29.3921.352L77.3990.454869.09818.6190.45129.83755.9.44734.55h36.327L480z"fill="%23C4C4C4"/%3E%3C/svg%3E"),linear-gradient(red,red);
-webkit-mask-size:50px,100%;
-webkit-mask-repeat:no-repeat;
-webkit-mask-position:calc(var(--x,.5)*100%+var(--x,.5)*100px-50px)calc(var(--y,.5)*100%+var(--y,.5)*100px-50px),0;
-webkit-mask-composite:xor;/*只显示不重合的地方,chorem、safari支持*/
mask-composite:exclude;/*排除,只显示不重合的地方,firefox支持*/
background:rgba(0,0,0,.3);
backdrop-filter:blur(5px)
}
星星镂空实现效果如下:
完整代码可以查看:https://codepen.io/xboxyan/pen/vYJPaVy
再比如一个心形❤,实现效果如下:
完整代码可以查看:https://codepen.io/xboxyan/pen/KKvEBjb
只有想不到,没有做不到
七、总结和说明
以上实现了一个鼠标跟随镂空的效果,从简单到复杂,从单一到通用,虽然借助了一点点 JS ,但是仅仅是“工具人”的角色,交互逻辑全部都由 CSS 完成,下面总结一下:
足够大的阴影是一个实现圆形镂空效果的小技巧
CSS 渐变也能轻易的绘制出圆形镂空背景
借助 CSS 变量可以很方便的利用鼠标位置实现想要的效果
backdrop-filter 可以想象成磨砂玻璃的功能
CSS Mask 可以给磨砂玻璃打孔,实现镂空的效果
借助遮罩合成特性和SVG,可以实现任意形状的镂空效果
CSS MASK 还是非常强大的,有必要还是多掌握一下。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、在看、收藏、转发。