@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
@@ -1,60 +1,15 @@
1
1
  <script setup>
2
2
  import {
3
- MetroWidget,
4
3
  HORIZONTAL,
5
- JsvNinePatch,
6
4
  useFocusHub,
7
5
  getTextWidth,
8
- getKeyFramesGroup,
6
+ FixPositionSlide,
7
+ ListWidget,
9
8
  } from "jsview";
10
- import Item from "./Item.vue";
9
+ import FoldableItem from "./FoldableItem.vue";
11
10
  import redCircle from "./assets/redCircle.png";
12
- import { reactive, provide, onMounted, onBeforeUnmount } from "vue";
13
-
14
- const focusHub = useFocusHub();
15
- const styleShell = getKeyFramesGroup();
16
-
17
- const animationCache = {};
18
- const addAnimation = (fromRect, toRect, name) => {
19
- if (animationCache.hasOwnProperty(name)) {
20
- styleShell.removeRule(name);
21
- delete animationCache[name];
22
- }
23
- console.log("LudlDebug anim");
24
- if (!(name in animationCache)) {
25
- const animStr = `@keyframes ${name} {
26
- 0% {
27
- left: ${fromRect.left};
28
- top: ${fromRect.top};
29
- width: ${fromRect.width};
30
- height: ${fromRect.height};
31
- }
32
- 50% {
33
- left: ${Math.min(fromRect.left, toRect.left)};
34
- top: ${Math.min(fromRect.top, toRect.top)};
35
- width: ${
36
- Math.max(fromRect.left + fromRect.width, toRect.left + toRect.width) -
37
- Math.min(fromRect.left, toRect.left)
38
- };
39
- height: ${
40
- Math.max(fromRect.top + fromRect.height, toRect.top + toRect.height) -
41
- Math.min(fromRect.top, toRect.top)
42
- };
43
- }
44
- 100% {
45
- left: ${toRect.left};
46
- top: ${toRect.top};
47
- width: ${toRect.width};
48
- height: ${toRect.height};
49
- }
50
- }`;
51
- animationCache[name] = animStr;
52
- styleShell.insertRule(animStr);
53
- }
54
- };
55
- const removeAnimation = (name) => {
56
- styleShell.removeRule(name);
57
- };
11
+ import { ref, provide, onMounted } from "vue";
12
+ import CreepFocus from "./CreepFocus.vue";
58
13
 
59
14
  const randomColor = () => {
60
15
  let randomColor = Math.round(Math.random() * 2 ** 24).toString(16);
@@ -62,40 +17,24 @@ const randomColor = () => {
62
17
  "#" + new Array(6 - randomColor.length).fill("0").join("") + randomColor
63
18
  );
64
19
  };
65
- const focusSize = reactive({
66
- width: 0,
67
- height: 0,
68
- left: 0,
69
- top: 0,
70
- animation: null,
71
- onTransitionEnd: null,
72
- });
73
-
74
- let animateChange = 0; // A B版动画切换,解决animation属性不变化引起不刷新问题
20
+ const slideType = new FixPositionSlide();
21
+ const focusHub = useFocusHub();
75
22
 
