one-screen-scale 1.1.0 → 1.3.1
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/README.md +356 -186
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +122 -46
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/react.cjs.js +1 -1
- package/dist/react.cjs.js.map +1 -1
- package/dist/react.d.ts +49 -19
- package/dist/react.esm.js +1 -1
- package/dist/react.esm.js.map +1 -1
- package/package.json +71 -71
- package/src/baseStyles.js +107 -0
- package/src/compensate.js +320 -0
- package/src/composables.js +13 -11
- package/src/core.js +317 -160
- package/src/directives.js +144 -34
- package/src/index.d.ts +122 -46
- package/src/index.js +33 -14
- package/src/react.d.ts +49 -19
- package/src/react.js +139 -46
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
export
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
+
* 并对每个 <canvas> 派发 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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
3
|
-
* useFitScreen(ref, options) → { scale, inverseScale, refresh }
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
return
|
|
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
|
+
}
|