背景
之前做過小程式适配暗夜模式的需求,分享一下經驗。
因為是微信小程式(不是抖音小程式、支付寶小程式等等),産品要求我們使用微信的樣式,是以我們項目采用了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);