一文带你了解如何镂空页面

6,010 阅读6分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

前言

在前端应用中, 不管是PC端, 还是移动端. 总有很多时候需要对用户进行引导.

一般, 我们会在我们的应用页面之上覆盖一层黑色蒙层, 然后进行引导.

引导的内容有如何操作页面功能呢, 引导用户如何操作.

进行引导的这个时候, 我们页面上其他功能是不可以使用的.

这样子的场景相信开发移动端的同学们是最熟悉的, PC端的应用场景就相对较少了.

而我发现, 很多同学会直接把连带黑色蒙层的背景一起切成一张图, 直接甩在页面上, 或者把主要的图切出来.

这样子, 固然可以得到一样的效果.

然而却不是最优方案, 同时大张的图片也会增加性能消耗、网络延迟.

本文就带大家来学会如何在前端页面优雅的打洞.

我们先来看几张我做的镂空效果.

WechatIMG345.png

简单图形使用css镂空

第一种方法, 使用CSS进行镂空.

那么在css中如何镂空页面呢?

有同学会想了, 使用4个黑色透明的div拼合, 中间就可以得到一个矩形的镂空.

那么问题来了, 我要镂空圆形, 椭圆, 圆角矩形呢?又该如何去实现呢?

在css中有两个属性:

  • border
  • box-shadow

border是给节点盒子添加边框, box-shadow是为节点盒子添加阴影.

那么, 这两个属性就促使我们有了实现镂空的基础.

当边框或者阴影较大的时候, 就可以达到镂空的效果了

<div class="cricle"></div>
/* 镂空一个圆 */
.cricle{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateY(-50%);
    width: 100px;
    height: 100px;
    border-radius: 50%;
    border: 1000px solid rgba(0,0,0,0.8);
    /* box-shadow: 0 0 0 1000px rgba(0,0,0,0.8); */
}

这样子, 我们的页面就有了一个镂空的圆.

WechatIMG349.png

注意
使用border-radius来控制div形状.

问题又来了, 这样子我们只是镂空了一个, 如果我们需要页面上镂空多个呢?

这个时候我们就需要变通一下, 在每一个镂空节点之上在套一个div盒子, 然后把每个盒子拼合在页面上.

 <!-- 镂空两个 -->
<div class="mask-con-1">
    <div class="round-rect"></div>
</div>
<div class="mask-con-2">
    <div class="cricle"></div>
</div>
.mask-con-1{
    position: absolute;
    left: 0;
    width: 50%;
    height: 100%;
    overflow: hidden;
}
.round-rect{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translateX(-56.8%) translateY(-56.8%);
    width: 120px;
    height: 180px;
    border: 1000px solid rgba(0,0,0,0.8);
    border-radius: 1020px;
}

.mask-con-2{
    position: absolute;
    top: 0;
    right: 0;
    width: 50%;
    height: 100%;
    overflow: hidden;
}

.mask-con-2 .cricle{
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translateX(-50%) translateY(-51%);
    width: 400px;
    height: 400px;
    border-radius: 50%;
    border: 1000px solid rgba(0,0,0,0.8);
}

这样子我们就得到了下图中的镂空效果:

WechatIMG346.png

是不是很有意思, 这样子我们就不需要每次遇到引导就去切图覆盖了.

然而我们需要注意一下的点:

box-shadow具有一定兼容性, 建议使用border.
同时设置border和border-radius时, 圆角会被覆盖, 所以我们设置的时候border-radius在border的基础上加上圆角范围.

我们可以来看下border和border-radius的相爱相杀.

index2.gif

可以看到, 随着border的增大, 内层的圆角慢慢就消失了.

所以我们要设置内层镂空圆角, 就需要在border的基础值上增加.

css镂空也有很多局限, 比如镂空不规则图形就相形见绌

我们还有canvas的方法来镂空.

复杂不规则图形使用canvas镂空

canvas绘制形状的方法, 也可以用来做镂空.
具体如何去做呢?

顺时针画背景, 逆时针画需要镂空的形状

这个就是canvas做镂空的核心所在了.

<canvas id="canvas"></canvas>

在canvas上绘制.

const canvas = document.getElementById("canvas");
canvas.width = 960;
canvas.height = 540;
const ctx = canvas.getContext("2d");

我们先来画一个镂空的矩形试下:

顺时针画一个大背景, 逆时针画一个矩形

ctx.beginPath();
ctx.fillStyle = 'rgba(0,0,0,0.8)';

