天天看点

使用 weui wxss 小程序如何适配暗夜模式

背景

之前做过小程序适配暗夜模式的需求,分享一下经验。

因为是微信小程序(不是抖音小程序、支付宝小程序等等),产品要求我们使用微信的样式,所以我们项目采用了​​weui-wxss​​。

weui的坑

但是这个weui wxss比较坑,它里面虽然有暗夜模式的样式,但是必须在根结点增加属性 ​

​data-weui-theme="dark"​

​,才能生效。例如:

<view data-weui-theme="dark">

</view>      

为什么说它坑?

因为我们都习惯了使用​

​@media (prefers-color-scheme:dark) {}​

​来判断暗夜模式/白天模式。这是原生的方法,非常方便。

但是weui wxss没用这个写法,都是以​

​data-weui-theme​

​属性为准。而这个属性的值,需要开发者赋值。需要专门弄一个变量作为页面的data,使用API获取当前系统主题后,动态赋值。

因为暗黑模式的实现还不得不耦合JS,就比较恶心了,暗夜模式的JS逻辑侵入了我们业务代码。

具体做法

在app.json配置darkmode

{
  "darkmode": true,
  "themeLocation": "theme.json"
}      

还需要增加文件​

​theme.json​

​​,可以参考官方文档:​​DarkMode 适配指南​​。

每个页面中声明一个theme的变量

Page({
  data: {
    theme: 'light',
  },
});      

默认值用白天模式​

​light​

​​。另一个取值是​

​drak​

​。

每个页面中声明一个themeChangeCallback回调函数

Page({
  themeChangeCallback({ theme }) {
    this.setData({ theme });
    // 这里还需要调用wx.setNavigationBarColor和wx.setBackgroundColor,设置导航栏颜色和背景色      

onLoad时注册事件,监听主题色变化

Page({
  onLoad(options) {
    wx.onThemeChange(this.themeChangeCallback);
  },
});      

onUnload时取消注册事件,关闭监听主题色变化

Page({
  onUnload() {
    wx.offThemeChange(this.themeChangeCallback);
  },
});      

onShow时执行themeChangeCallback回调函数

Page({
  onShow() {
    this.themeChangeCallback();
  },
});      

在wxml中根元素设置属性

<view data-weui-theme="{{theme}}">

</view>      

怎么样,上面的方案是不是要吐了?关键是:每个页面都需要写这么多重复的逻辑!可维护性极差,代码重复率极高。

我们急需一种解决方案,请往下看。

更优雅的方案

建议你先阅读下文章:​​《如何全局重写小程序 Page函数 wx对象?》​​,学会这种方法,我们再来看下方的代码。

function onLoadProxy(onLoad) {
  return function (options) {
    wx.onThemeChange(this.themeChangeCallback);
    if (onLoad) {
      return onLoad.call(this, options);
    }
  };
}

function onUnloadProxy(onUnload) {
  return function () {
    wx.offThemeChange(this.themeChangeCallback);
    if (onUnload) {
      return onUnload.call(this);
    }
  };
}

function onShowProxy(onShow) {
  return function () {
    this.themeChangeCallback();
    if (onShow) {
      return onShow.call(this);
    }
  };
}

const PageProxy = (Page) => function (options) {
  const newOptions = {
    ...options,
    data: { ...options.data, theme: 'light' },
    themeChangeCallback({ theme }) {
      this.setData({ theme });
      // 这里还需要调用wx.setNavigationBarColor和wx.setBackgroundColor,设置导航栏颜色和背景色
    },
    onLoad: onLoadProxy(options.onLoad),
    onUnload: onUnloadProxy(),
    onShow: onShowProxy(),
  };
  Page(newOptions);
};

Page = PageProxy(Page);      

继续阅读