@wapri/ghooks 0.0.0-stage → 0.2.19-beta.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.
@@ -0,0 +1,5 @@
1
+ export * from 'vue-hooks-plus';
2
+ export * from './useVh100';
3
+ export * from './useLandscape';
4
+ export * from './useTicket';
5
+ export * from './useLottie';
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from 'vue-hooks-plus';
2
+ export * from "./useVh100";
3
+ export * from "./useLandscape";
4
+ export * from "./useTicket";
5
+ export * from "./useLottie";
@@ -0,0 +1,36 @@
1
+ /**
2
+ * lottie-api 未提供类型声明,这里按仓库实际用到的 API 补充。
3
+ * 上游实现参考:https://github.com/bodymovin/lottie-api
4
+ */
5
+ declare module 'lottie-api' {
6
+ import type { AnimationItem } from 'lottie-web'
7
+
8
+ /** keyPath 命中的单个图层元素,canvas 与 svg 渲染器暴露的字段不同 */
9
+ export interface LottieApiElement {
10
+ element: {
11
+ /** canvas 渲染器下的 Image 元素 */
12
+ img?: { src: string }
13
+ /** svg 渲染器下的 <image> 元素 */
14
+ innerElem?: Element
15
+ }
16
+ }
17
+
18
+ /** 一个 keyPath 命中的图层集合 */
19
+ export interface LottieApiKeyPath {
20
+ getElements: () => LottieApiElement[]
21
+ }
22
+
23
+ /** createAnimationApi 返回的操作句柄 */
24
+ export interface LottieApi {
25
+ /** 按图层名/keyPath 取元素 */
26
+ getKeyPath: (keyPath: string) => LottieApiKeyPath
27
+ /** 当前渲染器类型 */
28
+ getRendererType: () => string
29
+ }
30
+
31
+ const lottieApi: {
32
+ createAnimationApi: (animation: AnimationItem) => LottieApi
33
+ }
34
+
35
+ export default lottieApi
36
+ }
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <div>
3
+ <p>
4
+ 当前屏幕方向:
5
+ <strong>{{ isLandscape ? '横屏' : '竖屏' }}</strong>
6
+ </p>
7
+ <p>当前视口尺寸:{{ width }} × {{ height }}</p>
8
+ <p style="color: #999">调整窗口宽度(宽>高即为横屏)可实时观察状态变化</p>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { ref, onUnmounted } from 'vue'
14
+ import { useLandscape } from '@wapri/ghooks'
15
+
16
+ const isLandscape = useLandscape()
17
+ const width = ref(window.innerWidth)
18
+ const height = ref(window.innerHeight)
19
+
20
+ const updateSize = () => {
21
+ width.value = window.innerWidth
22
+ height.value = window.innerHeight
23
+ }
24
+
25
+ window.addEventListener('resize', updateSize)
26
+ onUnmounted(() => {
27
+ window.removeEventListener('resize', updateSize)
28
+ })
29
+ </script>
@@ -0,0 +1,29 @@
1
+ <template>
2
+ <div>
3
+ <p>
4
+ 当前屏幕方向:
5
+ <strong>{{ isLandscape ? '横屏' : '竖屏' }}</strong>
6
+ </p>
7
+ <p style="color: #999">将窗口拉宽至横屏(宽>高),页面立即被遮罩覆盖并提示旋转屏幕;恢复竖屏后遮罩自动消失</p>
8
+ <div class="content-box">业务内容区域(仅在竖屏时可见)</div>
9
+ </div>
10
+ </template>
11
+
12
+ <script setup lang="ts">
13
+ import { useLandscape } from '@wapri/ghooks'
14
+ import { os } from '@wapri/gbase'
15
+
16
+ const isLandscape = useLandscape({
17
+ lock: !os.isIpad() && !os.isPC(),
18
+ tip: '请旋转您的屏幕,切换至竖屏模式',
19
+ })
20
+ </script>
21
+
22
+ <style scoped>
23
+ .content-box {
24
+ padding: 24px;
25
+ border: 1px dashed #ccc;
26
+ border-radius: 4px;
27
+ color: #666;
28
+ }
29
+ </style>
@@ -0,0 +1,10 @@
1
+ import { type Ref } from 'vue';
2
+ export interface UseLandscapeOptions {
3
+ /** 横屏时自动展示遮罩层阻止横屏浏览,竖屏后自动消失,默认 false */
4
+ lock?: boolean;
5
+ /** 遮罩层的提示文案,默认 '请旋转您的屏幕,切换至竖屏模式' */
6
+ tip?: string;
7
+ /** 遮罩层的层级,默认 9999 */
8
+ zIndex?: number | string;
9
+ }
10
+ export declare const useLandscape: (options?: UseLandscapeOptions) => Ref<boolean>;
@@ -0,0 +1,56 @@
1
+ import { ref, onMounted, onBeforeUnmount } from 'vue';
2
+ var isClient = typeof window !== 'undefined';
3
+ var DEFAULT_TIP = '请旋转您的屏幕,切换至竖屏模式';
4
+ var DEFAULT_Z_INDEX = 9999;
5
+ var checkIsLandscape = function checkIsLandscape() {
6
+ return window.innerWidth > window.innerHeight;
7
+ };
8
+ export var useLandscape = function useLandscape() {
9
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
10
+ var _options$lock = options.lock,
11
+ lock = _options$lock === void 0 ? false : _options$lock,
12
+ _options$tip = options.tip,
13
+ tip = _options$tip === void 0 ? DEFAULT_TIP : _options$tip,
14
+ _options$zIndex = options.zIndex,
15
+ zIndex = _options$zIndex === void 0 ? DEFAULT_Z_INDEX : _options$zIndex;
16
+ var isLandscape = ref(isClient ? checkIsLandscape() : false);
17
+ var mask = null;
18
+ var removeMask = function removeMask() {
19
+ if (!mask) return;
20
+ mask.remove();
21
+ mask = null;
22
+ };
23
+ var createMask = function createMask() {
24
+ if (mask) return;
25
+ var maskNode = document.createElement('div');
26
+ maskNode.style.cssText = ['position: fixed', 'top: 0', 'right: 0', 'bottom: 0', 'left: 0', "z-index: ".concat(zIndex), 'display: flex', 'align-items: center', 'justify-content: center', 'background: #fff', 'touch-action: none'].join(';');
27
+ var text = document.createElement('p');
28
+ text.style.cssText = ['margin: 0', 'padding: 0 32px', 'font-size: 16px', 'line-height: 1.6', 'color: #333', 'text-align: center'].join(';');
29
+ text.textContent = tip;
30
+ maskNode.appendChild(text);
31
+ document.body.appendChild(maskNode);
32
+ mask = maskNode;
33
+ };
34
+ var update = function update() {
35
+ isLandscape.value = checkIsLandscape();
36
+ if (!lock) return;
37
+ if (isLandscape.value) {
38
+ createMask();
39
+ } else {
40
+ removeMask();
41
+ }
42
+ };
43
+ onMounted(function () {
44
+ if (!isClient) return;
45
+ update();
46
+ window.addEventListener('orientationchange', update);
47
+ window.addEventListener('resize', update);
48
+ });
49
+ onBeforeUnmount(function () {
50
+ if (!isClient) return;
51
+ window.removeEventListener('orientationchange', update);
52
+ window.removeEventListener('resize', update);
53
+ removeMask();
54
+ });
55
+ return isLandscape;
56
+ };
@@ -0,0 +1,150 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { useLottie } from '@wapri/ghooks'
4
+ import animationData from './包邮.json'
5
+
6
+ const container = ref<HTMLElement>()
7
+ const status = ref('等待加载')
8
+
9
+ const { instance, play, pause, stop, playSegments, playAnimation, setLoop, getDuration } = useLottie(
10
+ container,
11
+ animationData,
12
+ {
13
+ loop: true,
14
+ onReady: (item) => {
15
+ status.value = `加载完成,共 ${item.totalFrames} 帧`
16
+ },
17
+ },
18
+ )
19
+
20
+ /** 一次播放两段区间 */
21
+ const handleSegmentPlay = () => {
22
+ setLoop(true)
23
+ status.value = '区间播放:[0,24] + [24,72]'
24
+ playSegments([
25
+ [0, 24],
26
+ [24, 72],
27
+ ])
28
+ }
29
+
30
+ /** 关闭循环后按顺序异步播放两段,每段结束才进入下一段 */
31
+ const handleAsyncPlay = async () => {
32
+ setLoop(false)
33
+ status.value = '第一段开始'
34
+ await playAnimation([[0, 24]], true)
35
+ status.value = '第一段结束'
36
+ await new Promise((resolve) => setTimeout(resolve, 600))
37
+ status.value = '第二段开始'
38
+ await playAnimation([24, 72])
39
+ status.value = '第二段结束'
40
+ }
41
+ </script>
42
+
43
+ <template>
44
+ <div class="lottie-demo">
45
+ <div
46
+ ref="container"
47
+ class="lottie-demo__stage"
48
+ />
49
+
50
+ <div class="lottie-demo__panel">
51
+ <div class="lottie-demo__actions">
52
+ <button
53
+ type="button"
54
+ @click="play"
55
+ >
56
+ 播放
57
+ </button>
58
+ <button
59
+ type="button"
60
+ @click="pause"
61
+ >
62
+ 暂停
63
+ </button>
64
+ <button
65
+ type="button"
66
+ @click="stop"
67
+ >
68
+ 停止
69
+ </button>
70
+ <button
71
+ type="button"
72
+ @click="handleSegmentPlay"
73
+ >
74
+ 帧播放
75
+ </button>
76
+ <button
77
+ type="button"
78
+ @click="handleAsyncPlay"
79
+ >
80
+ 异步多段动画
81
+ </button>
82
+ </div>
83
+
84
+ <p class="lottie-demo__status">{{ status }}</p>
85
+ <p class="lottie-demo__status">
86
+ {{ instance ? `实例已就绪,总时长 ${getDuration(false).toFixed(2)}s` : '实例加载中…' }}
87
+ </p>
88
+ </div>
89
+ </div>
90
+ </template>
91
+
92
+ <style scoped>
93
+ .lottie-demo {
94
+ display: flex;
95
+ max-width: 760px;
96
+ flex-wrap: wrap;
97
+ align-items: center;
98
+ gap: 20px;
99
+ padding: 20px;
100
+ border: 1px solid #e5e7eb;
101
+ border-radius: 8px;
102
+ color: #1f2937;
103
+ }
104
+
105
+ .lottie-demo__stage {
106
+ width: 240px;
107
+ height: 240px;
108
+ flex: 0 0 auto;
109
+ border-radius: 8px;
110
+ background: #f8fafc;
111
+ }
112
+
113
+ .lottie-demo__panel {
114
+ flex: 1;
115
+ min-width: 260px;
116
+ }
117
+
118
+ .lottie-demo__actions {
119
+ display: flex;
120
+ flex-wrap: wrap;
121
+ gap: 8px;
122
+ }
123
+
124
+ .lottie-demo__actions button {
125
+ height: 32px;
126
+ padding: 0 14px;
127
+ border: 1px solid #d1d5db;
128
+ border-radius: 4px;
129
+ background: #fff;
130
+ color: inherit;
131
+ font: inherit;
132
+ font-size: 14px;
133
+ cursor: pointer;
134
+ transition:
135
+ border-color 0.2s,
136
+ color 0.2s;
137
+ }
138
+
139
+ .lottie-demo__actions button:hover {
140
+ border-color: #f97316;
141
+ color: #ea580c;
142
+ }
143
+
144
+ .lottie-demo__status {
145
+ margin: 14px 0 0;
146
+ color: #6b7280;
147
+ font-size: 13px;
148
+ line-height: 1.8;
149
+ }
150
+ </style>
@@ -0,0 +1,169 @@
1
+ <script setup lang="ts">
2
+ import { onMounted, ref } from 'vue'
3
+ import { useLottie } from '@wapri/ghooks'
4
+
5
+ /** 远程 json 地址:交给 lottie-web 自己发请求,不需要先 fetch(示例地址,换成自己的 CDN 即可) */
6
+ const REMOTE_URL = 'https://cdn.example.com/lottie/loading.json'
7
+
8
+ const container = ref<HTMLElement>()
9
+ const status = ref('远程资源加载中…')
10
+
11
+ const { instance, play, pause, stop, playSegments, playAnimation, setLoop, getDuration, on } = useLottie(
12
+ container,
13
+ null,
14
+ {
15
+ path: REMOTE_URL,
16
+ loop: true,
17
+ onReady: (item) => {
18
+ status.value = `远程资源加载完成,共 ${item.totalFrames} 帧`
19
+ },
20
+ },
21
+ )
22
+
23
+ // useLottie 在 onMounted 里创建实例,这里注册晚于它,所以能拿到实例
24
+ onMounted(() => {
25
+ on('data_failed', () => {
26
+ status.value = '远程资源加载失败:请检查地址是否可访问、以及是否允许跨域'
27
+ })
28
+ })
29
+
30
+ /** 一次播放两段区间 */
31
+ const handleSegmentPlay = () => {
32
+ setLoop(true)
33
+ status.value = '区间播放:[0,24] + [24,72]'
34
+ playSegments([
35
+ [0, 24],
36
+ [24, 72],
37
+ ])
38
+ }
39
+
40
+ /** 关闭循环后按顺序异步播放两段,每段结束才进入下一段 */
41
+ const handleAsyncPlay = async () => {
42
+ setLoop(false)
43
+ status.value = '第一段开始'
44
+ await playAnimation([[0, 24]], true)
45
+ status.value = '第一段结束'
46
+ await new Promise((resolve) => setTimeout(resolve, 600))
47
+ status.value = '第二段开始'
48
+ await playAnimation([24, 72])
49
+ status.value = '第二段结束'
50
+ }
51
+ </script>
52
+
53
+ <template>
54
+ <div class="lottie-remote-demo">
55
+ <div
56
+ ref="container"
57
+ class="lottie-remote-demo__stage"
58
+ ></div>
59
+
60
+ <div class="lottie-remote-demo__panel">
61
+ <div class="lottie-remote-demo__actions">
62
+ <button
63
+ type="button"
64
+ @click="play"
65
+ >
66
+ 播放
67
+ </button>
68
+ <button
69
+ type="button"
70
+ @click="pause"
71
+ >
72
+ 暂停
73
+ </button>
74
+ <button
75
+ type="button"
76
+ @click="stop"
77
+ >
78
+ 停止
79
+ </button>
80
+ <button
81
+ type="button"
82
+ @click="handleSegmentPlay"
83
+ >
84
+ 帧播放
85
+ </button>
86
+ <button
87
+ type="button"
88
+ @click="handleAsyncPlay"
89
+ >
90
+ 异步多段动画
91
+ </button>
92
+ </div>
93
+
94
+ <p class="lottie-remote-demo__status">{{ status }}</p>
95
+ <p class="lottie-remote-demo__status">
96
+ {{ instance ? `实例已就绪,总时长 ${getDuration(false).toFixed(2)}s` : '实例加载中…' }}
97
+ </p>
98
+ <p class="lottie-remote-demo__url">{{ REMOTE_URL }}</p>
99
+ </div>
100
+ </div>
101
+ </template>
102
+
103
+ <style scoped>
104
+ .lottie-remote-demo {
105
+ display: flex;
106
+ max-width: 760px;
107
+ flex-wrap: wrap;
108
+ align-items: center;
109
+ gap: 20px;
110
+ padding: 20px;
111
+ border: 1px solid #e5e7eb;
112
+ border-radius: 8px;
113
+ color: #1f2937;
114
+ }
115
+
116
+ .lottie-remote-demo__stage {
117
+ width: 240px;
118
+ height: 240px;
119
+ flex: 0 0 auto;
120
+ border-radius: 8px;
121
+ background: #f8fafc;
122
+ }
123
+
124
+ .lottie-remote-demo__panel {
125
+ flex: 1;
126
+ min-width: 260px;
127
+ }
128
+
129
+ .lottie-remote-demo__actions {
130
+ display: flex;
131
+ flex-wrap: wrap;
132
+ gap: 8px;
133
+ }
134
+
135
+ .lottie-remote-demo__actions button {
136
+ height: 32px;
137
+ padding: 0 14px;
138
+ border: 1px solid #d1d5db;
139
+ border-radius: 4px;
140
+ background: #fff;
141
+ color: inherit;
142
+ font: inherit;
143
+ font-size: 14px;
144
+ cursor: pointer;
145
+ transition:
146
+ border-color 0.2s,
147
+ color 0.2s;
148
+ }
149
+
150
+ .lottie-remote-demo__actions button:hover {
151
+ border-color: #f97316;
152
+ color: #ea580c;
153
+ }
154
+
155
+ .lottie-remote-demo__status {
156
+ margin: 14px 0 0;
157
+ color: #6b7280;
158
+ font-size: 13px;
159
+ line-height: 1.8;
160
+ }
161
+
162
+ .lottie-remote-demo__url {
163
+ margin: 8px 0 0;
164
+ color: #9ca3af;
165
+ font-size: 12px;
166
+ line-height: 1.6;
167
+ word-break: break-all;
168
+ }
169
+ </style>