最近在自己用 uniapp 写应用玩,想实现一个软件锁的效果,折腾了不少时间,折腾出来了。

软件锁的界面一般是一个遮罩盖住整个页面,然后进行人脸或指纹验证,验证通过就关闭遮罩这么个逻辑。

我一开始使用 fixed 进行全屏覆盖,但是显然没这么简单:

这个遮罩无法覆盖原生的头部与底部栏。

此方法作罢,继续寻找尝试,最后发现 nvue 页面可以实现全屏幕的覆盖而不受影响原生控件的影响。

为什么呢,因为用这种方式实现的遮罩其实并不是在软件首页,而是跳转至了一个专门用来做遮罩的二级页面,这个二级页面什么也没有(当然要有其他你也可以自己加),没有原生的头部,二级页面自然也没有底部的导航栏。上面这个特点普通的 vue 页面就能做到,但是 nvue 页面还有一个特点是可以将页面的背景图片设置为透明,这是普通页面无法做到的。uniapp 的多级页面显示其实就是一层一层往上叠加页面,把下面的遮盖住,但是如果上面的页面是透明的,那么下面的页面自然就显示出来了。

当然如果没有透明度要求那直接用普通页面做也可以满足。

下面来说下实现过程:

首先新建一个页面,我命名为 mask.nvue (注意扩展名是 nvue)

1
2
3
4
5
<template>
<!-- blurEffect 是 nvue 中的 view 标签独有的属性,可选值有:dark/extralight/light/none,用于背景高斯模糊 -->
<!-- nvue 中 css 的使用限制很大,无法使用 filter 等属性 -->
<view class="mask" blurEffect="dark"></view>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<script>
export default {
onLoad() {
// 进入页面就开启验证
uni.startSoterAuthentication({
requestAuthModes: ['facial', 'fingerPrint'],
success: res => {
// 因为识别有动画效果,所以延迟一秒等动画完成再返回
setTimeout(() => {
uni.navigateBack({})
}, 1000)
}
})
}
}
</script>
1
2
3
4
5
6
7
8
9
<style scoped>
.mask {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
</style>

然后在 pages.json 中添加路由:

1
2
3
4
5
6
7
8
9
10
11
{
"path": "pages/mask/mask",
"style": {
"navigationStyle": "custom",
"app-plus": {
"animationType": "none", // 不使用页面切换动画,否则就会被发现是页面切换
"background": "transparent", //页面设置透明背景
"popGesture": "none" //禁用侧滑返回,防止用户取消验证使用侧滑返回而绕过检验
}
}
}

这样就完成了,使用的话你只要直接在应用进入首页的时候,把页面跳转至验证页面就可以了,验证成功自动返回首页。

1
2
3
4
5
6
// /pages/index/index
onLoad(){
uni.navigateTo({
url: '/pages/mask/mask'
})
}

当然,在跳转前你需要使用 uni.checkIsSoterEnrolledInDevice(OBJECT) 判断当前设置是否已经录入了指纹或者 Face ID并且在软件设置中打开了启用软件锁的开关(如果有的话),否则就不需要验证。

这是触发的效果:

GitHub 仓库地址