你還在用圖片做引導蒙層?
- 2019 年 12 月 16 日
- 筆記
首先認識一下引導蒙層,如下圖頁面是一個蒙層,會在某個局部位置高亮我們需要重點突出的內容:

蒙層有兩個核心元素,引導內容區域(即需要重點突出的內容元素)和半透明蒙層元素。
都9012年了,如果還在用圖片做蒙層,是不是太low了。
圖片引導蒙層有幾大缺點:
- 圖片大,影響載入
- 圖片的內容都是假的,和真實的底部內容沒對上
- 全螢幕蒙層圖片,圖片的寬高和螢幕寬高不一致,顯示兩邊留黑,或者有壓縮的效果。
- 圖片的引導位置不能點擊。
- low ?not cool ?
本文講述六種思路來實現引導蒙層
- z-index實現蒙層
- 動態opacity實現
- border實現
- box-shadow實現
- 節點複製實現
- canvas實現
以上六種引導蒙層實現思路,在一定情況下都能滿足業務需求,從不同角度來實現了引導蒙層。z-index最簡單,canvas最靈活,就個人而言,更加喜歡骨架屏式的動態opacity蒙層實現,更有趣更酷!!!
思路一:使用z-index
- 新增一個div,設置為半透明區域,大小覆蓋整個頁面
- 半透明蒙層區域z-index大於頁面元素
- 引導內容區域大於半透明蒙層區域z-index
這個好理解,頁面元素都是有層級的,我們只需要把引導內容區域設置為最頂層的層級,在引導內容區域之下設置一個遮罩層,其他內容元素的z-index都地域這個遮罩層即可。我們來看一個簡單例子。
.z1{ position:absolute; left:50px; top:50px; width:50px; height:50px; background:blue; z-index:1; } .z2{ position:absolute; left:60px; top:60px; width:50px; height:50px; background:red; z-index:2; } .z3{ position:absolute; left:70px; top:70px; width:50px; height:50px; background:yellow; z-index:3; }
我們修改一下z2的樣式。
.z2{ position:absolute; left:50px; top:50px; width:50px; height:50px; background:black; opacity:0.5; z-index:2; animation:z_index 2s linear infinite alternate; } @keyframes z_index { from { left:50px; top:50px; width:50px; height:50px; } to { left:0px; top:0px; width:200px; height:200px; } }
只要在布局頁面元素的時候,把需要做蒙層的元素確定好,配合js,動態的設置元素的z-index + opacity,就可以很好的做到頁面的引導蒙層效果。
思路二:使用opacity將非蒙層元素半透明
- 引導內容區域無需改動
- 頁面其他節點元素半透明
我們不再新增蒙層,而是完全操作頁面節點,將需要遮罩的節點都設置為半透明,引導蒙層顯現內容則完全顯示出來。頁面的效果和蒙層不太一樣,對於空白地方,我們仍然是完全顯示,只是將有內容的元素給半透明,類似骨架屏的效果。 為了演示效果,我們看如下例子: 頁面設置6個元素。
<div class="wrap"> <div class="z z1"></div> <div class="z z2"></div> <div class="z z3"></div> <div class="z z4"></div> <div class="z z5"></div> <div class="z z6"></div> </div>
將元素內容用flex並排布局。
.wrap{ display:flex; flex-wrap:wrap; width:150px; } .z{ width:50px; height:50px; transition:all 1s; } .z1{ background:blue; } .z2{ background:black; } .z3{ background:yellow; } .z4{ background:red; } .z5{ background:green; } .z6{ background:orange; }使用js操作,依次半透明其他元素,顯示當前元素來模擬蒙層。
let arry = Array.from(document.querySelectorAll(".z")); let index = -1; let direct = 1; setInterval(()=>{ if(index>=5) direct = -1; else if(index<=0) direct = 1; index = index+direct; arry.forEach((d,i)=>{ d.style.opacity = 1; }); setTimeout(()=>{ arry.forEach((d,i)=>{ if(i==index) return; d.style.opacity = 0.1; }); },1000); },2000)
看了這個例子,我們清晰的看到這個引導蒙層的實現過程。這種引導蒙層其實更好玩有趣,有點類似當前流行的骨架屏,其他已有元素需要遮罩的內容就是骨架屏的灰色部分,需要顯現的就是重點的蒙層內容。 有趣!!!
思路三:使用border的方式來實現
沒錯,就是普遍不能在普遍的border了,且看如下:
div { border:1px solid #red; }那用border怎麼實現引導蒙層呢?
1、先了解下三角形
先看一個簡單的例子:
<div class="border_1"></div>
.border_1{ width: 100px; height: 100px; border-top:50px solid red; border-right: 50px solid transparent; border-bottom: 50px solid transparent; border-left: 50px solid transparent; box-sizing:border-box; }
實現了一個倒三角,這個應用場景是不是就比較多了,很多tips的指引,標註等都會用到。我就看到過這種倒三角使用一張圖片代替的做法。 仔細看這段程式碼,主要有實現了三點:
- 四邊都設置了邊框
- 寬高都為100px,即上下、左右表框之和,其實小於等於這個值都行。
- 只有頂部邊框是紅色,其他邊框是透明的。
為了理解上面的實現,我們來看下如下程式碼:
.border_2{ width: 100px; height: 100px; background-color:green; border-style:solid; border-color:red yellow blue black; border-width:50px; animation:border_ani 2s linear infinite alternate; box-sizing:border-box; } @keyframes border_ani { from { border-width:50px; } to { border-width:0; } }
從圖中我們可以清晰的看到,隨著border-width的變化,整個div的綠色背景在跟隨變化。
- 當border-width=0的時候,整個頁面只有綠色背景,即都是內容的大小
- 當border-width=50的時候,整個div的大小都被border給充滿了,上下左右均分1/4,就是四個倒三角。
這樣我們就清晰的能得到:
當border-right,border-left,border-bottom都transparent透明,border-top是紅色的時候,所看到的就是一個倒三角。
同理我們還可以設置邊框的大小不一致,可以實現斜三角:
.border_3{ width: 0; height: 0; border-top:30px solid red; border-right: 10px solid transparent; border-bottom: 20px solid transparent; border-left: 100px solid transparent; box-sizing:border-box; }
還可以實現工作中經常碰到的梯形:
.border_4{ width: 150px; height: 150px; border-top:50px solid red; border-right: 50px solid transparent; border-bottom: 50px solid transparent; border-left: 50px solid transparent; box-sizing:border-box; }
好了,這裡不累贅了,感興趣的可以各種嘗試。遇到這種簡單邊線圖,就不要動不動使用圖片了。
2、再看實現引導蒙層
- 新增一個div,作為蒙層元素
- div中間大小和引導內容元素大小完全一致,且位置恰好重疊
- div的border設置為半透明且無限放大
了解了上面的三角形的實現之後,估計你也能想出怎麼做引導蒙層了。一個div有四個邊框,如果我們把邊框都設置成半透明,然後中間的區域(上面border_2的green)設置成全透明會不就可以實現區域引導蒙層了嗎?然後再把邊框設置成超過螢幕的大小呢,就是全景引導蒙層了!
.border_5{ width: 150px; height: 150px; border-top:50px solid rgba(0,0,0,.5); border-right: 50px solid rgba(0,0,0,.5); border-bottom: 50px solid rgba(0,0,0,.5); border-left: 50px solid rgba(0,0,0,.5); box-sizing:border-box; }
這是一個150px的區域蒙層,我們看下如下示例就能很明白了:
.border_6{ width: 20px; height: 20px; border-style:solid; border-color:rgba(0,0,0,.5); border-width:20px; animation:border_ani 2s linear infinite alternate; box-sizing:content-box; } @keyframes border_ani { from { border-width:20px; } to { border-width:100px; } }
當然,我們還可以設置border-radius來實現圓形的蒙層區域,如下:

當然,這裡的邊框大小值都是寫死的,具體實現需要根據頁面內容修改或者動態修改即可。
如果是橢圓呢?
總結:
border可以實現各種邊線的形狀,可以實現引導蒙層,頁面指定區域透明,之外的都半透明來實現即可。
思路四、使用box-shadow來實現
- 新增一個div,作為蒙層元素
- div大小和內容元素大小完全一致,且位置恰好重疊
- div的box-shadow的陰影尺寸設置為半透明且設置為比較大的約2000px大小
box-shadow,大夥都不陌生,就是盒子的陰影,我們先來了解下它的基本用法:
.boxshadow_1{ width:50px; height:50px; background:blue; box-shadow: 10px 10px 5px 4px #000; }
在寬高為50px的div,它的陰影水平和垂直都是10px,陰影模糊距離是5px,陰影的尺寸是4px,陰影是#000的顏色(這裡給body增加了一個yellow的背景色以便於區分)。
首先我們把陰影透明:
.boxshadow_2{ width:50px; height:50px; background:blue; box-shadow: 10px 10px 5px 4px rgba(0,0,0,.5); }
那怎麼讓陰影遮蓋整個頁面呢?
- 陰影的水平和垂直距離是指距離原dev的距離,這個調整達不到效果,只會讓陰影更多的偏離元素。
- 陰影的模糊距離指陰影的邊緣漸變模糊的距離,距離越長,只會讓漸變模糊加長,蒙層大小不會變。
- 陰影的尺寸,這個是指多大的陰影,那我們將陰影尺寸設置很大呢?是的,就是這個了
看如下的例子,我們調整陰影的尺寸:
.boxshadow_3 { width:50px; height:50px; background:blue; box-shadow: 0px 0px 5px 0px rgba(0,0,0,.5); animation:box_ani 2s linear infinite alternate; } @keyframes box_ani { from { box-shadow: 10px 10px 5px 0px rgba(0,0,0,.5); } to { box-shadow: 10px 10px 5px 100px rgba(0,0,0,.5); } }如上,我們只需要把陰影尺寸加大就可以實現引導蒙層了。 如果需要引導蒙層狀態下還能響應事件呢?只需要加一個pointer-events屬性即可。
box-shadow的陰影距離切勿盲目設置過大,經過測試這個值如果過大,比如4000px,在部分手機上陰影無法顯示出來。經過實踐,設置為2000px為佳。
思路五:使用頁面節點複製
- 新增兩個div,一個半透明蒙層元素和一個蒙層內容區域
- 將頁面節點引導內容拷貝到蒙層內容區域
- 將蒙層內容區域的大小和位置與原節點引導內容完全重合
頁面內容已經做好了,我們需要引導蒙層來顯示某個元素,那麼將元素複製到最外層,頂層增加一層蒙層來實現,需要突出的引導內容在蒙層之上即可實現。
<div class="content one">我是第一個div,我是第一個div</div> <div class="content two">我是第二個div,我是第二個div</div> <div class="content three">我是第三個div,我是第三個div</div> <div class="content four">我是第四個div,我是第四個div</div> <div class="mask"></div> <div id="maskContent"></div>
這裡設置了一個固定蒙層,和一個固定的蒙層內容元素,我們只需要填充即可。
.content{ padding:10px; z-index:0; } .mask{ position:fixed; left:0; top:0; width:100%; height:100%; background:rgba(0,0,0,.8); z-index:900 } #maskContent{ position:fixed; z-index:999; display:inline-block; background-color: #fff; }這裡內容區域都是0,然後mask是900,我們的蒙層元素是999,就是最上層了。
function renderContent(cls){ let targetNode = document.querySelector(`.${cls}`); let maskContent = document.getElementById("maskContent"); maskContent.innerHTML = targetNode.outerHTML; let pos = targetNode.getBoundingClientRect(); maskContent.style.top=pos.top+"px"; maskContent.style.left=pos.left+"px"; maskContent.style.width=pos.width+"px"; maskContent.style.height=pos.height+"px"; } let i = 0; setInterval(()=>{ renderContent(['one','two','three','four'][i]); if(++i>=4) i = 0; },1000)這裡為了演示效果,增加了一個定時器改變不同的遮罩層。易於理解,看下效果:

思路六:使用canvas實現
- 新增一個canvas,繪製兩次圖形
- 第一次:繪製一個全螢幕的半透明陰影
- 第二次:使用xor繪製一個和引導內容區域的大小位置完全重合的區域
第二次繪製的內容區域和第一次重疊,使用xor,所以會透明,該引導內容區域就會完全顯示出來,這就是我們想要的效果了。
使用canvas的globalCompositeOperation屬性來實現,內容參考http://www.tutorialspoint.com/html5/canvas_composition.htm
重點看xor:Shapes are made transparent where both overlap and drawn normal everywhere else. 翻譯:canvas繪製的形狀在重疊處都會變成透明的,非重疊處的其他任何地方都正常繪製內容。
所以我們就可以在canvas裡面繪製一個canvas蒙層,然後在蒙層中需要顯示的內容用xor來繪製重疊,然後重疊內容就會被透明,那麼這個透明區域的內容就是我們想要的引導蒙層突出內容區域。具體看實例:
<div class="content one">我是第一個div,我是第一個div</div> <div class="content two">我是第二個div,我是第二個div</div> <div class="content three">我是第三個div,我是第三個div</div> <div class="conteent four">我是第四個div,我是第四個div</div> <canvas id="mask"></canvas>
頁面增加一個canvas節點。將canvas整體設置成半透明,然後再用xor來實現內容的繪製。
function mask(cls){ let targetNode = document.querySelector(`.${cls}`); let pos = targetNode.getBoundingClientRect(); let canvas = document.getElementById("mask"); let width = window.innerWidth; let height = window.innerHeight;; canvas.setAttribute("width", width); canvas.setAttribute("height",height); var ctx = canvas.getContext("2d"); ctx.globalCompositeOperation = 'source-over'; ctx.fillStyle ='rgba(255, 255, 255, 0.9)'; ctx.fillRect(0, 0, width, height); ctx.fill(); ctx.fillStyle ='white'; ctx.globalCompositeOperation="xor"; ctx.fillRect(pos.left,pos.top,pos.width,pos.height); ctx.fill(); } let array = ['one','two','three','four']; let i = 0; setInterval(()=>{ mask(array[i]); i++; if(i>=4) i = 0; },1000)
看完以上實現,你最喜歡哪種實現方式呢?
