one-screen-scale 1.1.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.d.ts CHANGED
@@ -1,46 +1,122 @@
1
- // ===== 核心 =====
2
-
3
- export interface ScaleOptions {
4
- sourceWidth: number
5
- sourceHeight: number
6
- targetWidth?: number
7
- targetHeight?: number
8
- mode?: 'contain' | 'fixed-width'
9
- visualWidth?: number
10
- // screenWidth?: number
11
- }
12
-
13
- export interface ScalerOptions {
14
- mode?: 'contain' | 'fixed-width'
15
- visualWidth?: number
16
- origin?: string
17
- autoResize?: boolean
18
- }
19
-
20
- export interface ScalerInstance {
21
- start: () => number
22
- stop: () => void
23
- refresh: () => number
24
- getScale: () => number
25
- }
26
-
27
- export function calculateScale(options: ScaleOptions): number
28
- export function applyScale(el: HTMLElement | string, scale: number, origin?: string): void
29
- export function getPageScale(el?: HTMLElement | string): number
30
- export function getInverseScale(el?: HTMLElement | string): number
31
- export function createScaler(el: HTMLElement | string, options?: ScalerOptions): ScalerInstance
32
-
33
- // ===== Vue =====
34
-
35
- import type { Ref, Directive } from 'vue'
36
-
37
- export const vFitScreen: Directive<HTMLElement, ScalerOptions>
38
-
39
- export function useFitScreen(
40
- targetRef: Ref<HTMLElement | null> | HTMLElement,
41
- options?: ScalerOptions
42
- ): {
43
- scale: Ref<number>
44
- inverseScale: Ref<number>
45
- refresh: () => void
46
- }
1
+ // ===== 核心 =====
2
+
3
+ export interface ScaleOptions {
4
+ sourceWidth: number
5
+ sourceHeight: number
6
+ targetWidth?: number
7
+ targetHeight?: number
8
+ mode?: 'contain' | 'fixed-width'
9
+ visualWidth?: number
10
+ }
11
+
12
+ export interface ScalerOptions {
13
+ mode?: 'contain' | 'fixed-width'
14
+ visualWidth?: number
15
+ origin?: string
16
+ autoResize?: boolean
17
+ /**
18
+ * 是否自动注入模式所需基础样式(默认 true)
19
+ * - contain: width:100vw; min-height:100vh/100dvh
20
+ * - fixed-width: 暂不注入(宽高由 refresh 行内补偿)
21
+ */
22
+ injectBaseStyles?: boolean
23
+ /**
24
+ * 内容尺寸变化时自动重算(默认 true)。
25
+ * 监听 ResizeObserver / 内部图片 load 等,避免图未加载完就测量导致 scale 偏大。
26
+ */
27
+ observeContent?: boolean
28
+ }
29
+
30
+ export interface ScalerInstance {
31
+ start: () => number
32
+ stop: () => void
33
+ refresh: () => number
34
+ getScale: () => number
35
+ }
36
+
37
+ export const BASE_CLASS: {
38
+ contain: string
39
+ 'fixed-width': string
40
+ compensate: string
41
+ }
42
+
43
+ export const SCALE_CHANGE_EVENT: 'one-screen-scale-change'
44
+ export const CANVAS_RESIZE_EVENT: 'one-screen-scale-canvas-resize'
45
+
46
+ export function calculateScale(options: ScaleOptions): number
47
+ export function applyScale(el: HTMLElement | string, scale: number, origin?: string): void
48
+ export function getPageScale(el?: HTMLElement | string): number
49
+ export function getInverseScale(el?: HTMLElement | string): number
50
+ export function createScaler(el: HTMLElement | string, options?: ScalerOptions): ScalerInstance
51
+ export function ensureBaseStyleSheet(): HTMLStyleElement | null
52
+ export function applyBaseStyles(el: HTMLElement, mode?: 'contain' | 'fixed-width'): string | null
53
+ export function removeBaseStyles(el: HTMLElement): void
54
+ export function applyCompensateStyles(el: HTMLElement): string | null
55
+ export function removeCompensateStyles(el: HTMLElement): void
56
+
57
+ // ===== 组件反缩放补偿(仅抵消页面 v-fit-screen 的 scale;不处理浏览器 Ctrl+/-) =====
58
+
59
+ export interface CompensateRefreshDetail {
60
+ scale: number
61
+ inverseScale: number
62
+ el: HTMLElement
63
+ }
64
+
65
+ export interface CompensateOptions {
66
+ /**
67
+ * 补偿后通知子树 canvas 重绘:探测 ECharts / Chart.js,
68
+ * 并对每个 &lt;canvas&gt; 派发 CANVAS_RESIZE_EVENT。
69
+ */
70
+ resizeCanvas?: boolean
71
+ /** @deprecated 请用 resizeCanvas;传入 echarts 模块时仍表示开启 + 注入库 */
72
+ echarts?: boolean | { getInstanceByDom?: (el: Element) => { resize: (opts?: object) => void } | undefined }
73
+ echartsLib?: { getInstanceByDom?: (el: Element) => { resize: (opts?: object) => void } | undefined }
74
+ onRefresh?: (detail: CompensateRefreshDetail) => void
75
+ }
76
+
77
+ export interface CompensateInstance {
78
+ refresh: () => void
79
+ stop: () => void
80
+ }
81
+
82
+ export function resolvePageScale(fromEl?: HTMLElement | null): number
83
+ export function applyCompensateTransform(el: HTMLElement): {
84
+ scale: number
85
+ inverseScale: number
86
+ }
87
+ export function clearCompensateTransform(el: HTMLElement): void
88
+ export function resizeCanvasUnder(el: HTMLElement, echartsLib?: CompensateOptions['echartsLib']): number
89
+ /** @deprecated 请用 resizeCanvasUnder */
90
+ export function resizeEchartsUnder(el: HTMLElement, echartsLib?: CompensateOptions['echartsLib']): number
91
+ export function createCompensate(
92
+ el: HTMLElement,
93
+ options?: CompensateOptions | (() => CompensateOptions)
94
+ ): CompensateInstance
95
+ /** 全局注入 echarts,供 resizeCanvas 探测(无需每个调用传模块) */
96
+ export function setCompensateEcharts(echarts: CompensateOptions['echartsLib'] | null): void
97
+
98
+ // ===== Vue =====
99
+
100
+ import type { Ref, Directive } from 'vue'
101
+
102
+ export const vFitScreen: Directive<HTMLElement, ScalerOptions | undefined>
103
+ /**
104
+ * 第三方组件反缩放。修饰符 `.resize-canvas` 或 `{ resizeCanvas: true }` 通知子树 canvas 重绘。
105
+ */
106
+ export const vCompensate: Directive<
107
+ HTMLElement,
108
+ CompensateOptions | 'resize-canvas' | 'resizeCanvas' | 'echarts' | undefined
109
+ >
110
+ export const vFitCompensate: Directive<
111
+ HTMLElement,
112
+ CompensateOptions | 'resize-canvas' | 'resizeCanvas' | 'echarts' | undefined
113
+ >
114
+
115
+ export function useFitScreen(
116
+ targetRef: Ref<HTMLElement | null> | HTMLElement,
117
+ options?: ScalerOptions
118
+ ): {
119
+ scale: Ref<number>
120
+ inverseScale: Ref<number>
121
+ refresh: () => void
122
+ }
package/src/index.js CHANGED
@@ -1,14 +1,33 @@
1
- // ---- 核心(框架无关) ----
2
- export {
3
- calculateScale,
4
- applyScale,
5
- getPageScale,
6
- getInverseScale,
7
- createScaler
8
- } from './core.js'
9
-
10
- // ---- Vue 指令 ----
11
- export { vFitScreen } from './directives.js'
12
-
13
- // ---- Vue Composable ----
14
- export { useFitScreen } from './composables.js'
1
+ // ---- 核心(框架无关) ----
2
+ export {
3
+ calculateScale,
4
+ applyScale,
5
+ getPageScale,
6
+ getInverseScale,
7
+ createScaler,
8
+ applyBaseStyles,
9
+ removeBaseStyles,
10
+ applyCompensateStyles,
11
+ removeCompensateStyles,
12
+ ensureBaseStyleSheet,
13
+ BASE_CLASS,
14
+ SCALE_CHANGE_EVENT,
15
+ } from './core.js'
16
+
17
+ // ---- 组件反缩放补偿(仅抵消页面 scale) ----
18
+ export {
19
+ resolvePageScale,
20
+ applyCompensateTransform,
21
+ clearCompensateTransform,
22
+ resizeCanvasUnder,
23
+ resizeEchartsUnder,
24
+ createCompensate,
25
+ setCompensateEcharts,
26
+ CANVAS_RESIZE_EVENT,
27
+ } from './compensate.js'
28
+
29
+ // ---- Vue 指令 ----
30
+ export { vFitScreen, vCompensate, vFitCompensate } from './directives.js'
31
+
32
+ // ---- Vue Composable ----
33
+ export { useFitScreen } from './composables.js'
package/src/react.d.ts CHANGED
@@ -1,19 +1,49 @@
1
- // ===== React =====
2
-
3
- import type { RefObject } from 'react'
4
-
5
- export interface ScalerOptions {
6
- mode?: 'contain' | 'fixed-width'
7
- visualWidth?: number
8
- origin?: string
9
- autoResize?: boolean
10
- }
11
-
12
- export function useFitScreen(
13
- targetRef: RefObject<HTMLElement | null> | HTMLElement,
14
- options?: ScalerOptions
15
- ): {
16
- scale: number
17
- inverseScale: number
18
- refresh: () => void
19
- }
1
+ // ===== React =====
2
+
3
+ import type { RefObject } from 'react'
4
+
5
+ export interface ScalerOptions {
6
+ mode?: 'contain' | 'fixed-width'
7
+ visualWidth?: number
8
+ origin?: string
9
+ autoResize?: boolean
10
+ /**
11
+ * 是否自动注入模式所需基础样式(默认 true)
12
+ * - contain: width:100vw; min-height:100vh/100dvh
13
+ * - fixed-width: 暂不注入(宽高由 refresh 行内补偿)
14
+ */
15
+ injectBaseStyles?: boolean
16
+ /**
17
+ * 内容尺寸变化时自动重算(默认 true)
18
+ */
19
+ observeContent?: boolean
20
+ }
21
+
22
+ export interface CompensateOptions {
23
+ /** 补偿后通知子树 canvas 重绘(ECharts / Chart.js / 自定义事件) */
24
+ resizeCanvas?: boolean
25
+ /** @deprecated 请用 resizeCanvas */
26
+ echarts?: boolean | { getInstanceByDom?: (el: Element) => { resize: (opts?: object) => void } | undefined }
27
+ echartsLib?: { getInstanceByDom?: (el: Element) => { resize: (opts?: object) => void } | undefined }
28
+ onRefresh?: (detail: { scale: number; inverseScale: number; el: HTMLElement }) => void
29
+ }
30
+
31
+ export function useFitScreen(
32
+ targetRef: RefObject<HTMLElement | null> | HTMLElement,
33
+ options?: ScalerOptions
34
+ ): {
35
+ scale: number
36
+ inverseScale: number
37
+ refresh: () => void
38
+ }
39
+
40
+ /**
41
+ * 对应 Vue `v-compensate`:抵消页面 scale 的组件反缩放。
42
+ * 节点晚于首次 effect 出现时会自动补挂;`resizeCanvas` 变化会重建监听。
43
+ */
44
+ export function useCompensate(
45
+ targetRef: RefObject<HTMLElement | null> | HTMLElement,
46
+ options?: CompensateOptions
47
+ ): {
48
+ refresh: () => void
49
+ }
package/src/react.js CHANGED
@@ -1,46 +1,139 @@
1
- /**
2
- * React hook
3
- * useFitScreen(ref, options) → { scale, inverseScale, refresh }
4
- */
5
- import { useRef, useState, useEffect, useCallback } from 'react'
6
- import { createScaler } from './core.js'
7
-
8
- export function useFitScreen(targetRef, options = {}) {
9
- const [scale, setScale] = useState(1)
10
- const [inverseScale, setInverseScale] = useState(1)
11
- const scalerRef = useRef(null)
12
-
13
- useEffect(() => {
14
- const el = targetRef?.current ?? targetRef
15
- if (!el) return
16
-
17
- const scaler = createScaler(el, options)
18
- scalerRef.current = scaler
19
- const s = scaler.start()
20
- setScale(s)
21
- setInverseScale(s > 0 ? 1 / s : 1)
22
-
23
- const onResize = () => {
24
- const s = scaler.getScale()
25
- setScale(s)
26
- setInverseScale(s > 0 ? 1 / s : 1)
27
- }
28
- window.addEventListener('resize', onResize)
29
-
30
- return () => {
31
- window.removeEventListener('resize', onResize)
32
- scaler.stop()
33
- scalerRef.current = null
34
- }
35
- }, [targetRef])
36
-
37
- const refresh = useCallback(() => {
38
- const el = targetRef?.current ?? targetRef
39
- if (!el || !scalerRef.current) return
40
- const s = scalerRef.current.refresh()
41
- setScale(s)
42
- setInverseScale(s > 0 ? 1 / s : 1)
43
- }, [targetRef])
44
-
45
- return { scale, inverseScale, refresh }
46
- }
1
+ /**
2
+ * React hooks
3
+ * useFitScreen(ref, options) → { scale, inverseScale, refresh }
4
+ * useCompensate(ref, options) → { refresh } // 对应 Vue v-compensate
5
+ */
6
+ import { useRef, useState, useEffect, useCallback } from 'react'
7
+ import { createScaler, SCALE_CHANGE_EVENT } from './core.js'
8
+ import { createCompensate } from './compensate.js'
9
+
10
+ function resolveElement(targetRef) {
11
+ if (!targetRef) return null
12
+ if (typeof HTMLElement !== 'undefined' && targetRef instanceof HTMLElement) return targetRef
13
+ if (targetRef.current instanceof HTMLElement) return targetRef.current
14
+ if (targetRef.value instanceof HTMLElement) return targetRef.value
15
+ return null
16
+ }
17
+
18
+ /** ref 第一次 effect 时可能还是空的,等到节点出现再挂。卸载时停掉等待 */
19
+ function waitForElement(targetRef, onReady) {
20
+ let raf = 0
21
+ let timer = 0
22
+ let frames = 0
23
+ let settled = false
24
+
25
+ const finish = () => {
26
+ settled = true
27
+ cancelAnimationFrame(raf)
28
+ clearInterval(timer)
29
+ }
30
+
31
+ const tick = () => {
32
+ if (settled) return
33
+ const el = resolveElement(targetRef)
34
+ if (el) {
35
+ finish()
36
+ onReady(el)
37
+ return
38
+ }
39
+ frames += 1
40
+ if (frames < 60) raf = requestAnimationFrame(tick)
41
+ else if (!timer) timer = setInterval(tick, 200)
42
+ }
43
+
44
+ tick()
45
+ return finish
46
+ }
47
+
48
+ export function useFitScreen(targetRef, options = {}) {
49
+ const [scale, setScale] = useState(1)
50
+ const [inverseScale, setInverseScale] = useState(1)
51
+ const scalerRef = useRef(null)
52
+ const optionsRef = useRef(options)
53
+ optionsRef.current = options
54
+
55
+ useEffect(() => {
56
+ let detach = () => {}
57
+ let disposed = false
58
+
59
+ const cancelWait = waitForElement(targetRef, (el) => {
60
+ if (disposed) return
61
+ const scaler = createScaler(el, optionsRef.current)
62
+ scalerRef.current = scaler
63
+ const s = scaler.start()
64
+ setScale(s)
65
+ setInverseScale(s > 0 ? 1 / s : 1)
66
+
67
+ const onScale = (event) => {
68
+ if (event.detail?.el !== el) return
69
+ setScale(event.detail.scale)
70
+ setInverseScale(event.detail.inverseScale)
71
+ }
72
+ window.addEventListener(SCALE_CHANGE_EVENT, onScale)
73
+ detach = () => {
74
+ window.removeEventListener(SCALE_CHANGE_EVENT, onScale)
75
+ scaler.stop()
76
+ scalerRef.current = null
77
+ }
78
+ })
79
+
80
+ return () => {
81
+ disposed = true
82
+ cancelWait()
83
+ detach()
84
+ }
85
+ }, [targetRef])
86
+
87
+ const refresh = useCallback(() => {
88
+ const el = targetRef?.current ?? targetRef
89
+ if (!el || !scalerRef.current) return
90
+ const s = scalerRef.current.refresh()
91
+ setScale(s)
92
+ setInverseScale(s > 0 ? 1 / s : 1)
93
+ }, [targetRef])
94
+
95
+ return { scale, inverseScale, refresh }
96
+ }
97
+
98
+ /**
99
+ * 第三方组件反缩放补偿(对应 Vue `v-compensate`)。
100
+ * 挂在槽位内层 ref 上;父槽位需有明确宽高。
101
+ *
102
+ * @example
103
+ * const innerRef = useRef(null)
104
+ * useCompensate(innerRef, { resizeCanvas: true })
105
+ */
106
+ export function useCompensate(targetRef, options = {}) {
107
+ const apiRef = useRef(null)
108
+ const optionsRef = useRef(options)
109
+ optionsRef.current = options
110
+ const resizeFlag = Boolean(options?.resizeCanvas || options?.echarts || options?.echartsLib)
111
+ const lib =
112
+ (options && typeof options.echarts === 'object' && options.echarts) ||
113
+ options?.echartsLib ||
114
+ null
115
+
116
+ useEffect(() => {
117
+ let api = null
118
+ let disposed = false
119
+
120
+ const cancelWait = waitForElement(targetRef, (el) => {
121
+ if (disposed) return
122
+ api = createCompensate(el, () => optionsRef.current || {})
123
+ apiRef.current = api
124
+ })
125
+
126
+ return () => {
127
+ disposed = true
128
+ cancelWait()
129
+ api?.stop()
130
+ apiRef.current = null
131
+ }
132
+ }, [targetRef, resizeFlag, lib])
133
+
134
+ const refresh = useCallback(() => {
135
+ apiRef.current?.refresh()
136
+ }, [])
137
+
138
+ return { refresh }
139
+ }