one-screen-scale 1.0.0 → 1.1.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/LICENSE +21 -21
- package/README.md +186 -180
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +46 -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 +19 -19
- package/dist/react.esm.js +1 -1
- package/dist/react.esm.js.map +1 -1
- package/package.json +71 -71
- package/src/composables.js +67 -67
- package/src/core.js +160 -156
- package/src/directives.js +34 -34
- package/src/index.d.ts +46 -46
- package/src/index.js +14 -14
- package/src/react.d.ts +19 -19
- package/src/react.js +46 -46
package/src/core.js
CHANGED
|
@@ -1,156 +1,160 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 一屏自适应缩放 — 核心算法(框架无关)
|
|
3
|
-
*
|
|
4
|
-
* 两种模式:
|
|
5
|
-
* 'contain' 一屏展示,内容不溢出,用 min(scaleX, scaleY)
|
|
6
|
-
* 'fixed-width' 固定视觉宽度,内容可滚动,反向补高
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
// ---------- helpers ----------
|
|
10
|
-
|
|
11
|
-
function resolveEl(el) {
|
|
12
|
-
if (!el) return null
|
|
13
|
-
if (typeof el === 'string') return document.querySelector(el)
|
|
14
|
-
if (el instanceof HTMLElement) return el
|
|
15
|
-
// Vue ref / React ref
|
|
16
|
-
if (el.value instanceof HTMLElement) return el.value
|
|
17
|
-
if (el.current instanceof HTMLElement) return el.current
|
|
18
|
-
return null
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// ---------- scale calculation ----------
|
|
22
|
-
|
|
23
|
-
export function calculateScale({
|
|
24
|
-
sourceWidth,
|
|
25
|
-
sourceHeight,
|
|
26
|
-
targetWidth = (typeof window !== 'undefined' ? window.innerWidth : 1920),
|
|
27
|
-
targetHeight = (typeof window !== 'undefined' ? window.innerHeight : 1080),
|
|
28
|
-
mode = 'contain',
|
|
29
|
-
visualWidth = 450, // fixed-width 模式下的目标视觉宽度
|
|
30
|
-
screenWidth = (typeof window !== 'undefined' ? window.screen.width : 1920), // fixed-width 模式下的设备屏幕宽度
|
|
31
|
-
}) {
|
|
32
|
-
if (!sourceWidth || !sourceHeight || sourceWidth <= 0 || sourceHeight <= 0) return 1
|
|
33
|
-
|
|
34
|
-
let scale
|
|
35
|
-
|
|
36
|
-
if (mode === 'fixed-width') {
|
|
37
|
-
if(screenWidth < visualWidth){
|
|
38
|
-
|
|
39
|
-
}else{
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
target
|
|
57
|
-
target
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* 一屏自适应缩放 — 核心算法(框架无关)
|
|
3
|
+
*
|
|
4
|
+
* 两种模式:
|
|
5
|
+
* 'contain' 一屏展示,内容不溢出,用 min(scaleX, scaleY)
|
|
6
|
+
* 'fixed-width' 固定视觉宽度,内容可滚动,反向补高
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// ---------- helpers ----------
|
|
10
|
+
|
|
11
|
+
function resolveEl(el) {
|
|
12
|
+
if (!el) return null
|
|
13
|
+
if (typeof el === 'string') return document.querySelector(el)
|
|
14
|
+
if (el instanceof HTMLElement) return el
|
|
15
|
+
// Vue ref / React ref
|
|
16
|
+
if (el.value instanceof HTMLElement) return el.value
|
|
17
|
+
if (el.current instanceof HTMLElement) return el.current
|
|
18
|
+
return null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// ---------- scale calculation ----------
|
|
22
|
+
|
|
23
|
+
export function calculateScale({
|
|
24
|
+
sourceWidth,
|
|
25
|
+
sourceHeight,
|
|
26
|
+
targetWidth = (typeof window !== 'undefined' ? window.innerWidth : 1920),
|
|
27
|
+
targetHeight = (typeof window !== 'undefined' ? window.innerHeight : 1080),
|
|
28
|
+
mode = 'contain',
|
|
29
|
+
visualWidth = 450, // fixed-width 模式下的目标视觉宽度
|
|
30
|
+
// screenWidth = (typeof window !== 'undefined' ? window.screen.width : 1920), // fixed-width 模式下的设备屏幕宽度
|
|
31
|
+
}) {
|
|
32
|
+
if (!sourceWidth || !sourceHeight || sourceWidth <= 0 || sourceHeight <= 0) return 1
|
|
33
|
+
|
|
34
|
+
let scale
|
|
35
|
+
|
|
36
|
+
if (mode === 'fixed-width') {
|
|
37
|
+
// if(screenWidth < visualWidth){
|
|
38
|
+
// scale = 1
|
|
39
|
+
// }else{
|
|
40
|
+
// scale = visualWidth / Math.max(targetWidth, screenWidth)
|
|
41
|
+
// }
|
|
42
|
+
// 只用视口宽:视口比目标宽才缩小,否则 1:1(保持清晰)
|
|
43
|
+
scale = targetWidth > visualWidth ? visualWidth / targetWidth : 1
|
|
44
|
+
} else {
|
|
45
|
+
const sx = targetWidth / sourceWidth
|
|
46
|
+
const sy = targetHeight / sourceHeight
|
|
47
|
+
scale = Math.min(sx, sy)
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return scale
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ---------- apply / read ----------
|
|
54
|
+
|
|
55
|
+
export function applyScale(el, scale, origin = 'top center') {
|
|
56
|
+
const target = resolveEl(el)
|
|
57
|
+
if (!target) return
|
|
58
|
+
target.style.transform = `scale(${scale})`
|
|
59
|
+
target.style.transformOrigin = origin
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function getPageScale(el) {
|
|
63
|
+
const target = resolveEl(el)
|
|
64
|
+
if (!target) return 1
|
|
65
|
+
|
|
66
|
+
const style = window.getComputedStyle(target)
|
|
67
|
+
const t = style.transform
|
|
68
|
+
if (!t || t === 'none') return 1
|
|
69
|
+
|
|
70
|
+
// matrix(a, b, c, d, e, f) — 2d
|
|
71
|
+
const m2 = t.match(/matrix\(([^,]+),\s*[^,]+,\s*[^,]+,\s*([^,]+)/)
|
|
72
|
+
if (m2) return (parseFloat(m2[1]) + parseFloat(m2[2])) / 2
|
|
73
|
+
|
|
74
|
+
// matrix3d — 3d
|
|
75
|
+
const m3 = t.match(/matrix3d\(([^,]+),[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,([^,]+)\)/)
|
|
76
|
+
if (m3) return (parseFloat(m3[1]) + parseFloat(m3[2])) / 2
|
|
77
|
+
|
|
78
|
+
// scale(x, y) or scale(s)
|
|
79
|
+
const sm = t.match(/scale\(([^)]+)\)/)
|
|
80
|
+
if (sm) {
|
|
81
|
+
const vals = sm[1].split(',').map(v => parseFloat(v.trim()))
|
|
82
|
+
return vals.length >= 2 ? (vals[0] + vals[1]) / 2 : vals[0]
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return 1
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function getInverseScale(el = '.share-page') {
|
|
89
|
+
const s = getPageScale(el)
|
|
90
|
+
return s > 0 ? 1 / s : 1
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// ---------- scaler instance ----------
|
|
94
|
+
|
|
95
|
+
export function createScaler(el, options = {}) {
|
|
96
|
+
const target = resolveEl(el)
|
|
97
|
+
if (!target) throw new Error('[one-screen-scale] element not found')
|
|
98
|
+
|
|
99
|
+
const {
|
|
100
|
+
mode = 'contain',
|
|
101
|
+
visualWidth = 450,
|
|
102
|
+
origin = 'top center',
|
|
103
|
+
autoResize = true
|
|
104
|
+
} = options
|
|
105
|
+
|
|
106
|
+
let currentScale = 1
|
|
107
|
+
let handler = null
|
|
108
|
+
|
|
109
|
+
function compute() {
|
|
110
|
+
const sw = target.offsetWidth
|
|
111
|
+
const sh = target.offsetHeight
|
|
112
|
+
const tw = document.documentElement.clientWidth || window.innerWidth
|
|
113
|
+
const th = window.innerHeight
|
|
114
|
+
|
|
115
|
+
return calculateScale({
|
|
116
|
+
sourceWidth: sw,
|
|
117
|
+
sourceHeight: sh,
|
|
118
|
+
targetWidth: tw,
|
|
119
|
+
targetHeight: th,
|
|
120
|
+
mode,
|
|
121
|
+
visualWidth,
|
|
122
|
+
// screenWidth: window.screen.width
|
|
123
|
+
})
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function refresh() {
|
|
127
|
+
const next = compute()
|
|
128
|
+
if (next !== currentScale) {
|
|
129
|
+
currentScale = next
|
|
130
|
+
applyScale(target, next, origin)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// fixed-width 模式:反向补偿高度
|
|
134
|
+
if (mode === 'fixed-width' && next > 0) {
|
|
135
|
+
// target.style.width = `${Math.max(window.innerWidth, window.screen.width)}px`
|
|
136
|
+
const viewportWidth = document.documentElement.clientWidth || window.innerWidth
|
|
137
|
+
target.style.width = `${viewportWidth}px`
|
|
138
|
+
target.style.height = `${Math.round(window.innerHeight / next)}px`
|
|
139
|
+
}
|
|
140
|
+
return currentScale
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function start() {
|
|
144
|
+
refresh()
|
|
145
|
+
if (autoResize) {
|
|
146
|
+
handler = () => refresh()
|
|
147
|
+
window.addEventListener('resize', handler)
|
|
148
|
+
}
|
|
149
|
+
return currentScale
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function stop() {
|
|
153
|
+
if (handler) {
|
|
154
|
+
window.removeEventListener('resize', handler)
|
|
155
|
+
handler = null
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { start, stop, refresh, getScale: () => currentScale }
|
|
160
|
+
}
|
package/src/directives.js
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Vue 3 指令
|
|
3
|
-
* v-fit-screen 页面级缩放
|
|
4
|
-
* v-compensate 组件级反缩放补偿
|
|
5
|
-
*/
|
|
6
|
-
import { createScaler } from './core.js'
|
|
7
|
-
|
|
8
|
-
// ---- v-fit-screen ----
|
|
9
|
-
|
|
10
|
-
export const vFitScreen = {
|
|
11
|
-
mounted(el, binding) {
|
|
12
|
-
const options = binding.value || {}
|
|
13
|
-
const scaler = createScaler(el, options)
|
|
14
|
-
scaler.start()
|
|
15
|
-
el.__fitScreenScaler = scaler
|
|
16
|
-
},
|
|
17
|
-
|
|
18
|
-
updated(el, binding) {
|
|
19
|
-
const scaler = el.__fitScreenScaler
|
|
20
|
-
if (scaler) {
|
|
21
|
-
const options = binding.value || {}
|
|
22
|
-
// 简单处理:重新 refresh
|
|
23
|
-
scaler.refresh()
|
|
24
|
-
}
|
|
25
|
-
},
|
|
26
|
-
|
|
27
|
-
unmounted(el) {
|
|
28
|
-
const scaler = el.__fitScreenScaler
|
|
29
|
-
if (scaler) {
|
|
30
|
-
scaler.stop()
|
|
31
|
-
delete el.__fitScreenScaler
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Vue 3 指令
|
|
3
|
+
* v-fit-screen 页面级缩放
|
|
4
|
+
* v-compensate 组件级反缩放补偿
|
|
5
|
+
*/
|
|
6
|
+
import { createScaler } from './core.js'
|
|
7
|
+
|
|
8
|
+
// ---- v-fit-screen ----
|
|
9
|
+
|
|
10
|
+
export const vFitScreen = {
|
|
11
|
+
mounted(el, binding) {
|
|
12
|
+
const options = binding.value || {}
|
|
13
|
+
const scaler = createScaler(el, options)
|
|
14
|
+
scaler.start()
|
|
15
|
+
el.__fitScreenScaler = scaler
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
updated(el, binding) {
|
|
19
|
+
const scaler = el.__fitScreenScaler
|
|
20
|
+
if (scaler) {
|
|
21
|
+
const options = binding.value || {}
|
|
22
|
+
// 简单处理:重新 refresh
|
|
23
|
+
scaler.refresh()
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
unmounted(el) {
|
|
28
|
+
const scaler = el.__fitScreenScaler
|
|
29
|
+
if (scaler) {
|
|
30
|
+
scaler.stop()
|
|
31
|
+
delete el.__fitScreenScaler
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
package/src/index.d.ts
CHANGED
|
@@ -1,46 +1,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
|
-
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
|
+
// 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
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
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
|
+
} from './core.js'
|
|
9
|
+
|
|
10
|
+
// ---- Vue 指令 ----
|
|
11
|
+
export { vFitScreen } from './directives.js'
|
|
12
|
+
|
|
13
|
+
// ---- Vue Composable ----
|
|
14
|
+
export { useFitScreen } from './composables.js'
|
package/src/react.d.ts
CHANGED
|
@@ -1,19 +1,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
|
-
|
|
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
|
+
|
|
12
|
+
export function useFitScreen(
|
|
13
|
+
targetRef: RefObject<HTMLElement | null> | HTMLElement,
|
|
14
|
+
options?: ScalerOptions
|
|
15
|
+
): {
|
|
16
|
+
scale: number
|
|
17
|
+
inverseScale: number
|
|
18
|
+
refresh: () => void
|
|
19
|
+
}
|
package/src/react.js
CHANGED
|
@@ -1,46 +1,46 @@
|
|
|
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 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
|
+
}
|