@shijiu/jsview-vue-samples 2.1.200 → 2.1.340-test.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.
Files changed (59) hide show
  1. package/AnimPicture/App.vue +224 -120
  2. package/AnimPicture/Item.vue +44 -0
  3. package/ConnectLine/App.vue +173 -0
  4. package/CoupletsTest/App.vue +212 -0
  5. package/CoupletsTest/Common/SpriteDeal.js +30 -0
  6. package/CoupletsTest/LeadWire.vue +221 -0
  7. package/CoupletsTest/Maroon.vue +112 -0
  8. package/CoupletsTest/Salvo.vue +251 -0
  9. package/CoupletsTest/Sprite/firecracker.json +212 -0
  10. package/CoupletsTest/Sprite/firecracker.png +0 -0
  11. package/CoupletsTest/Sprite/fireworks.json +220 -0
  12. package/CoupletsTest/Sprite/fireworks.png +0 -0
  13. package/CoupletsTest/Sprite/scroll.json +76 -0
  14. package/CoupletsTest/Sprite/scroll.png +0 -0
  15. package/CoupletsTest/Sprite/spark.json +268 -0
  16. package/CoupletsTest/Sprite/spark.png +0 -0
  17. package/CoupletsTest/images/Couplets.png +0 -0
  18. package/CoupletsTest/images/goldencoin1.png +0 -0
  19. package/CoupletsTest/images/goldencoin2.png +0 -0
  20. package/CoupletsTest/images/leadWire.png +0 -0
  21. package/CoupletsTest/images/line.png +0 -0
  22. package/CoupletsTest/images/purpleStar.png +0 -0
  23. package/CoupletsTest/images/redStar.png +0 -0
  24. package/CoupletsTest/images/scroll1.png +0 -0
  25. package/CoupletsTest/images/star1.png +0 -0
  26. package/CoupletsTest/images/star2.png +0 -0
  27. package/CoupletsTest/images/star3.png +0 -0
  28. package/CoupletsTest/images/star4.png +0 -0
  29. package/CoupletsTest/images/yellowStar.png +0 -0
  30. package/DemoHomepage/components/BodyFrame.vue +27 -11
  31. package/DemoHomepage/router.js +35 -5
  32. package/DemoHomepage/views/Homepage.vue +1 -1
  33. package/DispersedItemSample/DispersedItemSample.vue +138 -0
  34. package/DispersedItemSample/DispersedItemWidget/DispersedItemWidget.vue +358 -0
  35. package/DispersedItemSample/DispersedItemWidget/MyRenderItem.ts +115 -0
  36. package/DispersedItemSample/Item.vue +55 -0
  37. package/FilterDemo/AnimatePic.vue +5 -6
  38. package/FocusMoveStyle/App.vue +126 -110
  39. package/FocusMoveStyle/CreepFocus.vue +128 -0
  40. package/FocusMoveStyle/FoldableItem.vue +279 -0
  41. package/FocusMoveStyle/Item.vue +32 -31
  42. package/FreeMove/App.vue +2 -2
  43. package/ImpactStop/App.vue +343 -384
  44. package/LatexDemo/App.vue +11 -0
  45. package/MetroWidgetDemos/RefreshDemo/App.vue +101 -0
  46. package/MetroWidgetDemos/RefreshDemo/Item.vue +116 -0
  47. package/MetroWidgetDemos/RefreshDemo/assets/imageList.json +237 -0
  48. package/MetroWidgetDemos/RefreshDemo/data.js +16 -0
  49. package/MetroWidgetDemos/TripleWidget/App.vue +81 -0
  50. package/MetroWidgetDemos/TripleWidget/Item.vue +64 -0
  51. package/MetroWidgetDemos/TripleWidget/SWidgetItem.vue +93 -0
  52. package/MetroWidgetDemos/TripleWidget/WidgetItem.vue +111 -0
  53. package/MetroWidgetDemos/routeList.js +12 -0
  54. package/ProgressBar/App.vue +128 -0
  55. package/QrcodeDemo/App.vue +2 -2
  56. package/SpriteImage/App.vue +113 -68
  57. package/SwiperTest/App.vue +105 -0
  58. package/ViewOpacity/App.vue +98 -0
  59. package/package.json +1 -1
