CSS mask 实现鼠标跟随镂空效果

6791a921c25ebbb1fab83f2c77aa3395.png

这里可以用伪元素::before来绘制,结构更加精简。用代码实现就是:

.wrap::before{

content:‘’;

position: absolute;

width: 100px;

height: 100px;

border-radius: 50%;

left: 50%;

top: 50%;

transform: translate(-50%,-50%); /默认居中/

box-shadow: 0 0 0 999vw rgba(0, 0, 0, .5); /足够大的投影/

}

可以得到这样的效果:

823478919231b9eab1882c33a05664a5.png

二、借助 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);

})

这样,半透明效果的镂空效果就完成了。

ea75b678af4aae94396bfc309b187ebd.gif

完整代码可以访问:https://codepen.io/xboxyan/pen/VwzRaNZ

三、渐变也能实现半透明的效果


除了上述阴影扩展的方式,CSS 径向渐变也能实现这样的效果。

绘制一个从透明到半透明的渐变,如下:

.wrap::before{

content: ‘’;

position: absolute;

width: 100%;

height: 100%;

left: 0;

top: 0;

background: radial-gradient( circle at center, transparent 50px, rgba(0,0,0,.5) 51px);

}

可以得到这样的效果:

5fb2745c69bc5bcf26ab8eb5414ff3ce.png

然后,把鼠标坐标映射上去就可以了。从这里就可以看出 CSS 变量的好处,无需修改 JS,只需要在CSS中修改渐变中心点的位置就可以实现了

.wrap::before{

background: radial-gradient( circle at calc(var(–x) * 100% )  calc(var(–y) * 100% ), transparent 50px, rgba(0,0,0,.5) 51px);

}

4e5f4649b15f6612ff36ae2f0d817522.gif

四、背景模糊的效果尝试


CSS 中有一个专门针对背景(元素后面区域)的属性:backdrop-filter。

backdrop-filter链接:https://developer.mozilla.org/zh-CN/docs/Web/CSS/backdrop-filter

使用方式和 filter完全一致!

backdrop-filter: blur(10px);

下面是 MDN 中的一个示意效果:

a2f375b3c4c54d171c34f3a8684667df.png

backdrop-filter是让当前元素所在区域后面的内容模糊,要想看到效果,需要元素本身半透明或者完全透明;而filter是让当前元素自身模糊。

有兴趣的可以查看这篇文章:CSS backdrop-filter简介与苹果iOS毛玻璃效果 « 张鑫旭-鑫空间-鑫生活 (zhangxinxu.com)

文章链接:https://www.zhangxinxu.com/wordpress/2019/11/css-backdrop-filter/

需要注意的是,这种模糊与背景的半透明度没有任何关系,哪怕元素本身是透明的,仍然会有效果。例如下面是去除背景后的效果 ,整块都是模糊的。

5d98e62ce1e840b2f433be28c895cd66.png

如果直接运用到上面的例子会怎么样呢?

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: 0 0 0 999vw rgba(0, 0, 0, .5); /足够大的投影/

backdrop-filter: blur(5px)

}

得到效果如下:

b83a10fc4280bf42a8dd369c6f067c55.gif

可以看到圆形区域是模糊的,正好和希望的效果相反。其实也好理解,只有圆形区域才是真实的结构,外面都是阴影,所以最后作用的范围也只有圆形部分。

2. 渐变实现

现在在第二个例子中添加 backdrop-filter

