乐趣区

关于css:CSS-mask-实现鼠标跟随镂空效果

偶尔在某思看到这样一个问题,如何使一个 div 的局部区域变通明而其余局部含糊掉?,最初实现成果是这样的

进一步,还能实现任意形态的镂空成果

鼠标通过的中央清晰可见,其余中央则是含糊的。

可能一开始无从下手,不要急,能够先从简略的、相似的成果开始,一步一步尝试,一起看看吧。

一、一般半透明的成果

比方平时开发中碰到更多的可能是一个半透明的成果,有点相似于探照灯(鼠标里面的中央是半透明遮罩,看起来会暗一点)。如下:

那先从这种成果开始吧,假如有这样一个布局:

<div class="wrap" id="img">
    <img class="prew" src="https://tva1.sinaimg.cn/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: 0 0 0 999vw rgba(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);
})

这样,半透明成果的镂空成果就实现了

残缺代码能够拜访:backdrop-shadow (codepen.io)

三、突变也能实现半透明的成果

除了上述暗影扩大的形式,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);
}

能够失去这样的成果

而后,把鼠标坐标映射下来就能够了。从这里就能够看出 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);
}

四、背景含糊的成果尝试

CSS 中有一个专门针对背景(元素前面区域)的属性:backdrop-filter。应用形式和 filter完全一致!

backdrop-filter: blur(10px);

上面是 MDN 中的一个示意成果

backdrop-filter是让以后 元素所在区域前面的内容 含糊,要想看到成果,须要元素自身半透明或者齐全通明;而 filter 是让以后 元素本身 含糊。有趣味的能够查看这篇文章:CSS backdrop-filter 简介与苹果 iOS 毛玻璃成果 « 张鑫旭 - 鑫空间 - 鑫生存 (zhangxinxu.com)

须要留神的是,这种 含糊与背景的半透明度没有任何关系,哪怕元素自身是通明的,依然会有成果。例如上面是去除背景后的成果,整块都是含糊的

如果间接使用到下面的例子会怎么样呢?

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)
}

失去成果如下

能够看到圆形区域是含糊的,正好和心愿的成果相同。其实也好了解,只有圆形区域才是实在的构造,里面都是暗影,所以最初作用的范畴也只有圆形局部

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)
}

成果如下

曾经全副都含糊了,只是圆形区域外暗一些。因为 ::before 的尺寸占据整个容器,所以整个背地都变含糊了,圆形内部比拟暗是因为半透明突变的影响。

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

五、CSS MASK 实现镂空

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

能够对第二个例子稍作批改,通过径向突变绘制一个通明圆,残余局部都是纯色的遮罩层,示意如下

用代码实现就是

.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)
}

这样就实现了文章结尾的成果

残缺代码能够查看:backdrop-mask (codepen.io)

六、CSS MASK COMPOSITE 实现更丰盛的镂空成果

除了应用径向突变绘制遮罩层以外,还能够通过 CSS MASK COMPOSITE(遮罩合成)的形式来实现。规范要害值如下(firefox 反对):

/* Keyword values */
mask-composite: add; /* 叠加(默认)*/
mask-composite: subtract; /* 减去,排除掉下层的区域 */
mask-composite: intersect; /* 相交,只显示重合的中央 */
mask-composite: exclude; /* 排除,只显示不重合的中央 */

遮罩合成是什么意思呢?能够类比 photoshop 中的形态合成,简直是一一对应的

-webkit-mask-composite 与规范下的值有所不同,属性值十分多,如下(chorme、safari 反对)

-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,%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 中的计算,这样也能很好的实现这个成果

残缺代码能够查看:backdrop-mask-composite (codepen.io)

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

而后把这段 svg 代码本义一下,这里举荐应用张鑫旭老师的 SVG 在线压缩合并工具

替换到方才的例子中就能够了

.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)
}

星星镂空实现成果如下

残缺代码能够查看:backdrop-star (codepen.io)

再比方一个心形❤,实现成果如下

残缺代码能够查看:backdrop-heart (codepen.io)

只有想不到,没有做不到

七、总结和阐明

以上实现了一个鼠标追随镂空的成果,从简略到简单,从繁多到通用,尽管借助了一点点 JS,然而仅仅是“工具人”的角色,交互逻辑全副都由 CSS 实现,上面总结一下:

  1. 足够大的暗影是一个实现圆形镂空成果的小技巧
  2. CSS 突变也能轻易的绘制出圆形镂空背景
  3. 借助 CSS 变量能够很不便的利用鼠标地位实现想要的成果
  4. backdrop-filter 能够设想成磨砂玻璃的性能
  5. CSS Mask 能够给磨砂玻璃打孔,实现镂空的成果
  6. 借助遮罩合成个性和 SVG,能够实现任意形态的镂空成果

CSS MASK 还是十分弱小的,有必要还是多把握一下。最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤

退出移动版