偶然在某思看到這樣一個問題,如何使一個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 完成,下面總結一下:
- 足夠大的陰影是一個實現圓形鏤空效果的小技巧
- CSS 漸變也能輕易的繪製出圓形鏤空背景
- 藉助 CSS 變數可以很方便的利用滑鼠位置實現想要的效果
- backdrop-filter 可以想象成磨砂玻璃的功能
- CSS Mask 可以給磨砂玻璃打孔,實現鏤空的效果
- 藉助遮罩合成特性和SVG,可以實現任意形狀的鏤空效果
CSS MASK 還是非常強大的,有必要還是多掌握一下。最後,如果覺得還不錯,對你有幫助的話,歡迎點贊、收藏、轉發❤❤❤