.wrap::before{

content: ‘’;

position: absolute;

width: 100%;

height: 100%;

left: 0;

top: 0;

background: radial-gradient( circle at calc(var(–x) * 100% )  calc(var(–y) * 100% ), transparent 50px, rgba(0,0,0,.5) 51px);

backdrop-filter: blur(5px)

}`

效果如下:

a12d6122f9beee7eb709522858d77cdb.gif

已经全部都模糊了,只是圆形区域外暗一些。由于::before的尺寸占据整个容器,所以整个背后都变模糊了,圆形外部比较暗是因为半透明渐变的影响。

总之还是不能满足我们的需求,需要寻求新的解决方式。

五、CSS MASK 实现镂空


与其说是让圆形区域不模糊,还不如说是把那块区域给镂空了。就好比之前是一整块磨砂玻璃,然后通过 CSS MASK 打了一个圆孔,这样透过圆孔看到后面肯定是清晰的。

可以对第二个例子稍作修改,通过径向渐变绘制一个透明圆,剩余部分都是纯色的遮罩层,示意如下:

b3f08991f172b551dd944f9e06eb3235.png

用代码实现就是

.wrap::before{

content: ‘’;

position: absolute;

width: 100%;

height: 100%;

left: 0;

top: 0;

-webkit-mask: radial-gradient( circle at calc(var(–x, .5) * 100% )  calc(var(–y, .5) * 100% ), transparent 50px, #000 51px);

background: rgba(0,0,0,.3);

backdrop-filter: blur(5px)

}

这样就实现了文章开头的效果:

d519ff0c41f59e6351fdb0204a9ccc77.gif

完整代码可以查看:https://codepen.io/xboxyan/pen/porpoXJ

六、CSS MASK COMPOSITE 实现更丰富的镂空效果


除了使用径向渐变绘制遮罩层以外,还可以通过 CSS MASK COMPOSITE(遮罩合成)的方式来实现。

CSS MASK COMPOSITE 链接:https://developer.mozilla.org/en-US/docs/Web/CSS/mask-composite

标准关键值如下(firefox支持):

/* Keyword values */

mask-composite: add; /* 叠加(默认) */

mask-composite: subtract; /* 减去,排除掉上层的区域 */

mask-composite: intersect; /* 相交,只显示重合的地方 */

mask-composite: exclude; /* 排除,只显示不重合的地方 */

遮罩合成是什么意思呢?可以类比 photoshop 中的形状合成,几乎是一一对应的。

2ea333553a1f9e4f892afbb45facba9b.png

-webkit-mask-composite 与标准下的值有所不同,属性值非常多,如下(chorme 、safari 支持)

-webkit-mask-composite 链接:https://developer.mozilla.org/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; /只显示不重合的地方/

是不是一脸懵?这里做了一个对应的效果图,如果不太熟练,使用的时候知道有这样一个功能,然后对着找就行了。

86a7053be884f2107b354ca242d0474e.png

回到这里,可以绘制一整块背景和一个圆形背景,然后通过遮罩合成排除(mask-composite: exclude)打一个孔就行了,实现如下:

.wrap::before{

content: ‘’;

position: absolute;

width: 100%;

height: 100%;

left: 0;

top: 0;

-webkit-mask: url(“data:image/svg+xml,%3Csvg width=“50” height=“50” viewBox=“0 0 50 50” fill=“none” xmlns=“http://www.w3.org/2000/svg”%3E%3Ccircle cx=“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中的计算,这样也能很好的实现这个效果:

79a47affd5ddeb372bc648300c4efb34.gif

完整代码可以查看:https://codepen.io/xboxyan/pen/ExvMpQB

你可能已经发现,上述例子中的圆是通过 svg 绘制的,还用到了遮罩合成,看着好像更加繁琐了。其实呢,这是一种更加万能的解决方式,可以带来无限的可能性。比如我需要一个星星⭐️的镂空效果,很简单,先通过一个绘制软件画一个。

2d1a2e7c0a866ad2c353fc941c5a1570.png

然后把这段 svg 代码转义一下,这里推荐使用张鑫旭老师的SVG在线压缩合并工具。

链接:https://www.zhangxinxu.com/sp/svgo/

3e1dfecb53dbe16d60a1bc7dfa9fa433.png

替换到刚才的例子中就可以了。

.wrap::before{

content: ‘’;

position: absolute;

width: 100%;

height: 100%;

left: 0;

top: 0;

-webkit-mask: url(“data:image/svg+xml,%3Csvg width=“96” height=“91” viewBox=“0 0 96 91” fill=“none” xmlns=“http://www.w3.org/2000/svg”%3E%3Cpath d=“M48 0l11.226 34.55h36.327l-29.39 21.352L77.39 90.45 48 69.098 18.61 90.451 29.837 55.9.447 34.55h36.327L48 0z” 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)

}

星星镂空实现效果如下:

d3d415d2c47efd6fc354454c9751414a.gif

完整代码可以查看:https://codepen.io/xboxyan/pen/vYJPaVy

再比如一个心形❤,实现效果如下:

2e68df8b0aac7be0f2caa4082be22758.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值