提交 7f45e824 编写于 作者: D DCloud_LXH

feat(h5): darkmode

上级 90f5c6ba
......@@ -35,5 +35,6 @@ declare namespace NodeJS {
UNI_NVUE_APP_STYLES: string
UNI_APP_CHANGED_FILES: string
UNI_APP_CHANGED_PAGES: string
VUE_APP_DARK_MODE: 'true' | 'false'
}
}
import { runByHBuilderX } from '../hbx/env'
import { parseManifestJsonOnce } from '../json'
import { parseManifestJsonOnce, getPlatformManifestJsonOnce } from '../json'
export function initDefine(stringifyBoolean: boolean = false) {
const manifestJson = parseManifestJsonOnce(process.env.UNI_INPUT_DIR)
const platformManifestJson = getPlatformManifestJsonOnce()
const isRunByHBuilderX = runByHBuilderX()
const isDebug = !!manifestJson.debug
......@@ -49,6 +50,9 @@ export function initDefine(stringifyBoolean: boolean = false) {
'process.env.VUE_APP_PLATFORM': JSON.stringify(
process.env.UNI_PLATFORM || ''
),
'process.env.VUE_APP_DARK_MODE': JSON.stringify(
platformManifestJson.darkmode || false
),
}
}
......
......@@ -124,3 +124,11 @@ export function isEnableTreeShaking(manifestJson: Record<string, any>) {
export function getDevServerOptions(manifestJson: Record<string, any>) {
return extend({}, manifestJson.h5?.devServer)
}
export function getPlatformManifestJsonOnce() {
const platform =
process.env.UNI_PLATFORM === 'app-plus' ? 'app' : process.env.UNI_PLATFORM
return !process.env.UNI_INPUT_DIR
? {}
: parseManifestJsonOnce(process.env.UNI_INPUT_DIR)[platform] || {}
}
......@@ -13,7 +13,7 @@ import { isVueSfcFile } from '../vue/utils'
import { parseVueRequest } from '../vite'
import { EXTNAME_VUE_RE, TEXT_STYLE } from '../constants'
import { initTheme } from './theme'
import { parseManifestJsonOnce } from './manifest'
import { getPlatformManifestJsonOnce } from './manifest'
const pagesCacheSet: Set<string> = new Set()
......@@ -131,9 +131,12 @@ export function normalizePagesJson(
pagesCacheSet.clear()
pagesJson.pages.forEach((page) => pagesCacheSet.add(page.path))
return process.env.UNI_PLATFORM === 'app' || !process.env.UNI_INPUT_DIR
? pagesJson
: initTheme(parseManifestJsonOnce(process.env.UNI_INPUT_DIR), pagesJson)
const manifestJsonPlatform = getPlatformManifestJsonOnce()
if (!manifestJsonPlatform.darkmode) {
pagesJson = initTheme(manifestJsonPlatform, pagesJson)
}
return pagesJson
}
export function validatePages(pagesJson: Record<string, any>, jsonStr: string) {
......@@ -342,7 +345,7 @@ function normalizeNavigationBar(
}
const parsedNavigationBar = initTheme(
parseManifestJsonOnce(process.env.UNI_INPUT_DIR),
getPlatformManifestJsonOnce(),
navigationBar
)
......
import { extend } from '@vue/shared'
import type { Rule, Declaration, Plugin } from 'postcss'
import type { Rule, Declaration, Plugin, Root } from 'postcss'
import postcss from 'postcss'
import selectorParser from 'postcss-selector-parser'
import {
createRpx2Unit,
defaultRpx2Unit,
isBuiltInComponent,
COMPONENT_SELECTOR_PREFIX,
normalizeStyles,
} from '@dcloudio/uni-shared'
import {
parsePagesJsonOnce,
normalizeThemeConfigOnce,
getPlatformManifestJsonOnce,
} from '../../json'
export interface UniAppCssProcessorOptions {
unit?: string // 目标单位,默认rem
unitRatio?: number // 单位转换比例,默认10/320
......@@ -83,6 +91,37 @@ function walkDecls(rpx2unit: ReturnType<typeof createRpx2Unit>) {
}
}
function darkmodeAtRule(root: Root, platform: UniApp.PLATFORM) {
const pageJson = parsePagesJsonOnce(process.env.UNI_PLATFORM, platform)
const filePath = root.source?.input.file || ''
if (
process.env.VUE_APP_DARK_MODE === 'true' &&
filePath.indexOf('App.vue') !== -1
) {
const pageBGC = (pageJson.globalStyle || {}).backgroundColor || ''
if (pageBGC.indexOf('@') === 0) {
;['dark', 'light'].forEach((theme) => {
const { backgroundColor } = normalizeStyles(
{ backgroundColor: pageBGC },
normalizeThemeConfigOnce(getPlatformManifestJsonOnce()),
theme as UniApp.ThemeMode
)
if (backgroundColor !== 'undefined') {
const mediaRoot = postcss.parse(`
@media (prefers-color-scheme: ${theme}) {
body,
uni-page-body {
background-color: ${backgroundColor};
}
}
`)
root.nodes = [...mediaRoot.nodes, ...root.nodes]
}
})
}
}
}
const baiduTags: Record<string, string> = {
navigator: 'nav',
}
......@@ -132,6 +171,9 @@ const uniapp = (opts?: UniAppCssProcessorOptions) => {
OnceExit(root) {
root.walkDecls(walkDecls(rpx2unit))
const rewriteTag = transforms[platform]
if (['h5', 'app'].includes(platform)) {
darkmodeAtRule(root, platform)
}
if (rewriteTag) {
root.walkRules(
walkRules({
......
......@@ -107,6 +107,7 @@
"onSocketMessage",
"onSocketOpen",
"onTabBarMidButtonTap",
"onThemeChange",
"onUnhandledRejection",
"onUserCaptureScreen",
"onWindowResize",
......
import { watch, watchEffect, computed, ref, Ref, onMounted } from 'vue'
import {
watch,
watchEffect,
computed,
ref,
Ref,
onMounted,
reactive,
} from 'vue'
import { RouteLocationNormalizedLoaded, useRoute } from 'vue-router'
import { invokeHook, updatePageCssVar } from '@dcloudio/uni-core'
import {
......@@ -12,6 +20,7 @@ import { useTabBar } from '../../setup/state'
import { cssBackdropFilter } from '../../../service/api/base/canIUse'
import { loadFontFace } from '../../../service/api/ui/loadFontFace'
import { normalizeWindowBottom } from '../../../helpers/cssVar'
import { parseTheme, onThemeChange } from '../../../helpers/theme'
const UNI_TABBAR_ICON_FONT = 'UniTabbarIconFont'
......@@ -19,12 +28,22 @@ export default /*#__PURE__*/ defineSystemComponent({
name: 'TabBar',
setup() {
const visibleList = ref<UniApp.TabBarItemOptions[]>([])
const tabBar = useTabBar()!
const _tabBar = useTabBar()!
const tabBar = reactive(parseTheme(_tabBar))
useVisibleList(tabBar, visibleList)
useTabBarCssVar(tabBar)
const onSwitchTab = useSwitchTab(useRoute(), tabBar, visibleList)
const { style, borderStyle, placeholderStyle } = useTabBarStyle(tabBar)
onThemeChange(() => {
const tabBarStyle = parseTheme(_tabBar)
tabBar.backgroundColor = tabBarStyle.backgroundColor
tabBar.borderStyle = tabBarStyle.borderStyle
tabBar.color = tabBarStyle.color
tabBar.selectedColor = tabBarStyle.selectedColor
tabBar.blurEffect = tabBarStyle.blurEffect
})
onMounted(() => {
if (tabBar.iconfontSrc) {
loadFontFace({
......
import { computed, onBeforeMount, ref } from 'vue'
import { computed, onBeforeMount, ref, reactive } from 'vue'
import { extend, isArray } from '@vue/shared'
import { defineSystemComponent, Input } from '@dcloudio/uni-components'
import { getRealPath } from '@dcloudio/uni-platform'
......@@ -22,6 +22,7 @@ import {
usePageHeadTransparent,
usePageHeadTransparentBackgroundColor,
} from './transparent'
import { parseTheme, onThemeChange } from '../../../helpers/theme'
const ICON_PATHS = {
none: '',
......@@ -43,10 +44,16 @@ export default /*#__PURE__*/ defineSystemComponent({
setup() {
const headRef = ref(null)
const pageMeta = usePageMeta()
const navigationBar = pageMeta.navigationBar
const navigationBar = reactive(parseTheme(pageMeta.navigationBar))
// UniServiceJSBridge.emit('onNavigationBarChange', navigationBar.titleText)
const { clazz, style } = usePageHead(navigationBar)
onThemeChange(() => {
const _navigationBar = parseTheme(pageMeta.navigationBar)
navigationBar.backgroundColor = _navigationBar.backgroundColor
navigationBar.titleColor = _navigationBar.titleColor
})
const buttons = (__UNI_FEATURE_NAVIGATIONBAR_BUTTONS__ &&
usePageHeadButtons(pageMeta)) as PageHeadButtons
......@@ -61,7 +68,7 @@ export default /*#__PURE__*/ defineSystemComponent({
return () => {
// 单页面无需back按钮
const backButtonTsx = __UNI_FEATURE_PAGES__
? createBackButtonTsx(pageMeta)
? createBackButtonTsx(navigationBar, pageMeta.isQuit)
: null
const leftButtonsTsx = __UNI_FEATURE_NAVIGATIONBAR_BUTTONS__
? createButtonsTsx(buttons.left)
......@@ -95,8 +102,10 @@ export default /*#__PURE__*/ defineSystemComponent({
},
})
function createBackButtonTsx(pageMeta: UniApp.PageRouteMeta) {
const { navigationBar, isQuit } = pageMeta
function createBackButtonTsx(
navigationBar: UniApp.PageNavigationBar,
isQuit?: Boolean
) {
if (!isQuit) {
return (
<div class="uni-page-head-btn" onClick={onPageHeadBackButton}>
......
......@@ -21,6 +21,7 @@ import {
ON_RESIZE,
ON_WEB_INVOKE_APP_SERVICE,
WEB_INVOKE_APPSERVICE,
ON_THEME_CHANGE,
} from '@dcloudio/uni-shared'
import { injectAppHooks } from '@dcloudio/uni-api'
import { subscribeViewMethod, unsubscribeViewMethod } from '@dcloudio/uni-core'
......@@ -181,6 +182,7 @@ export function setupApp(comp: any) {
)
window.addEventListener('message', onMessage)
document.addEventListener('visibilitychange', onVisibilityChange)
onThemeChange()
})
return route.query
},
......@@ -233,3 +235,18 @@ function onVisibilityChange() {
emit(ON_APP_ENTER_BACKGROUND)
}
}
function onThemeChange() {
let mediaQueryList: MediaQueryList | null = null
try {
mediaQueryList = window.matchMedia('(prefers-color-scheme: dark)')
} catch (error) {}
if (mediaQueryList) {
mediaQueryList.addEventListener('change', (e) => {
UniServiceJSBridge.emit(ON_THEME_CHANGE, {
theme: e.matches ? 'dark' : 'light',
})
})
}
}
import { getTheme } from '../service/api/base/getBrowserInfo'
import { normalizeStyles, ON_THEME_CHANGE } from '@dcloudio/uni-shared'
export function onThemeChange(
callback: (res: { theme: UniApp.ThemeMode }) => void
) {
if (__uniConfig.darkmode) {
UniServiceJSBridge.on(ON_THEME_CHANGE, callback as UniApp.CallbackFunction)
}
}
export function offThemeChange(callback: UniApp.CallbackFunction) {
UniServiceJSBridge.off(ON_THEME_CHANGE, callback)
}
export function parseTheme<T extends Object>(pageStyle: T): T {
let parsedStyle = {} as T
if (__uniConfig.darkmode) {
parsedStyle = normalizeStyles(
pageStyle,
__uniConfig.themeConfig,
getTheme()
)
}
return __uniConfig.darkmode ? parsedStyle : pageStyle
}
......@@ -34,6 +34,18 @@ function IEVersion() {
}
}
export function getTheme() {
try {
return (
window.matchMedia('(prefers-color-scheme: light)').matches
? 'light'
: 'dark'
) as UniApp.ThemeMode
} catch (error) {
return 'light'
}
}
export function getBrowserInfo() {
let osname
let osversion = '0'
......@@ -205,6 +217,6 @@ export function getBrowserInfo() {
ua,
osname,
osversion,
theme: undefined,
theme: getTheme(),
}
}
......@@ -122,7 +122,7 @@ export const getSystemInfoSync = defineSyncApi<typeof uni.getSystemInfoSync>(
delete (systemInfo as any).screenTop
delete (systemInfo as any).enableDebug
delete (systemInfo as any).theme
if (!__uniConfig.darkmode) delete (systemInfo as any).theme
return sortObject(systemInfo)
}
......
import { ON_THEME_CHANGE } from '@dcloudio/uni-shared'
import { defineOnApi } from '@dcloudio/uni-api'
export const onThemeChange = defineOnApi<typeof uni.onThemeChange>(
ON_THEME_CHANGE,
() => {
UniServiceJSBridge.on(ON_THEME_CHANGE, (res) => {
UniServiceJSBridge.invokeOnCallback(ON_THEME_CHANGE, res)
})
}
)
......@@ -22,6 +22,7 @@ export * from './device/compass'
export * from './device/vibrate'
export * from './device/clipboard'
export * from './device/getWindowInfo'
export * from './device/theme'
export * from './storage/storage'
......
......@@ -11,6 +11,12 @@ body {
height: 100%;
}
@media (prefers-color-scheme: dark) {
body {
color-scheme: dark;
}
}
body {
overflow-x: hidden;
font-size: 16px;
......
......@@ -2,6 +2,8 @@ import fs from 'fs'
import path from 'path'
import { ResolvedConfig } from 'vite'
import { getPlatformManifestJsonOnce } from '@dcloudio/uni-cli-shared'
export function initEnv(config: ResolvedConfig) {
if (!process.env.UNI_PLATFORM) {
process.env.UNI_PLATFORM = 'h5'
......@@ -15,6 +17,8 @@ export function initEnv(config: ResolvedConfig) {
if (!process.env.UNI_OUTPUT_DIR) {
process.env.UNI_OUTPUT_DIR = path.resolve(config.root, config.build.outDir)
}
process.env.VUE_APP_DARK_MODE =
getPlatformManifestJsonOnce().darkmode || false
process.env.BROWSERSLIST_CONFIG = [
path.resolve(process.env.UNI_INPUT_DIR, '.browserslistrc'),
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册