//顺时针话方
ctx.moveTo(0, 0);
ctx.lineTo(960, 0);
ctx.lineTo(960, 540);
ctx.lineTo(0, 540);

//逆时针镂空
//方块
ctx.moveTo(40, 30);
ctx.lineTo(40, 100);
ctx.lineTo(150, 100);
ctx.lineTo(150, 30);

ctx.fill();

这样子, 我们就得到了镂空的一个矩形框了.

WechatIMG350.png

同样的, 我们再来画一个圆:

// 圆
ctx.moveTo(700, 200);
ctx.arc(700, 200, 150, 0, Math.PI * 2, true);

接着, 我们更换笔触, 画出一些不规则形状:

// 三角
ctx.moveTo(200, 30);
ctx.lineTo(200, 80);
ctx.lineTo(240, 60);

//类椭圆
ctx.moveTo(140, 267);
ctx.quadraticCurveTo(117, 252, 64, 336);
ctx.quadraticCurveTo(12, 423, 40, 438);
ctx.quadraticCurveTo(63, 453, 114, 367);
ctx.quadraticCurveTo(166, 280, 140, 267);

//不规则
ctx.moveTo(197,320);
ctx.lineTo(210, 342);
ctx.lineTo(215, 370);
ctx.lineTo(268, 360);
ctx.lineTo(226, 535);
ctx.lineTo(251, 539);
ctx.lineTo(300, 323);
ctx.lineTo(263, 326);
ctx.lineTo(235, 302);
ctx.lineTo(236, 303);
ctx.lineTo(213, 317);

ctx.fill();

就得到了下面的镂空效果.

WechatIMG345.png

难点应该是那个倾斜的椭圆了

我们用ps画出如下的效果图, 然后得到一系列的点, 将这些点写入二次贝赛尔曲线, 就可以一顺利画出来了.

WechatIMG343.png

这也是一个小技巧.

注意
镂空的图形一定要逆着背景去画.
当然, 镂空矩形还可以使用clearRect的方法

很多游戏引擎里面有个遮罩的概念

很多同学就会用遮罩去做镂空, 其实不可取哈, 遮罩性能较差.

但是不可否认, 遮罩是可以根据图片去镂空一块区域的. 功能还是非常香的.

编外使用svg镂空

最后再来介绍一下svg的镂空方法.

这个镂空不是很常见, svg兼容也不是很好.

看下我写的例子:

<!-- svg镂空 -->
<svg width="960" height="540">
    <defs>
        <mask id="mask">
            <rect class="rect1" x="0" y="0" width="100%" height="100%"/>
            <circle class="mask-to" cx="565" cy="67" r="50"/>
            <circle class="mask-to" cx="434" cy="320" r="100"/>
            <circle class="mask-to" cx="462" cy="209" r="40"/>
            <ellipse cx="141" cy="135" rx="50" ry="100"/>
            <rect class="mask-to" width="100" height="100" x='824' y="142"/>
        </mask>
    </defs>
    <rect class="rect-bg" x="0" y="0" width="100%" height="100%"/>
</svg>
.svg {
    position: absolute;
}
.rect1 {
    stroke:none; 
    fill: #fff;
}
.rect2 {
    stroke: none;
    fill: #fff;
    mask: url(#mask3);
}

.mask-to{
    fill: #000
}

.rect-bg{
    stroke: none; fill: rgba(0, 0, 0, 0.7); mask: url(#mask)
}

这样子也可以得到如下的镂空效果:

WechatIMG344.png

使用svg镂空可以画的图形也是不多.

同时需要注意的是, svg蒙层和背景需要形成对比.所以我这里选择的是黑白两色.

尾声

至此, 三种镂空的方法就介绍到这里了, 大家还有更好的办法吗?

我们需要注意:

如果页面镂空很单纯, 就使用css镂空.
如果涉及到不规则图形, 就使用canvas.
一般游戏中就使用canvas的方法好了.
尽量不去使用svg方法(不用管浏览器, 还是很强大的).

尽量少去请求图片资源, 优化性能是我们需要做的, 不能什么页面都靠图片堆砌.

如果你还有更好的方式, 可以留下你的评论, 感激不尽~

欢迎大家拍砖指正, 笔者功力尚浅, 如有不当之处请斧正.

文章粗浅, 望诸位不吝您的评论和点赞~

注: 本文系作者呕心沥血之作, 转载须声明