one-screen-scale 1.0.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.js CHANGED
@@ -4,11 +4,30 @@ export {
4
4
  applyScale,
5
5
  getPageScale,
6
6
  getInverseScale,
7
- createScaler
7
+ createScaler,
8
+ applyBaseStyles,
9
+ removeBaseStyles,
10
+ applyCompensateStyles,
11
+ removeCompensateStyles,
12
+ ensureBaseStyleSheet,
13
+ BASE_CLASS,
14
+ SCALE_CHANGE_EVENT,
8
15
  } from './core.js'
9
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
+
10
29
  // ---- Vue 指令 ----
11
- export { vFitScreen } from './directives.js'
30
+ export { vFitScreen, vCompensate, vFitCompensate } from './directives.js'
12
31
 
13
32
  // ---- Vue Composable ----
14
33
  export { useFitScreen } from './composables.js'
package/src/react.d.ts CHANGED
@@ -7,6 +7,25 @@ export interface ScalerOptions {
7
7
  visualWidth?: number
8
8
  origin?: string
9
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
10
29
  }
11
30
 
12
31
  export function useFitScreen(
@@ -17,3 +36,14 @@ export function useFitScreen(
17
36
  inverseScale: number
18
37
  refresh: () => void
19
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,36 +1,86 @@
1
1
  /**
2
- * React hook
2
+ * React hooks
3
3
  * useFitScreen(ref, options) → { scale, inverseScale, refresh }
4
+ * useCompensate(ref, options) → { refresh } // 对应 Vue v-compensate
4
5
  */
5
6
  import { useRef, useState, useEffect, useCallback } from 'react'
6
- import { createScaler } from './core.js'
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
+ }
7
47
 
8
48
  export function useFitScreen(targetRef, options = {}) {
9
49
  const [scale, setScale] = useState(1)
10
50
  const [inverseScale, setInverseScale] = useState(1)
11
51
  const scalerRef = useRef(null)
52
+ const optionsRef = useRef(options)
53
+ optionsRef.current = options
12
54
 
13
55
  useEffect(() => {
14
- const el = targetRef?.current ?? targetRef
15
- if (!el) return
56
+ let detach = () => {}
57
+ let disposed = false
16
58
 
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()
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()
25
64
  setScale(s)
26
65
  setInverseScale(s > 0 ? 1 / s : 1)
27
- }
28
- window.addEventListener('resize', onResize)
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
+ })
29
79
 
30
80
  return () => {
31
- window.removeEventListener('resize', onResize)
32
- scaler.stop()
33
- scalerRef.current = null
81
+ disposed = true
82
+ cancelWait()
83
+ detach()
34
84
  }
35
85
  }, [targetRef])
36
86
 
@@ -44,3 +94,46 @@ export function useFitScreen(targetRef, options = {}) {
44
94
 
45
95
  return { scale, inverseScale, refresh }
46
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
+ }