需求概述:
webveiw的H5页面需配合app实现深色模式,即当app切换皮肤模式时,h5页面也会变化成相应的皮肤模式。
思路:
- app告知h5当前app的皮肤模式。
- h5使用标记来记录皮肤模式。
- h5实现两种皮肤模式的样式,并根据皮肤模式的标记来采用哪种样式。
实现历程
思路其实挺简单的,但是因为公司之前没有相应的项目可以借鉴,实现过程中还是遇到了坑。
思路步骤一实现
第一条,app如何告知h5当前页面的皮肤模式呢?第一时间想到的是使用桥接(dsBridge)来通信,一开始也是这么做的,但是这样做一直会存在一个问题。webview加载url时会首先命中默认的皮肤模式(白色),造成当前皮肤模式如果是黑色的话,加载url会出现页面闪白的情况。之后一直在优化这个问题。但是想想如果采用桥接的方式来实现app告知h5页面皮肤状态的话无法解决页面闪白的问题。页面闪白的原因之一是app与h5通信的耗时,而这个时间是不可能不存在的,所以闪白的情况无解。
既然上面的思路不可行,那么只能换一种方式拿到当前app的皮肤模式,那该怎么获取这个状态呢?webview初始化时有一个user-agent
,这个字段的值是在webview加载url之前就已经存在的,所以可以通过app在user-agent
后面拼接参数来表示当前app的皮肤模式。(eg: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36 theme=dark')。通过在user-agent
后方加上theme=dark
,theme=light
来代表黑色和白色皮肤。而这个字段在浏览器是可以使用window.navigator.userAgent
来获取到它的值。再通过正则表达式来匹配内容就可以知道当前app的皮肤模式。
思路步骤二实现
由于页面实现是使用的Vue。要标记皮肤的模式,最好是在顶层的标签做标记,这样我们页面跳转也不用重新设置标记。那就可以选择Vue实例的挂载点(#app)、body
和html
。设置方法就是在标签内加上属性data-theme=dark
来表示黑色皮肤,data-theme=light
来表示白色皮肤。
// 判断当前的皮肤模式
export function getSystemTheme() {
const lightReg = /\?theme=light/;
const darkReg = /\?theme=dark/;
const u = navigator.userAgent;
const light = !!u.match(lightReg);
const dark = !!u.match(darkReg);
if (light) {
return 'light';
}
if (dark) {
return 'dark';
}
return '';
}
// 设置标记,保存当前皮肤模式 App.vue
beforeCreate() {
const theme = getSystemTheme();
if (theme === 'dark') {
document.body.setAttribute('data-theme', 'dark');
}
}
思路步骤三实现
因为使用了属性来标记皮肤的模式,所以可以使用属性选择器来实现黑色皮肤的样式。这是使用scss的mixin。
$light-colors: (
white: #ffffff,
gapLine: #ECECEC,
border: #DDDDDD,
ground: #F3F3F3,
);
$dark-colors: (
white: #161A22,
gapLine: #2D2F38,
border: #31394D,
ground: #0D1114,
);
@mixin bg-color($k, $important: false) {
@if ($important) {
[data-theme="dark"] & {
background-color: map-get($map: $dark-colors, $key: $k) !important;
}
} @else {
[data-theme="dark"] & {
background-color: map-get($map: $dark-colors, $key: $k);
}
}
}
// 使用
.menu {
background: map-get($light-colors, white);
@include bg-color(white);
}
// 上面四行将转换成 =>
.menu {
background: #ffffff;
}
[data-theme="dark"] .menu {
background-color: #161A22;
}