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 CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ligang <bpcnlg@163.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,180 +1,186 @@
1
- # one-screen-scale
2
-
3
- 基于 CSS `transform: scale()` 的一屏自适应方案。框架无关核心 + Vue 3 / React 开箱即用。
4
-
5
- ## 为什么用这个
6
-
7
- - 🎯 **真正的"一屏"** — 内容不溢出、不滚动(contain 模式)
8
- - 🧩 **零依赖核心** — 纯 JS,不绑定任何框架
9
- - ⚡ **指令 / Hook 开箱即用** — Vue 3 和 React 都支持
10
- - 🖥️ **Canvas 友好** — 通过反缩放补偿,ECharts 等 canvas 内容保持清晰
11
-
12
- ## 安装
13
-
14
- ```bash
15
- npm install one-screen-scale
16
- ```
17
-
18
- ## 快速开始
19
-
20
- ### Vue 3 — 指令(最简)
21
-
22
- ```vue
23
- <template>
24
- <div v-fit-screen class="page">
25
- <h1>活动标题</h1>
26
- <p>页面内容...</p>
27
- </div>
28
- </template>
29
-
30
- <script setup>
31
- import { vFitScreen } from 'one-screen-scale'
32
- </script>
33
- ```
34
-
35
- ### Vue 3 — Composable(需要 scale 值,如 ECharts 补偿)
36
-
37
- ```vue
38
- <template>
39
- <div ref="pageRef" class="page">
40
- <div :style="{ transform: `scale(${inverseScale})` }">
41
- <ECharts />
42
- </div>
43
- </div>
44
- </template>
45
-
46
- <script setup>
47
- import { ref } from 'vue'
48
- import { useFitScreen } from 'one-screen-scale'
49
-
50
- const pageRef = ref(null)
51
- const { scale, inverseScale } = useFitScreen(pageRef)
52
- // inverseScale 可用于子元素反缩放补偿
53
- </script>
54
- ```
55
-
56
- ### React
57
-
58
- ```jsx
59
- import { useRef } from 'react'
60
- import { useFitScreen } from 'one-screen-scale/react'
61
-
62
- function App() {
63
- const pageRef = useRef(null)
64
- const { scale, inverseScale } = useFitScreen(pageRef)
65
-
66
- return (
67
- <div ref={pageRef} className="page">
68
- <h1>活动标题</h1>
69
- </div>
70
- )
71
- }
72
- ```
73
-
74
- ### 纯 JS / jQuery
75
-
76
- ```js
77
- import { createScaler } from 'one-screen-scale'
78
-
79
- const scaler = createScaler(document.querySelector('.page'), {
80
- mode: 'contain'
81
- })
82
- scaler.start()
83
-
84
- // 销毁
85
- scaler.stop()
86
- ```
87
-
88
- ## 两种模式
89
-
90
- | 模式 | 算法 | 效果 | 场景 |
91
- |------|------|------|------|
92
- | `contain`(默认) | `min(scaleX, scaleY)` | 内容完整可见,不溢出 | 活动页、落地页 |
93
- | `fixed-width` | `visualWidth / max(innerWidth, screenWidth)` | 固定手机观感宽度,可滚动 | 规则页、长表单 |
94
-
95
- ```js
96
- // contain(默认)
97
- createScaler(el)
98
-
99
- // fixed-width
100
- createScaler(el, { mode: 'fixed-width', visualWidth: 450 })
101
- ```
102
-
103
- ## API
104
-
105
- ### 核心函数(框架无关)
106
-
107
- ```ts
108
- calculateScale(options: ScaleOptions): number
109
-
110
- applyScale(el: Element | string, scale: number, origin?: string): void
111
-
112
- getPageScale(el?: Element | string): number // 默认查 .share-page
113
- getInverseScale(el?: Element | string): number // 1 / getPageScale()
114
-
115
- createScaler(el: Element | string, options?: ScalerOptions): ScalerInstance
116
- ```
117
-
118
- ### ScalerInstance
119
-
120
- ```ts
121
- {
122
- start(): number // 启动缩放 + 监听 resize,返回当前 scale
123
- stop(): void // 停止监听
124
- refresh(): number // 手动重算
125
- getScale(): number // 获取当前 scale
126
- }
127
- ```
128
-
129
- ### ScalerOptions
130
-
131
- | 参数 | 类型 | 默认值 | 说明 |
132
- |------|------|--------|------|
133
- | `mode` | `'contain' \| 'fixed-width'` | `'contain'` | 缩放模式 |
134
- | `visualWidth` | `number` | `450` | fixed-width 模式目标视觉宽度 |
135
- | `origin` | `string` | `'top center'` | transform-origin |
136
- | `autoResize` | `boolean` | `true` | 自动监听窗口 resize |
137
-
138
- ### Vue
139
-
140
- ```ts
141
- // 指令
142
- vFitScreen: Directive<HTMLElement, ScalerOptions>
143
-
144
- // Composable
145
- useFitScreen(ref: Ref, options?: ScalerOptions): {
146
- scale: Ref<number>
147
- inverseScale: Ref<number>
148
- refresh: () => void
149
- }
150
- ```
151
-
152
- ### React
153
-
154
- ```ts
155
- import { useFitScreen } from 'one-screen-scale/react'
156
-
157
- useFitScreen(ref: RefObject, options?: ScalerOptions): {
158
- scale: number
159
- inverseScale: number
160
- refresh: () => void
161
- }
162
- ```
163
-
164
- ## Canvas / ECharts 模糊问题
165
-
166
- CSS `transform: scale()` 缩放的是位图,canvas 内容会变糊。解决办法:对 canvas 容器做反缩放补偿。
167
-
168
- ```js
169
- // Vue composable 示例
170
- const { inverseScale } = useFitScreen(pageRef)
171
-
172
- // 在包裹层上应用反缩放
173
- // <div :style="{ transform: `scale(${inverseScale})`, transformOrigin: 'top left' }">
174
- ```
175
-
176
- 反缩放后 canvas 以原生分辨率渲染,保持清晰。详情见 [Canvas 缩放补偿指南](https://github.com/nicepkg/one-screen-scale)。
177
-
178
- ## License
179
-
180
- MIT
1
+ # one-screen-scale
2
+
3
+ 基于 CSS `transform: scale()` 的一屏自适应方案。框架无关核心 + Vue 3 / React 开箱即用。
4
+
5
+ ## 为什么用这个
6
+
7
+ - 🎯 **真正的"一屏"** — 内容不溢出、不滚动(contain 模式)
8
+ - 🧩 **零依赖核心** — 纯 JS,不绑定任何框架
9
+ - ⚡ **指令 / Hook 开箱即用** — Vue 3 和 React 都支持
10
+ - 🖥️ **Canvas 友好** — 通过反缩放补偿,ECharts 等 canvas 内容保持清晰
11
+
12
+ ## 主要面向场景
13
+
14
+ - 使用了pxToViewport autoprefixer 或者类似的移动端自适应插件的h5网页
15
+ - 可以使用该插件在任何设备实现一屏自适应,如果是固定高度(一屏)则使用contain模式,如果是滚动高度则使用fixed-width模式
16
+ - 同时在pc端可以正常自适应显示同一个页面,从而无需再开发pc端对应页面
17
+
18
+ ## 安装
19
+
20
+ ```bash
21
+ npm install one-screen-scale
22
+ ```
23
+
24
+ ## 快速开始
25
+
26
+ ### Vue 3 — 指令(最简)
27
+
28
+ ```vue
29
+ <template>
30
+ <div v-fit-screen class="page">
31
+ <h1>活动标题</h1>
32
+ <p>页面内容...</p>
33
+ </div>
34
+ </template>
35
+
36
+ <script setup>
37
+ import { vFitScreen } from 'one-screen-scale'
38
+ </script>
39
+ ```
40
+
41
+ ### Vue 3 — Composable(需要 scale 值,如 ECharts 补偿)
42
+
43
+ ```vue
44
+ <template>
45
+ <div ref="pageRef" class="page">
46
+ <div :style="{ transform: `scale(${inverseScale})` }">
47
+ <ECharts />
48
+ </div>
49
+ </div>
50
+ </template>
51
+
52
+ <script setup>
53
+ import { ref } from 'vue'
54
+ import { useFitScreen } from 'one-screen-scale'
55
+
56
+ const pageRef = ref(null)
57
+ const { scale, inverseScale } = useFitScreen(pageRef)
58
+ // inverseScale 可用于子元素反缩放补偿
59
+ </script>
60
+ ```
61
+
62
+ ### React
63
+
64
+ ```jsx
65
+ import { useRef } from 'react'
66
+ import { useFitScreen } from 'one-screen-scale/react'
67
+
68
+ function App() {
69
+ const pageRef = useRef(null)
70
+ const { scale, inverseScale } = useFitScreen(pageRef)
71
+
72
+ return (
73
+ <div ref={pageRef} className="page">
74
+ <h1>活动标题</h1>
75
+ </div>
76
+ )
77
+ }
78
+ ```
79
+
80
+ ### 纯 JS / jQuery
81
+
82
+ ```js
83
+ import { createScaler } from 'one-screen-scale'
84
+
85
+ const scaler = createScaler(document.querySelector('.page'), {
86
+ mode: 'contain'
87
+ })
88
+ scaler.start()
89
+
90
+ // 销毁
91
+ scaler.stop()
92
+ ```
93
+
94
+ ## 两种模式
95
+
96
+ | 模式 | 算法 | 效果 | 场景 |
97
+ |------|------|------|------|
98
+ | `contain`(默认) | `min(scaleX, scaleY)` | 内容完整可见,不溢出 | 活动页、落地页 |
99
+ | `fixed-width` | `visualWidth / innerWidth`(视口 ≤ visualWidth 时为 1) | 固定手机观感宽度,可滚动 | 规则页、长表单 |
100
+
101
+ ```js
102
+ // contain(默认)
103
+ createScaler(el)
104
+
105
+ // fixed-width
106
+ createScaler(el, { mode: 'fixed-width', visualWidth: 450 })
107
+ ```
108
+
109
+ ## API
110
+
111
+ ### 核心函数(框架无关)
112
+
113
+ ```ts
114
+ calculateScale(options: ScaleOptions): number
115
+
116
+ applyScale(el: Element | string, scale: number, origin?: string): void
117
+
118
+ getPageScale(el?: Element | string): number // 默认查 .share-page
119
+ getInverseScale(el?: Element | string): number // 1 / getPageScale()
120
+
121
+ createScaler(el: Element | string, options?: ScalerOptions): ScalerInstance
122
+ ```
123
+
124
+ ### ScalerInstance
125
+
126
+ ```ts
127
+ {
128
+ start(): number // 启动缩放 + 监听 resize,返回当前 scale
129
+ stop(): void // 停止监听
130
+ refresh(): number // 手动重算
131
+ getScale(): number // 获取当前 scale
132
+ }
133
+ ```
134
+
135
+ ### ScalerOptions
136
+
137
+ | 参数 | 类型 | 默认值 | 说明 |
138
+ |------|------|--------|------|
139
+ | `mode` | `'contain' \| 'fixed-width'` | `'contain'` | 缩放模式 |
140
+ | `visualWidth` | `number` | `450` | fixed-width 模式目标视觉宽度 |
141
+ | `origin` | `string` | `'top center'` | transform-origin |
142
+ | `autoResize` | `boolean` | `true` | 自动监听窗口 resize |
143
+
144
+ ### Vue
145
+
146
+ ```ts
147
+ // 指令
148
+ vFitScreen: Directive<HTMLElement, ScalerOptions>
149
+
150
+ // Composable
151
+ useFitScreen(ref: Ref, options?: ScalerOptions): {
152
+ scale: Ref<number>
153
+ inverseScale: Ref<number>
154
+ refresh: () => void
155
+ }
156
+ ```
157
+
158
+ ### React
159
+
160
+ ```ts
161
+ import { useFitScreen } from 'one-screen-scale/react'
162
+
163
+ useFitScreen(ref: RefObject, options?: ScalerOptions): {
164
+ scale: number
165
+ inverseScale: number
166
+ refresh: () => void
167
+ }
168
+ ```
169
+
170
+ ## Canvas / ECharts 模糊问题
171
+
172
+ CSS `transform: scale()` 缩放的是位图,canvas 内容会变糊。解决办法:对 canvas 容器做反缩放补偿。
173
+
174
+ ```js
175
+ // Vue composable 示例
176
+ const { inverseScale } = useFitScreen(pageRef)
177
+
178
+ // 在包裹层上应用反缩放
179
+ // <div :style="{ transform: `scale(${inverseScale})`, transformOrigin: 'top left' }">
180
+ ```
181
+
182
+ 反缩放后 canvas 以原生分辨率渲染,保持清晰。详情见 [Canvas 缩放补偿指南](https://github.com/nicepkg/one-screen-scale)。
183
+
184
+ ## License
185
+
186
+ MIT
package/dist/index.cjs.js CHANGED
@@ -1,2 +1,2 @@
1
- "use strict";var e=require("vue");function t(e){return e?"string"==typeof e?document.querySelector(e):e instanceof HTMLElement?e:e.value instanceof HTMLElement?e.value:e.current instanceof HTMLElement?e.current:null:null}function n({sourceWidth:e,sourceHeight:t,targetWidth:n=("undefined"!=typeof window?window.innerWidth:1920),targetHeight:r=("undefined"!=typeof window?window.innerHeight:1080),mode:i="contain",visualWidth:o=450,screenWidth:s=("undefined"!=typeof window?window.screen.width:1920)}){if(!e||!t||e<=0||t<=0)return 1;let a;if("fixed-width"===i)a=s<o?1:o/Math.max(n,s);else{const i=n/e,o=r/t;a=Math.min(i,o)}return a}function r(e,n,r="top center"){const i=t(e);i&&(i.style.transform=`scale(${n})`,i.style.transformOrigin=r)}function i(e){const n=t(e);if(!n)return 1;const r=window.getComputedStyle(n).transform;if(!r||"none"===r)return 1;const i=r.match(/matrix\(([^,]+),\s*[^,]+,\s*[^,]+,\s*([^,]+)/);if(i)return(parseFloat(i[1])+parseFloat(i[2]))/2;const o=r.match(/matrix3d\(([^,]+),[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,([^,]+)\)/);if(o)return(parseFloat(o[1])+parseFloat(o[2]))/2;const s=r.match(/scale\(([^)]+)\)/);if(s){const e=s[1].split(",").map(e=>parseFloat(e.trim()));return e.length>=2?(e[0]+e[1])/2:e[0]}return 1}function o(e=".share-page"){const t=i(e);return t>0?1/t:1}function s(e,i={}){const o=t(e);if(!o)throw new Error("[one-screen-scale] element not found");const{mode:s="contain",visualWidth:a=450,origin:c="top center",autoResize:u=!0}=i;let l=1,d=null;function f(){const e=n({sourceWidth:o.offsetWidth,sourceHeight:o.offsetHeight,targetWidth:window.innerWidth,targetHeight:window.innerHeight,mode:s,visualWidth:a,screenWidth:window.screen.width});return e!==l&&(l=e,r(o,e,c),"fixed-width"===s&&e>0&&(o.style.width=`${Math.max(window.innerWidth,window.screen.width)}px`,o.style.height=`${Math.round(window.innerHeight/e)}px`)),l}return{start:function(){return f(),u&&(d=()=>f(),window.addEventListener("resize",d)),l},stop:function(){d&&(window.removeEventListener("resize",d),d=null)},refresh:f,getScale:()=>l}}const a={mounted(e,t){const n=s(e,t.value||{});n.start(),e.__fitScreenScaler=n},updated(e,t){const n=e.__fitScreenScaler;n&&(t.value,n.refresh())},unmounted(e){const t=e.__fitScreenScaler;t&&(t.stop(),delete e.__fitScreenScaler)}};exports.applyScale=r,exports.calculateScale=n,exports.createScaler=s,exports.getInverseScale=o,exports.getPageScale=i,exports.useFitScreen=function(t,n={}){const r=e.ref(1),i=e.ref(1);let a=null,c=null;function u(e){if(!e)return;a=s(e,n);const t=a.start();r.value=t,i.value=o(e),c=()=>{r.value=a?.getScale?.()??1,i.value=o(e)},window.addEventListener("resize",c)}return e.onMounted(()=>{u(t?.value??t)}),t&&"object"==typeof t&&"value"in t&&e.watch(()=>t.value,e=>{e&&!a&&u(e)}),e.onUnmounted(()=>{c&&(window.removeEventListener("resize",c),c=null),a?.stop(),a=null}),{scale:r,inverseScale:i,refresh:()=>{const e=t?.value??t;if(!e||!a)return;const n=a.refresh();r.value=n,i.value=o(e)}}},exports.vFitScreen=a;
1
+ "use strict";var e=require("vue");function t(e){return e?"string"==typeof e?document.querySelector(e):e instanceof HTMLElement?e:e.value instanceof HTMLElement?e.value:e.current instanceof HTMLElement?e.current:null:null}function n({sourceWidth:e,sourceHeight:t,targetWidth:n=("undefined"!=typeof window?window.innerWidth:1920),targetHeight:r=("undefined"!=typeof window?window.innerHeight:1080),mode:i="contain",visualWidth:o=450}){if(!e||!t||e<=0||t<=0)return 1;let s;if("fixed-width"===i)s=n>o?o/n:1;else{const i=n/e,o=r/t;s=Math.min(i,o)}return s}function r(e,n,r="top center"){const i=t(e);i&&(i.style.transform=`scale(${n})`,i.style.transformOrigin=r)}function i(e){const n=t(e);if(!n)return 1;const r=window.getComputedStyle(n).transform;if(!r||"none"===r)return 1;const i=r.match(/matrix\(([^,]+),\s*[^,]+,\s*[^,]+,\s*([^,]+)/);if(i)return(parseFloat(i[1])+parseFloat(i[2]))/2;const o=r.match(/matrix3d\(([^,]+),[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,([^,]+)\)/);if(o)return(parseFloat(o[1])+parseFloat(o[2]))/2;const s=r.match(/scale\(([^)]+)\)/);if(s){const e=s[1].split(",").map(e=>parseFloat(e.trim()));return e.length>=2?(e[0]+e[1])/2:e[0]}return 1}function o(e=".share-page"){const t=i(e);return t>0?1/t:1}function s(e,i={}){const o=t(e);if(!o)throw new Error("[one-screen-scale] element not found");const{mode:s="contain",visualWidth:c=450,origin:u="top center",autoResize:a=!0}=i;let l=1,d=null;function f(){const e=n({sourceWidth:o.offsetWidth,sourceHeight:o.offsetHeight,targetWidth:document.documentElement.clientWidth||window.innerWidth,targetHeight:window.innerHeight,mode:s,visualWidth:c});if(e!==l&&(l=e,r(o,e,u)),"fixed-width"===s&&e>0){const t=document.documentElement.clientWidth||window.innerWidth;o.style.width=`${t}px`,o.style.height=`${Math.round(window.innerHeight/e)}px`}return l}return{start:function(){return f(),a&&(d=()=>f(),window.addEventListener("resize",d)),l},stop:function(){d&&(window.removeEventListener("resize",d),d=null)},refresh:f,getScale:()=>l}}const c={mounted(e,t){const n=s(e,t.value||{});n.start(),e.__fitScreenScaler=n},updated(e,t){const n=e.__fitScreenScaler;n&&(t.value,n.refresh())},unmounted(e){const t=e.__fitScreenScaler;t&&(t.stop(),delete e.__fitScreenScaler)}};exports.applyScale=r,exports.calculateScale=n,exports.createScaler=s,exports.getInverseScale=o,exports.getPageScale=i,exports.useFitScreen=function(t,n={}){const r=e.ref(1),i=e.ref(1);let c=null,u=null;function a(e){if(!e)return;c=s(e,n);const t=c.start();r.value=t,i.value=o(e),u=()=>{r.value=c?.getScale?.()??1,i.value=o(e)},window.addEventListener("resize",u)}return e.onMounted(()=>{a(t?.value??t)}),t&&"object"==typeof t&&"value"in t&&e.watch(()=>t.value,e=>{e&&!c&&a(e)}),e.onUnmounted(()=>{u&&(window.removeEventListener("resize",u),u=null),c?.stop(),c=null}),{scale:r,inverseScale:i,refresh:()=>{const e=t?.value??t;if(!e||!c)return;const n=c.refresh();r.value=n,i.value=o(e)}}},exports.vFitScreen=c;
2
2
  //# sourceMappingURL=index.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs.js","sources":["../src/core.js","../src/directives.js","../src/composables.js"],"sourcesContent":["/**\n * 一屏自适应缩放 — 核心算法(框架无关)\n *\n * 两种模式:\n * 'contain' 一屏展示,内容不溢出,用 min(scaleX, scaleY)\n * 'fixed-width' 固定视觉宽度,内容可滚动,反向补高\n */\n\n// ---------- helpers ----------\n\nfunction resolveEl(el) {\n if (!el) return null\n if (typeof el === 'string') return document.querySelector(el)\n if (el instanceof HTMLElement) return el\n // Vue ref / React ref\n if (el.value instanceof HTMLElement) return el.value\n if (el.current instanceof HTMLElement) return el.current\n return null\n}\n\n// ---------- scale calculation ----------\n\nexport function calculateScale({\n sourceWidth,\n sourceHeight,\n targetWidth = (typeof window !== 'undefined' ? window.innerWidth : 1920),\n targetHeight = (typeof window !== 'undefined' ? window.innerHeight : 1080),\n mode = 'contain',\n visualWidth = 450, // fixed-width 模式下的目标视觉宽度\n screenWidth = (typeof window !== 'undefined' ? window.screen.width : 1920), // fixed-width 模式下的设备屏幕宽度\n}) {\n if (!sourceWidth || !sourceHeight || sourceWidth <= 0 || sourceHeight <= 0) return 1\n\n let scale\n\n if (mode === 'fixed-width') {\n if(screenWidth < visualWidth){\n scale = 1\n }else{\n scale = visualWidth / Math.max(targetWidth, screenWidth)\n }\n } else {\n const sx = targetWidth / sourceWidth\n const sy = targetHeight / sourceHeight\n scale = Math.min(sx, sy)\n }\n\n return scale\n}\n\n// ---------- apply / read ----------\n\nexport function applyScale(el, scale, origin = 'top center') {\n const target = resolveEl(el)\n if (!target) return\n target.style.transform = `scale(${scale})`\n target.style.transformOrigin = origin\n}\n\nexport function getPageScale(el) {\n const target = resolveEl(el)\n if (!target) return 1\n\n const style = window.getComputedStyle(target)\n const t = style.transform\n if (!t || t === 'none') return 1\n\n // matrix(a, b, c, d, e, f) — 2d\n const m2 = t.match(/matrix\\(([^,]+),\\s*[^,]+,\\s*[^,]+,\\s*([^,]+)/)\n if (m2) return (parseFloat(m2[1]) + parseFloat(m2[2])) / 2\n\n // matrix3d — 3d\n const m3 = t.match(/matrix3d\\(([^,]+),[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,([^,]+)\\)/)\n if (m3) return (parseFloat(m3[1]) + parseFloat(m3[2])) / 2\n\n // scale(x, y) or scale(s)\n const sm = t.match(/scale\\(([^)]+)\\)/)\n if (sm) {\n const vals = sm[1].split(',').map(v => parseFloat(v.trim()))\n return vals.length >= 2 ? (vals[0] + vals[1]) / 2 : vals[0]\n }\n\n return 1\n}\n\nexport function getInverseScale(el = '.share-page') {\n const s = getPageScale(el)\n return s > 0 ? 1 / s : 1\n}\n\n// ---------- scaler instance ----------\n\nexport function createScaler(el, options = {}) {\n const target = resolveEl(el)\n if (!target) throw new Error('[one-screen-scale] element not found')\n\n const {\n mode = 'contain',\n visualWidth = 450,\n origin = 'top center',\n autoResize = true\n } = options\n\n let currentScale = 1\n let handler = null\n\n function compute() {\n const sw = target.offsetWidth\n const sh = target.offsetHeight\n const tw = window.innerWidth\n const th = window.innerHeight\n\n return calculateScale({\n sourceWidth: sw,\n sourceHeight: sh,\n targetWidth: tw,\n targetHeight: th,\n mode,\n visualWidth,\n screenWidth: window.screen.width\n })\n }\n\n function refresh() {\n const next = compute()\n if (next !== currentScale) {\n currentScale = next\n applyScale(target, next, origin)\n\n // fixed-width 模式:反向补偿高度\n if (mode === 'fixed-width' && next > 0) {\n target.style.width = `${Math.max(window.innerWidth, window.screen.width)}px`\n target.style.height = `${Math.round(window.innerHeight / next)}px`\n }\n }\n return currentScale\n }\n\n function start() {\n refresh()\n if (autoResize) {\n handler = () => refresh()\n window.addEventListener('resize', handler)\n }\n return currentScale\n }\n\n function stop() {\n if (handler) {\n window.removeEventListener('resize', handler)\n handler = null\n }\n }\n\n return { start, stop, refresh, getScale: () => currentScale }\n}\n","/**\n * Vue 3 指令\n * v-fit-screen 页面级缩放\n * v-compensate 组件级反缩放补偿\n */\nimport { createScaler } from './core.js'\n\n// ---- v-fit-screen ----\n\nexport const vFitScreen = {\n mounted(el, binding) {\n const options = binding.value || {}\n const scaler = createScaler(el, options)\n scaler.start()\n el.__fitScreenScaler = scaler\n },\n\n updated(el, binding) {\n const scaler = el.__fitScreenScaler\n if (scaler) {\n const options = binding.value || {}\n // 简单处理:重新 refresh\n scaler.refresh()\n }\n },\n\n unmounted(el) {\n const scaler = el.__fitScreenScaler\n if (scaler) {\n scaler.stop()\n delete el.__fitScreenScaler\n }\n }\n}\n","/**\n * Vue 3 Composable\n * useFitScreen(ref, options) → { scale, inverseScale, refresh }\n */\nimport { ref, onMounted, onUnmounted, watch } from 'vue'\nimport { createScaler, getInverseScale } from './core.js'\n\nexport function useFitScreen(targetRef, options = {}) {\n const scale = ref(1)\n const inverseScale = ref(1)\n\n let scaler = null\n let onResize = null\n\n function setup(el) {\n if (!el) return\n scaler = createScaler(el, options)\n const s = scaler.start()\n scale.value = s\n inverseScale.value = getInverseScale(el)\n\n onResize = () => {\n scale.value = scaler?.getScale?.() ?? 1\n inverseScale.value = getInverseScale(el)\n }\n window.addEventListener('resize', onResize)\n }\n\n function teardown() {\n if (onResize) {\n window.removeEventListener('resize', onResize)\n onResize = null\n }\n scaler?.stop()\n scaler = null\n }\n\n // 处理 ref 初始有值的情况\n onMounted(() => {\n const el = targetRef?.value ?? targetRef\n setup(el)\n })\n\n // 处理 ref 延迟赋值的情况(异步挂载)\n if (targetRef && typeof targetRef === 'object' && 'value' in targetRef) {\n watch(\n () => targetRef.value,\n (el) => {\n if (el && !scaler) setup(el)\n }\n )\n }\n\n onUnmounted(() => {\n teardown()\n })\n\n const refresh = () => {\n const el = targetRef?.value ?? targetRef\n if (!el || !scaler) return\n const s = scaler.refresh()\n scale.value = s\n inverseScale.value = getInverseScale(el)\n }\n\n return { scale, inverseScale, refresh }\n}\n"],"names":["resolveEl","el","document","querySelector","HTMLElement","value","current","calculateScale","sourceWidth","sourceHeight","targetWidth","window","innerWidth","targetHeight","innerHeight","mode","visualWidth","screenWidth","screen","width","scale","Math","max","sx","sy","min","applyScale","origin","target","style","transform","transformOrigin","getPageScale","t","getComputedStyle","m2","match","parseFloat","m3","sm","vals","split","map","v","trim","length","getInverseScale","s","createScaler","options","Error","autoResize","currentScale","handler","refresh","next","offsetWidth","offsetHeight","height","round","start","addEventListener","stop","removeEventListener","getScale","vFitScreen","mounted","binding","scaler","__fitScreenScaler","updated","unmounted","targetRef","ref","inverseScale","onResize","setup","onMounted","watch","onUnmounted"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs.js","sources":["../src/core.js","../src/directives.js","../src/composables.js"],"sourcesContent":["/**\r\n * 一屏自适应缩放 — 核心算法(框架无关)\r\n *\r\n * 两种模式:\r\n * 'contain' 一屏展示,内容不溢出,用 min(scaleX, scaleY)\r\n * 'fixed-width' 固定视觉宽度,内容可滚动,反向补高\r\n */\r\n\r\n// ---------- helpers ----------\r\n\r\nfunction resolveEl(el) {\r\n if (!el) return null\r\n if (typeof el === 'string') return document.querySelector(el)\r\n if (el instanceof HTMLElement) return el\r\n // Vue ref / React ref\r\n if (el.value instanceof HTMLElement) return el.value\r\n if (el.current instanceof HTMLElement) return el.current\r\n return null\r\n}\r\n\r\n// ---------- scale calculation ----------\r\n\r\nexport function calculateScale({\r\n sourceWidth,\r\n sourceHeight,\r\n targetWidth = (typeof window !== 'undefined' ? window.innerWidth : 1920),\r\n targetHeight = (typeof window !== 'undefined' ? window.innerHeight : 1080),\r\n mode = 'contain',\r\n visualWidth = 450, // fixed-width 模式下的目标视觉宽度\r\n // screenWidth = (typeof window !== 'undefined' ? window.screen.width : 1920), // fixed-width 模式下的设备屏幕宽度\r\n}) {\r\n if (!sourceWidth || !sourceHeight || sourceWidth <= 0 || sourceHeight <= 0) return 1\r\n\r\n let scale\r\n\r\n if (mode === 'fixed-width') {\r\n // if(screenWidth < visualWidth){\r\n // scale = 1\r\n // }else{\r\n // scale = visualWidth / Math.max(targetWidth, screenWidth)\r\n // }\r\n // 只用视口宽:视口比目标宽才缩小,否则 1:1(保持清晰)\r\n scale = targetWidth > visualWidth ? visualWidth / targetWidth : 1\r\n } else {\r\n const sx = targetWidth / sourceWidth\r\n const sy = targetHeight / sourceHeight\r\n scale = Math.min(sx, sy)\r\n }\r\n\r\n return scale\r\n}\r\n\r\n// ---------- apply / read ----------\r\n\r\nexport function applyScale(el, scale, origin = 'top center') {\r\n const target = resolveEl(el)\r\n if (!target) return\r\n target.style.transform = `scale(${scale})`\r\n target.style.transformOrigin = origin\r\n}\r\n\r\nexport function getPageScale(el) {\r\n const target = resolveEl(el)\r\n if (!target) return 1\r\n\r\n const style = window.getComputedStyle(target)\r\n const t = style.transform\r\n if (!t || t === 'none') return 1\r\n\r\n // matrix(a, b, c, d, e, f) — 2d\r\n const m2 = t.match(/matrix\\(([^,]+),\\s*[^,]+,\\s*[^,]+,\\s*([^,]+)/)\r\n if (m2) return (parseFloat(m2[1]) + parseFloat(m2[2])) / 2\r\n\r\n // matrix3d — 3d\r\n const m3 = t.match(/matrix3d\\(([^,]+),[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,([^,]+)\\)/)\r\n if (m3) return (parseFloat(m3[1]) + parseFloat(m3[2])) / 2\r\n\r\n // scale(x, y) or scale(s)\r\n const sm = t.match(/scale\\(([^)]+)\\)/)\r\n if (sm) {\r\n const vals = sm[1].split(',').map(v => parseFloat(v.trim()))\r\n return vals.length >= 2 ? (vals[0] + vals[1]) / 2 : vals[0]\r\n }\r\n\r\n return 1\r\n}\r\n\r\nexport function getInverseScale(el = '.share-page') {\r\n const s = getPageScale(el)\r\n return s > 0 ? 1 / s : 1\r\n}\r\n\r\n// ---------- scaler instance ----------\r\n\r\nexport function createScaler(el, options = {}) {\r\n const target = resolveEl(el)\r\n if (!target) throw new Error('[one-screen-scale] element not found')\r\n\r\n const {\r\n mode = 'contain',\r\n visualWidth = 450,\r\n origin = 'top center',\r\n autoResize = true\r\n } = options\r\n\r\n let currentScale = 1\r\n let handler = null\r\n\r\n function compute() {\r\n const sw = target.offsetWidth\r\n const sh = target.offsetHeight\r\n const tw = document.documentElement.clientWidth || window.innerWidth\r\n const th = window.innerHeight\r\n\r\n return calculateScale({\r\n sourceWidth: sw,\r\n sourceHeight: sh,\r\n targetWidth: tw,\r\n targetHeight: th,\r\n mode,\r\n visualWidth,\r\n // screenWidth: window.screen.width\r\n })\r\n }\r\n\r\n function refresh() {\r\n const next = compute()\r\n if (next !== currentScale) {\r\n currentScale = next\r\n applyScale(target, next, origin)\r\n }\r\n\r\n // fixed-width 模式:反向补偿高度\r\n if (mode === 'fixed-width' && next > 0) {\r\n // target.style.width = `${Math.max(window.innerWidth, window.screen.width)}px`\r\n const viewportWidth = document.documentElement.clientWidth || window.innerWidth\r\n target.style.width = `${viewportWidth}px`\r\n target.style.height = `${Math.round(window.innerHeight / next)}px`\r\n }\r\n return currentScale\r\n }\r\n\r\n function start() {\r\n refresh()\r\n if (autoResize) {\r\n handler = () => refresh()\r\n window.addEventListener('resize', handler)\r\n }\r\n return currentScale\r\n }\r\n\r\n function stop() {\r\n if (handler) {\r\n window.removeEventListener('resize', handler)\r\n handler = null\r\n }\r\n }\r\n\r\n return { start, stop, refresh, getScale: () => currentScale }\r\n}\r\n","/**\r\n * Vue 3 指令\r\n * v-fit-screen 页面级缩放\r\n * v-compensate 组件级反缩放补偿\r\n */\r\nimport { createScaler } from './core.js'\r\n\r\n// ---- v-fit-screen ----\r\n\r\nexport const vFitScreen = {\r\n mounted(el, binding) {\r\n const options = binding.value || {}\r\n const scaler = createScaler(el, options)\r\n scaler.start()\r\n el.__fitScreenScaler = scaler\r\n },\r\n\r\n updated(el, binding) {\r\n const scaler = el.__fitScreenScaler\r\n if (scaler) {\r\n const options = binding.value || {}\r\n // 简单处理:重新 refresh\r\n scaler.refresh()\r\n }\r\n },\r\n\r\n unmounted(el) {\r\n const scaler = el.__fitScreenScaler\r\n if (scaler) {\r\n scaler.stop()\r\n delete el.__fitScreenScaler\r\n }\r\n }\r\n}\r\n","/**\r\n * Vue 3 Composable\r\n * useFitScreen(ref, options) → { scale, inverseScale, refresh }\r\n */\r\nimport { ref, onMounted, onUnmounted, watch } from 'vue'\r\nimport { createScaler, getInverseScale } from './core.js'\r\n\r\nexport function useFitScreen(targetRef, options = {}) {\r\n const scale = ref(1)\r\n const inverseScale = ref(1)\r\n\r\n let scaler = null\r\n let onResize = null\r\n\r\n function setup(el) {\r\n if (!el) return\r\n scaler = createScaler(el, options)\r\n const s = scaler.start()\r\n scale.value = s\r\n inverseScale.value = getInverseScale(el)\r\n\r\n onResize = () => {\r\n scale.value = scaler?.getScale?.() ?? 1\r\n inverseScale.value = getInverseScale(el)\r\n }\r\n window.addEventListener('resize', onResize)\r\n }\r\n\r\n function teardown() {\r\n if (onResize) {\r\n window.removeEventListener('resize', onResize)\r\n onResize = null\r\n }\r\n scaler?.stop()\r\n scaler = null\r\n }\r\n\r\n // 处理 ref 初始有值的情况\r\n onMounted(() => {\r\n const el = targetRef?.value ?? targetRef\r\n setup(el)\r\n })\r\n\r\n // 处理 ref 延迟赋值的情况(异步挂载)\r\n if (targetRef && typeof targetRef === 'object' && 'value' in targetRef) {\r\n watch(\r\n () => targetRef.value,\r\n (el) => {\r\n if (el && !scaler) setup(el)\r\n }\r\n )\r\n }\r\n\r\n onUnmounted(() => {\r\n teardown()\r\n })\r\n\r\n const refresh = () => {\r\n const el = targetRef?.value ?? targetRef\r\n if (!el || !scaler) return\r\n const s = scaler.refresh()\r\n scale.value = s\r\n inverseScale.value = getInverseScale(el)\r\n }\r\n\r\n return { scale, inverseScale, refresh }\r\n}\r\n"],"names":["resolveEl","el","document","querySelector","HTMLElement","value","current","calculateScale","sourceWidth","sourceHeight","targetWidth","window","innerWidth","targetHeight","innerHeight","mode","visualWidth","scale","sx","sy","Math","min","applyScale","origin","target","style","transform","transformOrigin","getPageScale","t","getComputedStyle","m2","match","parseFloat","m3","sm","vals","split","map","v","trim","length","getInverseScale","s","createScaler","options","Error","autoResize","currentScale","handler","refresh","next","offsetWidth","offsetHeight","documentElement","clientWidth","viewportWidth","width","height","round","start","addEventListener","stop","removeEventListener","getScale","vFitScreen","mounted","binding","scaler","__fitScreenScaler","updated","unmounted","targetRef","ref","inverseScale","onResize","setup","onMounted","watch","onUnmounted"],"mappings":"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"}
package/dist/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/dist/index.esm.js CHANGED
@@ -1,2 +1,2 @@
1
- import{ref as e,onMounted as t,watch as n,onUnmounted as r}from"vue";function i(e){return e?"string"==typeof e?document.querySelector(e):e instanceof HTMLElement?e:e.value instanceof HTMLElement?e.value:e.current instanceof HTMLElement?e.current:null:null}function o({sourceWidth:e,sourceHeight:t,targetWidth:n=("undefined"!=typeof window?window.innerWidth:1920),targetHeight:r=("undefined"!=typeof window?window.innerHeight:1080),mode:i="contain",visualWidth:o=450,screenWidth:s=("undefined"!=typeof window?window.screen.width:1920)}){if(!e||!t||e<=0||t<=0)return 1;let u;if("fixed-width"===i)u=s<o?1:o/Math.max(n,s);else{const i=n/e,o=r/t;u=Math.min(i,o)}return u}function s(e,t,n="top center"){const r=i(e);r&&(r.style.transform=`scale(${t})`,r.style.transformOrigin=n)}function u(e){const t=i(e);if(!t)return 1;const n=window.getComputedStyle(t).transform;if(!n||"none"===n)return 1;const r=n.match(/matrix\(([^,]+),\s*[^,]+,\s*[^,]+,\s*([^,]+)/);if(r)return(parseFloat(r[1])+parseFloat(r[2]))/2;const o=n.match(/matrix3d\(([^,]+),[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,([^,]+)\)/);if(o)return(parseFloat(o[1])+parseFloat(o[2]))/2;const s=n.match(/scale\(([^)]+)\)/);if(s){const e=s[1].split(",").map(e=>parseFloat(e.trim()));return e.length>=2?(e[0]+e[1])/2:e[0]}return 1}function a(e=".share-page"){const t=u(e);return t>0?1/t:1}function c(e,t={}){const n=i(e);if(!n)throw new Error("[one-screen-scale] element not found");const{mode:r="contain",visualWidth:u=450,origin:a="top center",autoResize:c=!0}=t;let l=1,d=null;function f(){const e=o({sourceWidth:n.offsetWidth,sourceHeight:n.offsetHeight,targetWidth:window.innerWidth,targetHeight:window.innerHeight,mode:r,visualWidth:u,screenWidth:window.screen.width});return e!==l&&(l=e,s(n,e,a),"fixed-width"===r&&e>0&&(n.style.width=`${Math.max(window.innerWidth,window.screen.width)}px`,n.style.height=`${Math.round(window.innerHeight/e)}px`)),l}return{start:function(){return f(),c&&(d=()=>f(),window.addEventListener("resize",d)),l},stop:function(){d&&(window.removeEventListener("resize",d),d=null)},refresh:f,getScale:()=>l}}const l={mounted(e,t){const n=c(e,t.value||{});n.start(),e.__fitScreenScaler=n},updated(e,t){const n=e.__fitScreenScaler;n&&(t.value,n.refresh())},unmounted(e){const t=e.__fitScreenScaler;t&&(t.stop(),delete e.__fitScreenScaler)}};function d(i,o={}){const s=e(1),u=e(1);let l=null,d=null;function f(e){if(!e)return;l=c(e,o);const t=l.start();s.value=t,u.value=a(e),d=()=>{s.value=l?.getScale?.()??1,u.value=a(e)},window.addEventListener("resize",d)}t(()=>{f(i?.value??i)}),i&&"object"==typeof i&&"value"in i&&n(()=>i.value,e=>{e&&!l&&f(e)}),r(()=>{d&&(window.removeEventListener("resize",d),d=null),l?.stop(),l=null});return{scale:s,inverseScale:u,refresh:()=>{const e=i?.value??i;if(!e||!l)return;const t=l.refresh();s.value=t,u.value=a(e)}}}export{s as applyScale,o as calculateScale,c as createScaler,a as getInverseScale,u as getPageScale,d as useFitScreen,l as vFitScreen};
1
+ import{ref as e,onMounted as t,watch as n,onUnmounted as r}from"vue";function i(e){return e?"string"==typeof e?document.querySelector(e):e instanceof HTMLElement?e:e.value instanceof HTMLElement?e.value:e.current instanceof HTMLElement?e.current:null:null}function o({sourceWidth:e,sourceHeight:t,targetWidth:n=("undefined"!=typeof window?window.innerWidth:1920),targetHeight:r=("undefined"!=typeof window?window.innerHeight:1080),mode:i="contain",visualWidth:o=450}){if(!e||!t||e<=0||t<=0)return 1;let s;if("fixed-width"===i)s=n>o?o/n:1;else{const i=n/e,o=r/t;s=Math.min(i,o)}return s}function s(e,t,n="top center"){const r=i(e);r&&(r.style.transform=`scale(${t})`,r.style.transformOrigin=n)}function u(e){const t=i(e);if(!t)return 1;const n=window.getComputedStyle(t).transform;if(!n||"none"===n)return 1;const r=n.match(/matrix\(([^,]+),\s*[^,]+,\s*[^,]+,\s*([^,]+)/);if(r)return(parseFloat(r[1])+parseFloat(r[2]))/2;const o=n.match(/matrix3d\(([^,]+),[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,[^,]+,([^,]+)\)/);if(o)return(parseFloat(o[1])+parseFloat(o[2]))/2;const s=n.match(/scale\(([^)]+)\)/);if(s){const e=s[1].split(",").map(e=>parseFloat(e.trim()));return e.length>=2?(e[0]+e[1])/2:e[0]}return 1}function c(e=".share-page"){const t=u(e);return t>0?1/t:1}function l(e,t={}){const n=i(e);if(!n)throw new Error("[one-screen-scale] element not found");const{mode:r="contain",visualWidth:u=450,origin:c="top center",autoResize:l=!0}=t;let a=1,d=null;function f(){const e=o({sourceWidth:n.offsetWidth,sourceHeight:n.offsetHeight,targetWidth:document.documentElement.clientWidth||window.innerWidth,targetHeight:window.innerHeight,mode:r,visualWidth:u});if(e!==a&&(a=e,s(n,e,c)),"fixed-width"===r&&e>0){const t=document.documentElement.clientWidth||window.innerWidth;n.style.width=`${t}px`,n.style.height=`${Math.round(window.innerHeight/e)}px`}return a}return{start:function(){return f(),l&&(d=()=>f(),window.addEventListener("resize",d)),a},stop:function(){d&&(window.removeEventListener("resize",d),d=null)},refresh:f,getScale:()=>a}}const a={mounted(e,t){const n=l(e,t.value||{});n.start(),e.__fitScreenScaler=n},updated(e,t){const n=e.__fitScreenScaler;n&&(t.value,n.refresh())},unmounted(e){const t=e.__fitScreenScaler;t&&(t.stop(),delete e.__fitScreenScaler)}};function d(i,o={}){const s=e(1),u=e(1);let a=null,d=null;function f(e){if(!e)return;a=l(e,o);const t=a.start();s.value=t,u.value=c(e),d=()=>{s.value=a?.getScale?.()??1,u.value=c(e)},window.addEventListener("resize",d)}t(()=>{f(i?.value??i)}),i&&"object"==typeof i&&"value"in i&&n(()=>i.value,e=>{e&&!a&&f(e)}),r(()=>{d&&(window.removeEventListener("resize",d),d=null),a?.stop(),a=null});return{scale:s,inverseScale:u,refresh:()=>{const e=i?.value??i;if(!e||!a)return;const t=a.refresh();s.value=t,u.value=c(e)}}}export{s as applyScale,o as calculateScale,l as createScaler,c as getInverseScale,u as getPageScale,d as useFitScreen,a as vFitScreen};
2
2
  //# sourceMappingURL=index.esm.js.map