23
+ const creepFocus = ref(null);
24
+ const showFocus = ref(true);
25
+ let currentFocusRect = {};
76
26
  const focusFrameController = {
77
- preRect: null,
78
- curRect: null,
79
- focusIndex: -1,
80
- curAnimName: null,
81
- onFocusChange: function (rect, focusIndex) {
82
- this.preRect = this.curRect;
83
- this.curRect = rect;
84
- if (this.focusIndex >= 0) {
85
- const animName = "test_focusMove_" + animateChange;
86
- animateChange = animateChange == 0 ? 1 : 0; // A B 版切换
87
- addAnimation(this.preRect, this.curRect, animName);
88
- focusSize.animation = `${animName} 0.3s linear`;
89
- }
90
- focusSize.left = this.curRect.left;
91
- focusSize.top = this.curRect.top;
92
- focusSize.width = this.curRect.width;
93
- focusSize.height = this.curRect.height;
94
- this.focusIndex = focusIndex;
27
+ focusVisible: (visible) => {
28
+ showFocus.value = visible ? true : false;
29
+ },
30
+ onFocusChange: (rect, focusIndex, doAnim) => {
31
+ creepFocus.value?.changeRect(rect, doAnim);
32
+ currentFocusRect = rect;
95
33
  },
96
34
  };
97
35
  provide("focusFrameController", focusFrameController);
98
36
 
37
+ //<<<<<<< 创建数据
99
38
  const contentData = [
100
39
  "推荐",
101
40
  "电视剧",
@@ -113,37 +52,85 @@ const contentData = [
113
52
  const SizeObj = {
114
53
  fontSize: 40,
115
54
  };
116
- const provideData = () => {
117
- const data = [];
118
- for (let i = 0; i < 12; ++i) {
119
- data.push({
120
- width: getTextWidth(contentData[i], SizeObj) + 60,
55
+ const data = [];
56
+ const getChildTabList = (textSize, childTextSize, contentText) => {
57
+ const list = [];
58
+ // 3-5个
59
+ const number = Math.round(Math.random() * 2 + 3);
60
+ for (let i = 0; i < number; ++i) {
61
+ list.push({
62
+ width: (i == number - 1 ? textSize : childTextSize) + 60,
121
63
  height: 65,
64
+ marginRight: i == number - 1 ? 0 : 10,
122
65
  color: randomColor(),
123
- content: contentData[i],
66
+ content: contentText + (i == number - 1 ? "" : "_" + i),
124
67
  });
125
68
  }
69
+ return list;
70
+ };
71
+ for (let i = 0; i < 12; ++i) {
72
+ const textSize = getTextWidth(contentData[i], SizeObj);
73
+ const childTextSize = getTextWidth(contentData[i] + "_0", SizeObj);
74
+ const childList = getChildTabList(textSize, childTextSize, contentData[i]);
75
+ let totalWidth = 0;
76
+ childList.forEach((item) => {
77
+ totalWidth += item.width + item.marginRight;
78
+ });
79
+ const initFocus = Math.round(Math.random() * (childList.length - 1));
80
+
81
+ data.push({
82
+ width: textSize + 60,
83
+ height: 65,
84
+ color: randomColor(),
85
+ content: contentData[i],
86
+ marginRight: 10,
87
+ name: "item_" + i, // focus name
88
+ childTab: {
89
+ width: totalWidth, //4个item + 3个gap
90
+ list: childList,
91
+ },
92
+ initFocus,
93
+ });
94
+ }
95
+ const provideData = () => {
126
96
  return data;
127
97
  };
98
+ // >>>>>>
99
+
100
+ const getDetaileInfo = () => {
101
+ let str = "";
102
+ for (let item of data) {
103
+ str += `${item.content}共有${item.childTab.list.length}个子tab, 展开后的默认焦点为${item.initFocus}\n`;
104
+ }
105
+ return str;
106
+ };
128
107
 
129
108
  const measures = (item) => {
130
109
  return {
131
110
  width: item.width,
132
111
  height: item.height,
133
- marginRight: 10,
134
- marginBottom: 10,
112
+ marginRight: item.marginRight,
135
113
  };
136
114
  };
137
115
 
116
+ const widgetRef = ref();
138
117
  onMounted(() => {
118
+ provide("parentWidget", widgetRef);
139
119
  focusHub.setFocus("myWidget");
140
120
  });
141
121
 
142
- onBeforeUnmount(() => {
143
- for (let name in animationCache) {
144
- removeAnimation(name);
122
+ const onFocusRectChange = () => {
123
+ //展开时的滑动不需要更新焦点大小
124
+ if (showFocus.value) {
125
+ const curFocusIndex = widgetRef.value.getCurrentFocusIndex();
126
+ const rect = widgetRef.value.getTemplatePosition(curFocusIndex);
127
+ focusFrameController.onFocusChange(rect, curFocusIndex);
145
128
  }
146
- });
129
+ };
130
+
131
+ const onAllResizeDone = () => {
132
+ console.log("onAllResizeDone");
133
+ };
147
134
  </script>
148
135
 
149
136
  <template>
@@ -163,38 +150,67 @@ onBeforeUnmount(() => {
163
150
  >
164
151
  爬行样式的焦点示例
165
152
  </div>
166
- <metro-widget
153
+ <list-widget
154
+ ref="widgetRef"
167
155
  name="myWidget"
156
+ :onFocusRectChange="onFocusRectChange"
157
+ :onAllItemResizeDone="onAllResizeDone"
168
158
  :top="100"
169
159
  :left="40"
170
160
  :width="1200"
171
161
  :height="110"
162
+ :slideSetting="slideType"
172
163
  :direction="HORIZONTAL"
173
164
  :provideData="provideData"
174
165
  :measures="measures"
175
166
  :padding="{ left: 20, right: 20, top: 20, height: 20 }"
176
167
  >
177
- <template #renderItem="{ data, onAction, query }">
178
- <item :data="data" :onAction="onAction" :query="query" />
168
+ <template #renderItem="{ data, onAction, query, onItemEdge }">
169
+ <foldable-item
170
+ :data="data"
171
+ :onAction="onAction"
172
+ :query="query"
173
+ :onItemEdge="onItemEdge"
174
+ />
179
175
  </template>
180
176
  <template #background>
181
- <jsv-nine-patch
182
- :style="{
183
- width: focusSize.width,
184
- height: focusSize.height,
185
- top: focusSize.top,
186
- left: focusSize.left,
187
- }"
188
- :animation="focusSize.animation"
189
- :imageUrl="redCircle"
190
- :imageWidth="35"
191
- :centerWidth="0"
192
- :borderOutset="0"
193
- :imageDspWidth="(focusSize.height / 17) * 2"
194
- :animTime="0.2"
195
- :waitForInit="true"
196
- ></jsv-nine-patch>
177
+ <creep-focus
178
+ v-if="showFocus"
179
+ ref="creepFocus"
180
+ :imgUrl="redCircle"
181
+ :initLeft="currentFocusRect?.left"
182
+ :initTop="currentFocusRect?.top"
183
+ :initWidth="currentFocusRect?.width"
184
+ :initHeight="currentFocusRect?.height"
185
+ >
186
+ </creep-focus>
197
187
  </template>
198
- </metro-widget>
188
+ </list-widget>
189
+ </div>
190
+
191
+ <div
192
+ :style="{
193
+ left: 100,
194
+ top: 250,
195
+ width: 1280,
196
+ height: 400,
197
+ fontSize: 25,
198
+ color: '#FFFFFF',
199
+ }"
200
+ >
201
+ OK键展开, 返回键折叠.
202
+ 展开后的默认焦点的位置和展开前相同(第一个和最后一个除外)
203
+ </div>
204
+ <div
205
+ :style="{
206
+ left: 100,
207
+ top: 300,
208
+ width: 1280,
209
+ height: 400,
210
+ fontSize: 20,
211
+ color: '#FFFFFF',
212
+ }"
213
+ >
214
+ {{ getDetaileInfo() }}
199
215
  </div>
200
216
  </template>
@@ -0,0 +1,128 @@
1
+ <script setup>
2
+ import { JsvNinePatch, getKeyFramesGroup } from "jsview";
3
+ import { reactive, onBeforeUnmount } from "vue";
4
+
5
+ const props = defineProps({
6
+ imgUrl: {
7
+ type: String,
8
+ },
9
+ initWidth: {
10
+ type: Number,
11
+ default: 0,
12
+ },
13
+ initHeight: {
14
+ type: Number,
15
+ default: 0,
16
+ },
17
+ initLeft: {
18
+ type: Number,
19
+ default: 0,
20
+ },
21
+ initTop: {
22
+ type: Number,
23
+ default: 0,
24
+ },
25
+ });
26
+
27
+ const styleShell = getKeyFramesGroup();
28
+
29
+ const animationCache = {};
30
+ const addAnimation = (fromRect, toRect, name) => {
31
+ if (animationCache.hasOwnProperty(name)) {
32
+ styleShell.removeRule(name);
33
+ delete animationCache[name];
34
+ }
35
+ if (!(name in animationCache)) {
36
+ const animStr = `@keyframes ${name} {
37
+ 0% {
38
+ left: ${fromRect.left};
39
+ top: ${fromRect.top};
40
+ width: ${fromRect.width};
41
+ height: ${fromRect.height};
42
+ }
43
+ 50% {
44
+ left: ${Math.min(fromRect.left, toRect.left)};
45
+ top: ${Math.min(fromRect.top, toRect.top)};
46
+ width: ${
47
+ Math.max(fromRect.left + fromRect.width, toRect.left + toRect.width) -
48
+ Math.min(fromRect.left, toRect.left)
49
+ };
50
+ height: ${
51
+ Math.max(fromRect.top + fromRect.height, toRect.top + toRect.height) -
52
+ Math.min(fromRect.top, toRect.top)
53
+ };
54
+ }
55
+ 100% {
56
+ left: ${toRect.left};
57
+ top: ${toRect.top};
58
+ width: ${toRect.width};
59
+ height: ${toRect.height};
60
+ }
61
+ }`;
62
+ animationCache[name] = animStr;
63
+ styleShell.insertRule(animStr);
64
+ }
65
+ };
66
+ const removeAnimation = (name) => {
67
+ styleShell.removeRule(name);
68
+ };
69
+
70
+ const focusSize = reactive({
71
+ width: props.initWidth,
72
+ height: props.initHeight,
73
+ left: props.initLeft,
74
+ top: props.initTop,
75
+ animation: null,
76
+ onTransitionEnd: null,
77
+ });
78
+
79
+ let animateChange = 0; // A B版动画切换,解决animation属性不变化引起不刷新问题
80
+ let preRect = null;
81
+ let curRect = null;
82
+ let first = true;
83
+
84
+ const changeRect = (rect, doAnim = true) => {
85
+ preRect = curRect;
86
+ curRect = rect;
87
+ if (first) {
88
+ first = false;
89
+ } else if (doAnim) {
90
+ const animName = "test_focusMove_" + animateChange;
91
+ animateChange = animateChange == 0 ? 1 : 0; // A B 版切换
92
+ addAnimation(preRect, curRect, animName);
93
+ focusSize.animation = `${animName} 0.3s linear`;
94
+ }
95
+ focusSize.left = curRect.left;
96
+ focusSize.top = curRect.top;
97
+ focusSize.width = curRect.width;
98
+ focusSize.height = curRect.height;
99
+ };
100
+
101
+ onBeforeUnmount(() => {
102
+ for (let name in animationCache) {
103
+ removeAnimation(name);
104
+ }
105
+ });
106
+
107
+ defineExpose({
108
+ changeRect,
109
+ });
110
+ </script>
111
+
112
+ <template>
113
+ <jsv-nine-patch
114
+ :style="{
115
+ width: focusSize.width,
116
+ height: focusSize.height,
117
+ top: focusSize.top,
118
+ left: focusSize.left,
119
+ }"
120
+ :animation="focusSize.animation"
121
+ :imageUrl="imgUrl"
122
+ :imageWidth="35"
123
+ :centerWidth="0"
124
+ :borderOutset="0"
125
+ :animTime="0.2"
126
+ :waitForInit="true"
127
+ ></jsv-nine-patch>
128
+ </template>
@@ -0,0 +1,279 @@
1
+ <!--
2
+ * @Author: ChenChanghua
3
+ * @Date: 2023-03-08 14:52:17
4
+ * @Description: file content
5
+ -->
6
+ <script setup>
7
+ import { ref, inject, shallowRef, provide } from "vue";
8
+ import { ListWidget, HORIZONTAL, useFocusHub, getKeyFramesGroup } from "jsview";
9
+ import Item from "./Item.vue";
10
+ import CreepFocus from "./CreepFocus.vue";
11
+ import redCircle from "./assets/redCircle.png";
12
+
13
+ const DURATION = 200;
14
+ const props = defineProps({
15
+ data: Object,
16
+ query: Object,
17
+ onAction: Object,
18
+ onItemEdge: Function,
19
+ });
20
+ const focusHub = useFocusHub();
21
+ const folded = ref(true);
22
+ const creepFocus = ref(null);
23
+ const showFocus = ref(true);
24
+ const styleShell = getKeyFramesGroup();
25
+ const itemKeepScale = ref(false);
26
+ provide("itemKeepScale", itemKeepScale);
27
+
28
+ const focused = ref(false);
29
+ const width = ref(props.data.width);
30
+ const widgetRef = shallowRef(null);
31
+ const widgetLeft = ref(0);
32
+
33
+ const focusRectController = {
34
+ onFocusChange: (rect) => {
35
+ creepFocus.value?.changeRect(rect);
36
+ },
37
+ };
38
+ const focusFrameController = inject("focusFrameController");
39
+
40
+ const provideData = () => {
41
+ return props.data.childTab.list;
42
+ };
43
+
44
+ const measures = (data) => {
45
+ return {
46
+ width: data.width,
47
+ height: data.height,
48
+ marginRight: data.marginRight,
49
+ };
50
+ };
51
+
52
+ // 注册回调
53
+ const onFocus = () => {
54
+ focusFrameController.focusVisible(true);
55
+ focused.value = true;
56
+ folded.value = true;
57
+ };
58
+
59
+ const foldSubTab = () => {
60
+ if (!folded.value) {
61
+ if (width.value == props.data.childTab.width) {
62
+ // showFocus.value = false;
63
+ width.value = props.data.width;
64
+ let index =
65
+ widgetRef.value?.getCurrentFocusIndex() ?? props.data.initFocus;
66
+
67
+ const anchor = getAnchor(index);
68
+ widgetLeft.value = -Math.round(
69
+ (props.data.childTab.width - props.data.width) * anchor
70
+ );
71
+
72
+ //创建动画
73
+ const anim = getClipAnimation(
74
+ props.data.childTab.width,
75
+ width.value,
76
+ anchor
77
+ );
78
+ clipAnimStr.value = anim.clip;
79
+ slideAnimStr.value = anim.slide;
80
+
81
+ props.query.updateItemSize(
82
+ props.query.index,
83
+ {
84
+ width: width.value,
85
+ height: props.data.height,
86
+ },
87
+ {
88
+ duration: DURATION,
89
+ onEnd: () => {
90
+ focusFrameController.focusVisible(true);
91
+ folded.value = true;
92
+ console.log("fold anim end.");
93
+ },
94
+ }
95
+ );
96
+ }
97
+ focusHub.returnFocusToParent();
98
+ }
99
+ };
100
+
101
+ const onBlur = () => {
102
+ if (!itemKeepScale.value) {
103
+ showFocus.value = false;
104
+ }
105
+ focused.value = false;
106
+ foldSubTab();
107
+ };
108
+
109
+ let animToken = 1;
110
+ const clipAnimStr = ref("");
111
+ const slideAnimStr = ref("");
112
+ const animationCache = {};
113
+ const getClipAnimation = (from, to, anchor) => {
114
+ animToken++;
115
+ const name = "foldableItemClipAnim" + animToken;
116
+ if (animationCache.hasOwnProperty(name)) {
117
+ styleShell.removeRule(name);
118
+ delete animationCache[name];
119
+ }
120
+ const clipAnimStr = `@keyframes ${name} { from { width: ${from}; } to { width: ${to}; } }`;
121
+ animationCache[name] = clipAnimStr;
122
+ styleShell.insertRule(clipAnimStr);
123
+
124
+ //平移动画
125
+ const anchorPosition = Math.round((to - from) * anchor);
126
+ const slideName = "foldableItemSlideAnim" + animToken;
127
+ if (animationCache.hasOwnProperty(slideName)) {
128
+ styleShell.removeRule(slideName);
129
+ delete animationCache[slideName];
130
+ }
131
+ const slideAnimStr = `@keyframes ${slideName} { from { transform: translate3d(${-anchorPosition},0,0); } to { transform: translate3d(0,0,0); } }`;
132
+
133
+ animationCache[slideName] = slideAnimStr;
134
+ styleShell.insertRule(slideAnimStr);
135
+
136
+ return {
137
+ clip: `${name} ${DURATION / 1000}s`,
138
+ slide: `${slideName} ${DURATION / 1000}s`,
139
+ };
140
+ };
141
+
142
+ const getAnchor = (index) => {
143
+ let anchor = 0;
144
+ const l = props.data.childTab.list;
145
+ if (index <= 0) {
146
+ anchor = 0;
147
+ } else if (index > l.length - 1) {
148
+ anchor = 1;
149
+ } else {
150
+ let focusMiddle = 0;
151
+ for (let i = 0; i <= index - 1; ++i) {
152
+ focusMiddle += l[i].width + l[i].marginRight;
153
+ }
154
+ focusMiddle += l[index].width / 2;
155
+ const scaleRate = props.data.childTab.width / props.data.width;
156
+ const targetPercent = focusMiddle / props.data.childTab.width; // 居中目标位置的百分比
157
+ /**
158
+ * anchor计算说明
159
+ * 考虑[0,1]的线段, 以a(0 <= a <= 1)为锚点, 放大n倍(n > 1)
160
+ * 坐标的映射关系为 x -> n(x - a) + a
161
+ * 此处我们想要的是目标焦点的中线对齐0.5, 即targetPercent放大后的位置是0.5
162
+ * 所以方程为 scaleRate * (targetPercent - anchor) + anchor = 0.5
163
+ */
164
+ anchor = (scaleRate * targetPercent - 0.5) / (scaleRate - 1);
165
+ }
166
+ return anchor;
167
+ };
168
+
169
+ const onClick = () => {
170
+ if (folded.value) {
171
+ itemKeepScale.value = false;
172
+ showFocus.value = true;
173
+ widgetLeft.value = 0;
174
+ folded.value = false;
175
+ width.value = props.data.childTab.width;
176
+ //为了居中而计算锚点
177
+ const anchor = getAnchor(props.data.initFocus);
178
+ //创建动画
179
+ const anim = getClipAnimation(props.data.width, width.value, anchor);
180
+ clipAnimStr.value = anim.clip;
181
+ slideAnimStr.value = anim.slide;
182
+
183
+ props.query.updateItemSize(
184
+ props.query.index,
185
+ {
186
+ width: width.value,
187
+ height: props.data.height,
188
+ },
189
+ {
190
+ anchor,
191
+ duration: DURATION,
192
+ onEnd: () => {
193
+ console.log("unfold anim end.");
194
+ },
195
+ }
196
+ );
197
+
198
+ focusHub.setFocus(props.data.name);
199
+ focusFrameController.focusVisible(false);
200
+ }
201
+ };
202
+ props.onAction.register("onFocus", onFocus);
203
+ props.onAction.register("onBlur", onBlur);
204
+ props.onAction.register("onClick", onClick);
205
+
206
+ const onKeyDown = (ev) => {
207
+ if (ev.keyCode === 10000 || ev.keyCode === 8 || ev.keyCode === 27) {
208
+ itemKeepScale.value = true;
209
+ foldSubTab();
210
+ return true;
211
+ }
212
+ return false;
213
+ };
214
+ </script>
215
+
216
+ <template>
217
+ <div
218
+ v-if="folded"
219
+ :style="{
220
+ fontSize: 40,
221
+ width: data.width,
222
+ height: data.height,
223
+ color: '#FFFFFF',
224
+ textAlign: 'center',
225
+ lineHeight: data.height,
226
+ transform: focused ? 'scale3d(1.1,1.1,1)' : 'scale3d(1,1,1)',
227
+ }"
228
+ >
229
+ {{ data.content }}
230
+ </div>
231
+ <div
232
+ v-else
233
+ :style="{
234
+ width: width,
235
+ height: data.height,
236
+ overflow: 'hidden',
237
+ animation: clipAnimStr,
238
+ }"
239
+ >
240
+ <jsv-focus-block
241
+ :onAction="{
242
+ onKeyDown,
243
+ }"
244
+ :style="{
245
+ left: widgetLeft,
246
+ width: data.childTab.width,
247
+ height: data.height,
248
+ backgroundColor: '#00990099',
249
+ borderRadius: 10,
250
+ animation: slideAnimStr,
251
+ }"
252
+ >
253
+ <list-widget
254
+ ref="widgetRef"
255
+ :name="data.name"
256
+ :width="data.childTab.width"
257
+ :height="data.height"
258
+ :provideData="provideData"
259
+ :direction="HORIZONTAL"
260
+ :measures="measures"
261
+ :onEdge="onItemEdge"
262
+ :initFocusId="data.initFocus"
263
+ >
264
+ <template #renderItem="{ data, onAction, query }">
265
+ <item
266
+ :focusRectController="focusRectController"
267
+ :data="data"
268
+ :onAction="onAction"
269
+ :query="query"
270
+ />
271
+ </template>
272
+ <template #background>
273
+ <creep-focus v-if="showFocus" ref="creepFocus" :imgUrl="redCircle">
274
+ </creep-focus>
275
+ </template>
276
+ </list-widget>
277
+ </jsv-focus-block>
278
+ </div>
279
+ </template>