diff --git a/packages/core/src/App.ts b/packages/core/src/App.ts index 3d45d818..e615f145 100644 --- a/packages/core/src/App.ts +++ b/packages/core/src/App.ts @@ -39,9 +39,10 @@ import { DATA_SOURCE_FIELDS_CHANGE_EVENT_PREFIX } from '@tmagic/utils'; import Env from './Env'; import { bindCommonEventListener, isCommonMethod, triggerCommonMethod } from './events'; +import Flexible from './Flexible'; import Node from './Node'; import Page from './Page'; -import { calcFontsize, transformStyle as defaultTransformStyle } from './utils'; +import { transformStyle as defaultTransformStyle } from './utils'; interface AppOptionsConfig { ua?: string; @@ -73,7 +74,6 @@ class App extends EventEmitter implements AppCore { public useMock = false; public platform = 'mobile'; public jsEngine: JsEngine = 'browser'; - public designWidth = 375; public request?: RequestFunction; public components = new Map(); @@ -81,6 +81,8 @@ class App extends EventEmitter implements AppCore { public eventQueueMap: Record = {}; public transformStyle: (style: Record) => Record; + public flexible?: Flexible; + private eventList = new Map<(fromCpt: Node, ...args: any[]) => void, string>(); private dataSourceEventList = new Map void>>(); @@ -97,8 +99,8 @@ class App extends EventEmitter implements AppCore { this.useMock = options.useMock; } - if (typeof options.designWidth !== 'undefined') { - this.setDesignWidth(options.designWidth); + if (this.jsEngine === 'browser') { + this.flexible = new Flexible({ designWidth: options.designWidth }); } this.transformStyle = @@ -120,13 +122,7 @@ class App extends EventEmitter implements AppCore { } public setDesignWidth(width: number) { - this.designWidth = width; - // 根据屏幕大小计算出跟节点的font-size,用于rem样式的适配 - if (this.jsEngine === 'browser') { - this.calcFontsize(); - globalThis.removeEventListener('resize', this.calcFontsize); - globalThis.addEventListener('resize', this.calcFontsize); - } + this.flexible?.setDesignWidth(width); } /** @@ -331,9 +327,8 @@ class App extends EventEmitter implements AppCore { this.removeAllListeners(); this.page = undefined; - if (this.jsEngine === 'browser') { - globalThis.removeEventListener('resize', this.calcFontsize); - } + this.flexible?.destroy(); + this.flexible = undefined; } private bindDataSourceEvents() { @@ -418,10 +413,6 @@ class App extends EventEmitter implements AppCore { this.eventQueueMap[event.eventConfig.to] = [event]; } } - - private calcFontsize() { - calcFontsize(this.designWidth); - } } export default App; diff --git a/packages/core/src/Env.ts b/packages/core/src/Env.ts index 58972641..ee00a825 100644 --- a/packages/core/src/Env.ts +++ b/packages/core/src/Env.ts @@ -27,6 +27,7 @@ class Env { isMqq = false; isWechat = false; isWeb = false; + isOpenHarmony = false; constructor(ua = globalThis.navigator.userAgent, options: Record = {}) { this.isIphone = ua.indexOf('iPhone') >= 0; @@ -47,7 +48,9 @@ class Env { this.isWechat = ua.indexOf('MicroMessenger') >= 0 && ua.indexOf('wxwork') < 0; - this.isWeb = !this.isIos && !this.isAndroid && !/(WebOS|BlackBerry)/.test(ua); + this.isOpenHarmony = ua.includes('OpenHarmony'); + + this.isWeb = !this.isIos && !this.isAndroid && !this.isOpenHarmony && !/(WebOS|BlackBerry)/.test(ua); Object.entries(options).forEach(([key, value]) => { (this as any)[key] = value; diff --git a/packages/core/src/Flexible.ts b/packages/core/src/Flexible.ts new file mode 100644 index 00000000..d59f96f7 --- /dev/null +++ b/packages/core/src/Flexible.ts @@ -0,0 +1,72 @@ +export default class Flexible { + public designWidth = 375; + private tid: NodeJS.Timeout | undefined; + + constructor(options?: { designWidth?: number }) { + if (globalThis.document.readyState === 'complete') { + this.setBodyFontSize(); + } else { + globalThis.document.addEventListener('DOMContentLoaded', this.setBodyFontSize, false); + } + + globalThis.addEventListener('resize', this.resizeHandler, false); + globalThis.addEventListener('pageshow', this.pageshowHandler, false); + + if (typeof options?.designWidth !== 'undefined') { + this.setDesignWidth(options.designWidth); + } + } + + public destroy() { + globalThis.document.removeEventListener('DOMContentLoaded', this.setBodyFontSize, false); + globalThis.removeEventListener('resize', this.resizeHandler, false); + globalThis.removeEventListener('pageshow', this.pageshowHandler, false); + } + + public setDesignWidth(width: number) { + this.designWidth = width; + this.refreshRem(); + } + + public setBodyFontSize() { + globalThis.document.body.style.fontSize = '.12rem'; + } + + public refreshRem() { + const { width } = document.documentElement.getBoundingClientRect(); + const fontSize = width / (this.designWidth / 100); + globalThis.document.documentElement.style.fontSize = `${fontSize}px`; + globalThis.document.documentElement.style.fontSize = `${this.correctRem(fontSize)}px`; + } + + /** + * 纠正由于文字缩放导致的字体大小计算不正确问题 + * @param {number} fontSize + * @returns {number} + */ + public correctRem(fontSize: number) { + const { document } = globalThis; + const d = document.createElement('div'); + d.style.cssText = 'width:1rem;height:0;overflow:hidden;position:absolute;z-index:-1;visibility:hidden;'; + document.documentElement.appendChild(d); + const dw = d.offsetWidth; + document.documentElement.removeChild(d); + if (Math.abs(dw - fontSize) > 1) { + return fontSize ** 2 / dw; + } + return fontSize; + } + + private resizeHandler = () => { + clearTimeout(this.tid); + this.tid = setTimeout(() => { + this.refreshRem(); + }, 300); + }; + + private pageshowHandler = (e: PageTransitionEvent) => { + if (e.persisted) { + this.resizeHandler(); + } + }; +} diff --git a/packages/core/src/utils.ts b/packages/core/src/utils.ts index 8a0590db..5c89ec70 100644 --- a/packages/core/src/utils.ts +++ b/packages/core/src/utils.ts @@ -115,9 +115,3 @@ export const transformStyle = (style: Record | string, jsEngine: Js return results; }; - -export const calcFontsize = (designWidth: number) => { - const { width } = document.documentElement.getBoundingClientRect(); - const fontSize = width / (designWidth / 100); - document.documentElement.style.fontSize = `${fontSize}px`; -};