天天看點

使用 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);      

繼續閱讀