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

7,530 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第29天,点击查看活动详情

背景

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

因为是微信小程序(不是抖音小程序、支付宝小程序等等),产品要求我们使用微信的样式,所以我们项目采用了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);

我们通过全局改写Page对象,一次性改造了所有页面的onLoadonShowonUnload。之后,我们的页面的代码中,完全不用管暗夜模式相关逻辑。

这避免了暗夜模式JS对业务代码的入侵,同时实现了暗夜模式这一功能,真是太方便了~

写在最后

推荐阅读:

我是HullQin,公众号线下聚会游戏的作者(欢迎关注公众号,联系我,交个朋友),转发本文前需获得作者HullQin授权。我独立开发了《联机桌游合集》,是个网页,可以很方便的跟朋友联机玩斗地主、五子棋等游戏,不收费无广告。还独立开发了《合成大西瓜重制版》。还开发了《Dice Crush》参加Game Jam 2022。喜欢可以关注我噢~我有空了会分享做游戏的相关技术,会在这2个专栏里分享:《教你做小游戏》《极致用户体验》