@@ -0,0 +1,358 @@
1
+ <!--
2
+ * 【界面概述】
3
+ * 展示DispersedItemWidget控件的用法
4
+ *
5
+ * 【控件介绍】
6
+ * DispersedItemWidget:
7
+ * props:
8
+ * data {Array} (必传) 用于设置离散型焦点管理的数组对象
9
+ 单个对象格式为:{
10
+ width: {int} (必传) 当前item的宽度
11
+ height: {int} (必传) 当前item的高度
12
+ top: {int} (必传) 当前item的top值
13
+ left {int} (必传) 当前item的left值
14
+ SpecIndex {Object} (可选) 指定当前item的某个方向键跳转到目标index
15
+ 对应方向键,属性为: up,down,left,right
16
+ }
17
+ * name {string} (必传) 用于设置焦点的名称
18
+ * initFocusId {int} (可选) 初始聚焦Index,默认为0
19
+ * onEdge (Function) (可选) 当前item在某个方向上无法移动时触发
20
+ slots:
21
+ renderItem: 该slot用于描画每个单元格
22
+ * 【技巧说明】
23
+ * Q: 插槽props如何使用?
24
+ * A: data: 当前item的数据
25
+ * query: 获取一些额外信息的对象
26
+ {
27
+ id: {int} item的id,
28
+ index: {int} item的index
29
+ }
30
+ * onAction: 单元格内控件需要通过onAction.register方法向MetroWidget注册 item 的回调,回调函数有
31
+ onFocus, onBlur, onClick,
32
+ *
33
+ * Q: 单元格的普通状态,焦点状态,失焦状态如何渲染?
34
+ * A: 在单元的空间中通过往onAction注册的onFocus,onBlur控制显示
35
+ *
36
+ * Q: 焦点怎么移出控件?
37
+ * A: 当焦点移动到控件边缘时,会调用onEdge回调。在回调中通过参数传递的值来决定焦点转移的行为
38
+ *
39
+ -->
40
+
41
+ <template>
42
+ <jsv-focus-block
43
+ ref="focusNode"
44
+ :name="props.name"
45
+ :onAction="{
46
+ onFocus: focusBlockOnFocus,
47
+ onBlur: focusBlockOnBlur,
48
+ onKeyDown: focusBlockOnKeyDown,
49
+ }"
50
+ >
51
+ <div
52
+ v-for="(item, index) in innerData"
53
+ :key="'_' + item.index"
54
+ :ref="item.divRef"
55
+ :style="{
56
+ left: item.left,
57
+ top: item.top,
58
+ width: item.width,
59
+ height: item.height,
60
+ }"
61
+ >
62
+ <div :key="`${props.name}_${index}`" :id="`${props.name}_${index}`">
63
+ <slot
64
+ name="renderItem"
65
+ :data="item.customerData"
66
+ :onAction="item.registerObj"
67
+ :query="item.query"
68
+ ></slot>
69
+ </div>
70
+ </div>
71
+ </jsv-focus-block>
72
+ </template>
73
+
74
+ <script setup>
75
+ import { MyRenderItem } from "./MyRenderItem";
76
+ import { nextTick, shallowRef } from "vue";
77
+ let focusNode = shallowRef(null);
78
+ let callFocusAfterUpdate = false;
79
+ let focusId = 0;
80
+ let preEdgeRect = null;
81
+ let id = 0;
82
+ let index = 0;
83
+ let innerData = [];
84
+ let preFocusId = -1;
85
+ let isFocus = false;
86
+ let onKeyDownLock = false;
87
+ const props = defineProps({
88
+ data: {
89
+ type: Array,
90
+ require: true,
91
+ },
92
+ initFocusId: {
93
+ type: Number,
94
+ },
95
+ onEdge: {
96
+ type: Function,
97
+ },
98
+ SpecIndex: {
99
+ type: Object,
100
+ },
101
+ name: {
102
+ type: String,
103
+ require: true,
104
+ },
105
+ });
106
+
107
+ const onItemFocus = (focusIndex) => {
108
+ innerData[focusIndex].customerCallbackMap.onFocus();
109
+ return innerData[focusIndex];
110
+ };
111
+
112
+ const onItemBlur = (blurIndex) => {
113
+ innerData[blurIndex].customerCallbackMap.onBlur();
114
+ return innerData[blurIndex];
115
+ };
116
+ const getItemByIndex = (index) => {
117
+ return innerData[index];
118
+ };
119
+ const _getPosition = (index) => {
120
+ let templateInfo = getItemByIndex(index);
121
+ return {
122
+ left: templateInfo.customerData.left,
123
+ top: templateInfo.customerData.top,
124
+ width: templateInfo.customerData.width,
125
+ height: templateInfo.customerData.height,
126
+ };
127
+ };
128
+ const draw = (customerData) => {
129
+ const item = new MyRenderItem(
130
+ customerData,
131
+ () => {
132
+ if (callFocusAfterUpdate && item.id === focusId) {
133
+ nextTick(() => {
134
+ onItemFocus(item, preEdgeRect);
135
+ });
136
+ callFocusAfterUpdate = false;
137
+ }
138
+ },
139
+ //query object
140
+ {
141
+ id: id++,
142
+ index: index++,
143
+ position: _getPosition,
144
+ }
145
+ );
146
+ innerData.push(item);
147
+ };
148
+
149
+ const onItemClick = (focusIndex) => {
150
+ innerData[focusIndex].customerCallbackMap.onClick();
151
+ };
152
+
153
+ //算角度
154
+ const getAngleRange = (direction) => {
155
+ switch (direction) {
156
+ case "up":
157
+ return [(5 * Math.PI) / 4, (7 * Math.PI) / 4];
158
+ case "left":
159
+ return [(3 * Math.PI) / 4, (5 * Math.PI) / 4];
160
+ case "down":
161
+ return [Math.PI / 4, (3 * Math.PI) / 4];
162
+ case "right":
163
+ return [(7 * Math.PI) / 4, Math.PI / 4];
164
+ default:
165
+ console.log("direction无效值!");
166
+ }
167
+ };
168
+
169
+ const isAngleInRange = (angle, range) => {
170
+ const [start, end] = range;
171
+ if (start < end) {
172
+ return angle >= start && angle <= end;
173
+ } else {
174
+ return angle >= start || angle <= end;
175
+ }
176
+ };
177
+
178
+ const calculateAngle = (currentCenter, nextCenter) => {
179
+ const deltaX = nextCenter.x - currentCenter.x;
180
+ const deltaY = nextCenter.y - currentCenter.y;
181
+ let angle = Math.atan2(deltaY, deltaX);
182
+
183
+ if (angle < 0) {
184
+ angle += 2 * Math.PI; // 将负角度转换为正角度
185
+ }
186
+
187
+ return angle;
188
+ };
189
+ const changeFocus = (currentIndex, nextIndex) => {
190
+ onItemBlur(currentIndex);
191
+ focusId = nextIndex;
192
+ onItemFocus(nextIndex);
193
+ };
194
+ const _moveToNext = (direction) => {
195
+ const currentIndex = focusId;
196
+ let nextIndex = -1;
197
+ let centerList = [];
198
+ let minDistance = Infinity;
199
+ //中心点创建数组管理
200
+ innerData.forEach((item, index) => {
201
+ centerList.push({
202
+ x: item.customerData.left + item.customerData.width / 2,
203
+ y: item.customerData.top + item.customerData.height / 2,
204
+ });
205
+ });
206
+ const currentCenter = centerList[currentIndex];
207
+ const angleRange = getAngleRange(direction);
208
+
209
+ for (let i = 0; i < centerList.length; i++) {
210
+ if (i === currentIndex) continue; // 跳过当前聚焦的元素
211
+ const nextCenter = centerList[i];
212
+ const angle = calculateAngle(currentCenter, nextCenter);
213
+ const distance = calculateDistance(currentCenter, nextCenter);
214
+ if (
215
+ isMovingInDirection(currentCenter, nextCenter, direction) &&
216
+ isAngleInRange(angle, angleRange) &&
217
+ distance < minDistance
218
+ ) {
219
+ minDistance = distance;
220
+ nextIndex = i;
221
+ }
222
+ }
223
+ if (nextIndex !== -1) {
224
+ // 移动焦点到距离最短的元素
225
+ changeFocus(currentIndex, nextIndex);
226
+ } else {
227
+ props?.onEdge();
228
+ }
229
+ };
230
+
231
+ const _appointToNext = (direction) => {
232
+ const currentIndex = focusId;
233
+ let nextIndex;
234
+ switch (direction) {
235
+ case "up":
236
+ nextIndex = innerData[currentIndex].customerData.SpecIndex.up;
237
+ changeFocus(currentIndex, nextIndex);
238
+ break;
239
+ case "left":
240
+ nextIndex = innerData[currentIndex].customerData.SpecIndex.left;
241
+ changeFocus(currentIndex, nextIndex);
242
+ break;
243
+ case "down":
244
+ nextIndex = innerData[currentIndex].customerData.SpecIndex.down;
245
+ changeFocus(currentIndex, nextIndex);
246
+ break;
247
+ case "right":
248
+ nextIndex = innerData[currentIndex].customerData.SpecIndex.right;
249
+ changeFocus(currentIndex, nextIndex);
250
+ break;
251
+ default:
252
+ return false;
253
+ }
254
+ };
255
+ const calculateDistance = (currentCenter, nextCenter) => {
256
+ const deltaX = nextCenter.x - currentCenter.x;
257
+ const deltaY = nextCenter.y - currentCenter.y;
258
+ return Math.sqrt(deltaX * deltaX + deltaY * deltaY);
259
+ };
260
+ const isMovingInDirection = (currentCenter, nextCenter, direction) => {
261
+ switch (direction) {
262
+ case "up":
263
+ return nextCenter.y < currentCenter.y;
264
+ case "left":
265
+ return nextCenter.x < currentCenter.x;
266
+ case "down":
267
+ return nextCenter.y > currentCenter.y;
268
+ case "right":
269
+ return nextCenter.x > currentCenter.x;
270
+ default:
271
+ return false;
272
+ }
273
+ };
274
+ const focusBlockOnKeyDown = (ev) => {
275
+ if (onKeyDownLock) {
276
+ return true;
277
+ }
278
+ switch (ev.keyCode) {
279
+ case 37:
280
+ if (
281
+ innerData[focusId].customerData.SpecIndex &&
282
+ innerData[focusId].customerData.SpecIndex.left >= 0
283
+ ) {
284
+ _appointToNext("left");
285
+ } else {
286
+ _moveToNext("left");
287
+ }
288
+ break;
289
+ case 38:
290
+ if (
291
+ innerData[focusId].customerData.SpecIndex &&
292
+ innerData[focusId].customerData.SpecIndex.up >= 0
293
+ ) {
294
+ _appointToNext("up");
295
+ } else {
296
+ _moveToNext("up");
297
+ }
298
+ break;
299
+ case 39:
300
+ if (
301
+ innerData[focusId].customerData.SpecIndex &&
302
+ innerData[focusId].customerData.SpecIndex.right >= 0
303
+ ) {
304
+ _appointToNext("right");
305
+ } else {
306
+ _moveToNext("right");
307
+ }
308
+ break;
309
+ case 40:
310
+ if (
311
+ innerData[focusId].customerData.SpecIndex &&
312
+ innerData[focusId].customerData.SpecIndex.down >= 0
313
+ ) {
314
+ _appointToNext("down");
315
+ } else {
316
+ _moveToNext("down");
317
+ }
318
+ break;
319
+ case 13:
320
+ onItemClick(focusId);
321
+ break;
322
+ default:
323
+ //只接受上下左右确定键
324
+ return false;
325
+ }
326
+ return true;
327
+ };
328
+ //循环遍历传入的数组造innerData的数据
329
+ props.data.forEach((item, index) => {
330
+ draw(item);
331
+ });
332
+
333
+ //焦点管理
334
+ const focusBlockOnFocus = () => {
335
+ isFocus = true;
336
+ preFocusId = -1;
337
+ if (innerData.length === 0) {
338
+ console.log(`MetroWidget: ${props.name} get focus while data is empty.`);
339
+ return;
340
+ }
341
+ if (props.initFocusId < 0 || props.initFocusId > innerData.length - 1) {
342
+ console.error("initFocusId无效!");
343
+ }
344
+ let focus_id = props.initFocusId ? props.initFocusId : 0;
345
+ focusId = focus_id;
346
+ onItemFocus(focusId);
347
+ props.onFocus?.();
348
+ };
349
+
350
+ const focusBlockOnBlur = () => {
351
+ isFocus = false;
352
+ preFocusId = focusId;
353
+ onItemBlur(preFocusId);
354
+ props.onBlur?.();
355
+ };
356
+ </script>
357
+
358
+ <style lang="scss" scoped></style>
@@ -0,0 +1,115 @@
1
+ import { ref } from "vue";
2
+ import type { Ref } from "vue";
3
+
4
+ interface CustomerCallbackMap {
5
+ onFocus?: (rect: object) => void;
6
+ onBlur?: () => void;
7
+ onClick?: () => void;
8
+ onWidgetEdge?: (rect: object) => void;
9
+ }
10
+
11
+ export class MyRenderItem {
12
+ private divMountedCallback: Array<(div: HTMLDivElement) => void> = [];
13
+
14
+ public customerData: object;
15
+ public mounted: Ref<boolean> = ref(false);
16
+ public touchInit: boolean = false;
17
+ public rootDiv: HTMLDivElement | null = null;
18
+
19
+ private customerCallbackMap: CustomerCallbackMap = {};
20
+ private onRef: (() => void) | null;
21
+ public readonly registerObj: object;
22
+ public readonly query: object;
23
+
24
+ constructor(customerData: object, onRef: () => void, query: object) {
25
+ this.customerData = customerData;
26
+ this.onRef = onRef;
27
+ const register = this.register.bind(this);
28
+ const unregister = this.unregister.bind(this);
29
+ this.registerObj = { register, unregister };
30
+ this.query = query;
31
+ }
32
+
33
+ //TODO item的div ref 会调用多次, 待调查
34
+ public divRef = (ele: HTMLDivElement | null) => {
35
+ this.rootDiv = ele;
36
+ if (ele) {
37
+ this.onDivMounted();
38
+ } else {
39
+ this.onDivUnmounted();
40
+ }
41
+ };
42
+
43
+ public slotRef = (ele: object | null) => {
44
+ if (!this.mounted.value && ele) {
45
+ this.mounted.value = true;
46
+ this.onRef?.();
47
+ } else if (!ele && this.mounted.value) {
48
+ this.mounted.value = false;
49
+ }
50
+ };
51
+
52
+ public register(name: string, callback: Function) {
53
+ (this.customerCallbackMap as any)[name] = callback;
54
+ }
55
+
56
+ public unregister(name: string) {
57
+ delete (this.customerCallbackMap as any)[name];
58
+ }
59
+
60
+ public onFocus(rect: object): boolean {
61
+ if (this.mounted.value) {
62
+ this.customerCallbackMap.onFocus?.(rect);
63
+ return true;
64
+ } else {
65
+ return false;
66
+ }
67
+ }
68
+
69
+ public onBlur(): boolean {
70
+ if (this.mounted.value) {
71
+ this.customerCallbackMap.onBlur?.();
72
+ return true;
73
+ } else {
74
+ return false;
75
+ }
76
+ }
77
+
78
+ public onClick(): boolean {
79
+ if (this.mounted.value) {
80
+ this.customerCallbackMap.onClick?.();
81
+ return true;
82
+ } else {
83
+ return false;
84
+ }
85
+ }
86
+
87
+ public onWidgetEdge(rect: object): boolean {
88
+ if (this.mounted.value) {
89
+ this.customerCallbackMap.onWidgetEdge?.(rect);
90
+ return true;
91
+ } else {
92
+ return false;
93
+ }
94
+ }
95
+
96
+ public addDivMountedListener(cbk: (div: HTMLDivElement) => void) {
97
+ this.divMountedCallback.push(cbk);
98
+ }
99
+
100
+ private onDivMounted(): void {
101
+ if (this.divMountedCallback.length > 0) {
102
+ this.divMountedCallback.forEach((cbk: (ele: HTMLDivElement) => void) => {
103
+ if (this.rootDiv) {
104
+ cbk(this.rootDiv);
105
+ }
106
+ });
107
+ }
108
+ this.divMountedCallback = [];
109
+ }
110
+
111
+ private onDivUnmounted(): void {
112
+ this.touchInit = false;
113
+ this.divMountedCallback = [];
114
+ }
115
+ }
@@ -0,0 +1,55 @@
1
+ <template>
2
+ <div
3
+ :style="{
4
+ width: props.data.width,
5
+ height: props.data.height,
6
+ backgroundColor: clicked
7
+ ? '#0000FF'
8
+ : focused
9
+ ? props.data.color
10
+ : '#FFFFFF',
11
+ left: props.data.left,
12
+ top: props.data.top,
13
+ fontSize: 30,
14
+ }"
15
+ >
16
+ {{ props.query.id }}
17
+ <div
18
+ :style="{
19
+ width: props.data.width,
20
+ height: 68,
21
+ fontSize: 24,
22
+ top: 32,
23
+ }"
24
+ >
25
+ {{ `left: ${props.data.left}\n` + `top: ${props.data.top}` }}
26
+ </div>
27
+ </div>
28
+ </template>
29
+
30
+ <script setup>
31
+ import { shallowRef } from "vue";
32
+ const props = defineProps({
33
+ data: Object,
34
+ onAction: Object,
35
+ query: Object,
36
+ });
37
+ let focused = shallowRef(false);
38
+ let clicked = shallowRef(false);
39
+ const onFocus = () => {
40
+ focused.value = true;
41
+ };
42
+
43
+ const onBlur = () => {
44
+ focused.value = false;
45
+ clicked.value = false;
46
+ };
47
+ const onClick = () => {
48
+ clicked.value = !clicked.value;
49
+ };
50
+ props.onAction.register("onFocus", onFocus);
51
+ props.onAction.register("onBlur", onBlur);
52
+ props.onAction.register("onClick", onClick);
53
+ </script>
54
+
55
+ <style scoped></style>
@@ -1,11 +1,10 @@
1
1
  <script setup>
2
2
  import { getDataUrl } from "../CommonUtils/ResourceData";
3
3
  //无网络环境下使用
4
- const DemoResourceBase = getDataUrl()
5
- const item_url = DemoResourceBase ?
6
- `${DemoResourceBase}/27bda620942566673ab449a3ef765321.png`
7
- :
8
- "http://oss.image.51vtv.cn/homepage/20210209/27bda620942566673ab449a3ef765321.png";
4
+ const DemoResourceBase = getDataUrl();
5
+ const item_url = DemoResourceBase
6
+ ? `${DemoResourceBase}/27bda620942566673ab449a3ef765321.png`
7
+ : "http://oss.image.qcast.cn/homepage/20210209/27bda620942566673ab449a3ef765321.png";
9
8
  </script>
10
9
 
11
10
  <template>
@@ -60,4 +59,4 @@ const item_url = DemoResourceBase ?
60
59
  to {
61
60
  }
62
61
  }
63
- </style>
62
+ </style>