上图是 "QQ 截图" 选择区域时的画面, 可以看到除了中间框选的部分, 其他区域被一层半透明层覆盖 (backdrop), 这种效果不知道专业叫法, 这里称呼它 "镂空遮盖层". 实际业务需求中倒是不多见, 比较常见的是 "页面上的新手引导","视频网站的关灯模式" 等用到这种效果, 通用简单的做法是将内容元素的 z-index 设置大于遮盖层的, 使该元素显示在遮盖层上面. 接下来分享下其他的方法, 可能对某些特殊场景有用.
以下介绍的方法有: 通过多个 DIV 拼接, 单个 DIV 利用 CSS 的属性 border, outline, box-shadow 和混合模式 mix-blend-mode 来实现, 下面分别从视觉和动作来说明.
视觉
首先, 先用样式实现内容上覆盖一层半透明遮盖, 其中某个区域镂空.
DIV 拼接
最原始的方法, 围着镂空区域拼接 4 个 div, 拼接的方式很多, 比如上面的图中用了不同颜色来表示 4 个 div.
web 前端开发学习 Q-q-u-n: 731771211, 分享学习的方法和需要注意的小细节, 不停更新最新的教程和学习方法 (详细的前端项目实战教学视频, PDF)
- <div class="box">
- <div class="text"></div>
- <div class="item-1"></div>
- <div class="item-2"></div>
- <div class="item-3"></div>
- <div class="item-4"></div>
- </div>
- <style>
- .item-1, .item-2, .item-3, .item-4 {
- position: absolute;
- }
- .item-1 {
- top: 0; left: 0;
- background: rgba(0, 0, 0, 0.5);
- width: 450; height: 100px;
- }
- .item-2 {
- top: 0; left: 450; right: 0;
- height: 300px;
- background: rgba(0, 0, 0, 0.5);
- }
- .item-3 {
- top: 300px; left: 150px; bottom: 0; right: 0;
- background: rgba(0, 0, 0, 0.5);
- }
- .item-4 {
- top:100px; left: 0; bottom: 0;
- width: 150px;
- background: rgba(0, 0, 0, 0.5);
- }
- </style>
优点是兼容性好, 缺点是要用多个 DOM 元素去构造遮盖层, 计算麻烦, 并且镂空区域只能是矩形.
border
盒模型包括了 content 和 border, 可以用 content 表示镂空区域, 用 border 表示遮盖层, 并且通过 border-width 来定位镂空区域的位置. 和上面的方法有相似之处.
- <div class="box">
- <div class="text"></div>
- <div class="rect-border"></div>
- </div>
- <style>
- .rect-border {
- position: fixed;
- top: 0; left: 0;
- width: 300px;
- height: 200px;
- border-style: solid;
- border-color: rgba(0, 0, 0, 0.5);
- border-top-width: 100px;
- border-right-width: calc(100vw - 450px);
- border-bottom-width: calc(100vh - 300px);
- border-left-width: 150px;
- }
- </style>
用这种方法需要计算, 有些情况还需要用到 JS, 并且镂空区域只能是矩形 (当然在不使用渐变的情况下).
outline
outline 不占据空间, 不影响本身元素和其他元素, 可以通过对它设置足够大的值来作为遮盖层.
- <div class="box">
- <div class="text"></div>
- <div class="rect-outline"></div>
- </div>
- <style>
- .rect-outline {
- position: absolute;
- left: 150px;
- top: 100px;
- width: 300px;
- height: 200px;
- outline: 3000px solid rgba(0, 0, 0, 0.5);
- }
Web 前端开发学习 Q-q-u-n: 731771211, 分享学习的方法和需要注意的小细节, 不停更新最新的教程和学习方法 (详细的前端项目实战教学视频, PDF)
优点简单方便, 但镂空区域同样只能是矩形.
box-shadow
与 outline 类似, box-shadow 同样不影响元素的大小位置.
- <div class="box">
- <div class="text"></div>
- <div class="rect-shadow"></div>
- </div>
- <style>
- .rect-shadow {
- position: absolute;
- left: 150px;
- top: 100px;
- width: 300px;
- height: 200px;
- border-radius: 10px;
- box-shadow: 0 0 0 3000px rgba(0, 0, 0, 0.5);
- }
- </style>
比上面一种方法好的地方是元素设置 border-radius 是有效的, 所以镂空区域可以是圆角矩形, 椭圆, 圆形等.
mix-blend-mode
利用混合模式, 可以使元素与父元素叠加部分透明, 摆脱了单个元素限制, 使镂空区域可以更自由, 做更复杂的图形, 比如对话气泡框.
- <div class="box">
- <div class="text"></div>
- <div class="overlay">
- <div class="spoltlight">
- </div>
- </div>
- </div>
- <style>
- .overlay {
- position: absolute;
- top: 0; right: 0; bottom: 0; left: 0;
- z-index: 99999;
- background-color: rgba(0, 0, 0, 0.5);
- mix-blend-mode: hard-light;
- pointer-events: auto;
- }
- .spoltlight {
- position: absolute;
- left: 150px;
- top: 100px;
- width: 300px;
- height: 200px;
- border-radius: 10px;
- background-color: gray;
- }
- .spoltlight::after {
- content: "";
- position: absolute;
- top: 100%;
- right: 50px;
- border: 15px solid transparent;
- border-top-color: gray;
- }
- </style>
Web 前端开发学习 Q-q-u-n: 731771211, 分享学习的方法和需要注意的小细节, 不停更新最新的教程和学习方法 (详细的前端项目实战教学视频, PDF)
动作
很多场景下, 还需要对镂空区域下的 DOM 进行点击, 选择等操作.
用 DIV 拼接的方法, 镂空区域本来就是空的, 所以可以直接对下面的 DOM 操作.
其他方法, 因为有真实 DOM 覆盖在内容上面, 所以就要用到 pointer-events:none, 这样鼠标事件可以 "穿透" 该元素作用于下面的内容上.
大多数场景需要阻止操作遮盖部分下面的 DOM, 只对镂空部分进行 "穿透", 所以不能直接对镂空遮盖层设置 pointer-events:none, 应该在鼠标移动到镂空区域时设置 pointer-events:none, 离开镂空区域设置 pointer-events:auto. 注意的是不能在镂空遮盖层上监听 mosuemove, 因为当它被设置为 pointer-events:none 时, 就无法监听了.
对于 outline 和 box-shadow, 因为它们本身就不占据空间, 鼠标事件对它们是没有效果的, 自带 "穿透" 效果, 所以除了要对镂空遮罩层设置 pointer-events:none, 还需要再覆盖一层透明的遮盖层, 同样监听父级容器的 mousemove 事件, 动态的对该透明层设置 pointer-events
混合模式的方法, 原以为通过监听镂空元素的 mouseenter 和 mouseleave 来控制 pointer-events 就可以了, 但同样当 pointer-events:none 时无法监听鼠标事件, 所以也只能通过鼠标坐标来判断.
某些场景下, 如仿 Snipaste 截图效果, 需要对遮盖层下的所有元素监听鼠标进出事件, 这时候对整个镂空遮罩层设置 pointer-events:none 就行了.
来源: http://www.jianshu.com/p/8dbeb5a69403