@shijiu/jsview-vue 2.0.1073 → 2.0.1106

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shijiu/jsview-vue",
3
- "version": "2.0.1073",
3
+ "version": "2.0.1106",
4
4
  "license": "MIT",
5
5
  "repository": "system/jsview-framework",
6
6
  "author": "mengxk",
@@ -386,6 +386,7 @@ const {
386
386
  );
387
387
 
388
388
  defineExpose(exportObject);
389
+
389
390
  </script>
390
391
 
391
392
  <template>
@@ -414,6 +415,7 @@ defineExpose(exportObject);
414
415
  width: touchContainerW,
415
416
  height: touchContainerH,
416
417
  }"
418
+
417
419
  >
418
420
  <div>
419
421
  <slot name="background"></slot>
@@ -439,7 +441,7 @@ defineExpose(exportObject);
439
441
  }"
440
442
  >
441
443
  <div
442
- v-for="item in renderData"
444
+ v-for="(item, index) in renderData"
443
445
  :key="pageUpdateToken + '_' + item.index"
444
446
  :ref="item.divRef"
445
447
  :style="{
@@ -450,6 +452,7 @@ defineExpose(exportObject);
450
452
  v-if="!enableItemRenderBreak || item.mounted || itemRender"
451
453
  :key="renderBreakKey"
452
454
  :ref="item.slotRef"
455
+ :id="`${name}_${index}`"
453
456
  >
454
457
  <slot
455
458
  name="renderItem"
@@ -1233,7 +1233,7 @@ export const setup = (
1233
1233
  );
1234
1234
  if (next_focus_item !== null) {
1235
1235
  preFocusId = focusId;
1236
- const preFocusItem = getItemByIndex(preFocusId);
1236
+ const preFocusItem = getItemById(preFocusId);
1237
1237
  focusId = next_focus_item.id;
1238
1238
 
1239
1239
  templateItemAdder.tryAddItem(next_focus_item.templateInfo, 1);
@@ -1361,14 +1361,25 @@ export const setup = (
1361
1361
  edge = EdgeDirection.top;
1362
1362
  }
1363
1363
  const curTemplateInfo = getItemById(focusId)?.templateInfo;
1364
- let rect = {
1365
- x: curTemplateInfo.left - x_off_set,
1366
- y: curTemplateInfo.top - y_off_set,
1367
- width: curTemplateInfo.width,
1368
- height: curTemplateInfo.height,
1369
- };
1364
+ let rect
1365
+ if (curTemplateInfo) {
1366
+ rect = {
1367
+ x: curTemplateInfo.left - x_off_set,
1368
+ y: curTemplateInfo.top - y_off_set,
1369
+ width: curTemplateInfo.width,
1370
+ height: curTemplateInfo.height,
1371
+ };
1372
+ } else {
1373
+ rect = {
1374
+ x: 0,
1375
+ y: 0,
1376
+ width: 0,
1377
+ height: 0,
1378
+ };
1379
+ }
1380
+
1370
1381
  props.onEdge?.({ direction: edge, rect: rect });
1371
- innerData[id2Index(focusId)].onWidgetEdge({
1382
+ innerData[id2Index(focusId)]?.onWidgetEdge({
1372
1383
  direction: edge,
1373
1384
  });
1374
1385
  }
@@ -1692,12 +1703,14 @@ export const setup = (
1692
1703
  if (props.onScroll) {
1693
1704
  const lastTemplateInfo = getItemByIndex(
1694
1705
  metroTemplate.size - 1
1695
- ).templateInfo;
1696
- let totalWidth;
1697
- if (vertical) {
1698
- totalWidth = lastTemplateInfo.top + lastTemplateInfo.height - 1;
1699
- } else {
1700
- totalWidth = lastTemplateInfo.left + lastTemplateInfo.width - 1;
1706
+ )?.templateInfo;
1707
+ let totalWidth = 0;
1708
+ if (lastTemplateInfo) {
1709
+ if (vertical) {
1710
+ totalWidth = lastTemplateInfo.top + lastTemplateInfo.height - 1;
1711
+ } else {
1712
+ totalWidth = lastTemplateInfo.left + lastTemplateInfo.width - 1;
1713
+ }
1701
1714
  }
1702
1715
  props.onScroll(visibleInfo.start, visibleInfo.range, totalWidth);
1703
1716
  }
@@ -32,8 +32,7 @@ export class PageUpdater {
32
32
  mergeTmp: boolean,
33
33
  permanentList: Array<PermanentItemInfo> | null,
34
34
  extensionRange: number = 0
35
- ) {
36
- if (!template || template.size === 0) return;
35
+ ): {apply: Function, applyTmp: Function} {
37
36
  this.count++;
38
37
  const [visibleStart, visibleEnd] = template.getVisibleItemList(
39
38
  start - extensionRange,
@@ -388,6 +388,10 @@ class CommonMetroTemplate extends MetroTemplate {
388
388
  }
389
389
 
390
390
  const itemIndex = this.id2Index(baseId);
391
+ if (itemIndex < 0) {
392
+ console.log("getNextItem: no focusable item.");
393
+ return null;
394
+ }
391
395
  const offset = vOffset !== 0 ? vOffset : hOffset;
392
396
  let nextTemplateItem = this.getNextItemInner(
393
397
  itemIndex,
@@ -724,7 +728,7 @@ class CommonMetroTemplate extends MetroTemplate {
724
728
  visibleStart: number,
725
729
  visibleEnd: number,
726
730
  baseId: number): [number, number] {
727
- if (visibleStart > visibleEnd) {
731
+ if (visibleStart > visibleEnd || this.templateList.length == 0) {
728
732
  // No visible item
729
733
  return [-1, -1];
730
734
  }
@@ -732,7 +736,7 @@ class CommonMetroTemplate extends MetroTemplate {
732
736
  visibleStart = Math.round(visibleStart);
733
737
  visibleEnd = Math.round(visibleEnd);
734
738
  let searchBaseItemIdx = this.id2Index(baseId);
735
- if (typeof searchBaseItemIdx === "undefined") searchBaseItemIdx = 0;
739
+ if (searchBaseItemIdx < 0) searchBaseItemIdx = 0;
736
740
  const commonData: VisibleSearchConfigCommon = {
737
741
  resultStartIdx: -1,
738
742
  resultEndIdx: -1,
@@ -270,12 +270,12 @@ abstract class MetroTemplate {
270
270
 
271
271
  width: itemObj.width,
272
272
  height: itemObj.height,
273
- left: itemObj.left ? itemObj.left : 0,
274
- top: itemObj.top ? itemObj.top : 0,
275
- marginRight: itemObj.marginRight ? itemObj.marginRight : 0,
276
- marginBottom: itemObj.marginBottom ? itemObj.marginBottom : 0,
277
- focusable: itemObj.focusable ? itemObj.focusable : true,
278
- findNextAnchor: itemObj.findNextAnchor ? itemObj.findNextAnchor : null,
273
+ left: itemObj.left ?? 0,
274
+ top: itemObj.top ?? 0,
275
+ marginRight: itemObj.marginRight ?? 0,
276
+ marginBottom: itemObj.marginBottom ?? 0,
277
+ focusable: itemObj.focusable ?? true,
278
+ findNextAnchor: itemObj.findNextAnchor ?? null,
279
279
 
280
280
  neighborIndexList: {
281
281
  left: [],
@@ -7,9 +7,9 @@ let PluginInfo = {
7
7
  // downloadUrl:"http://192.168.0.63:8080/plugin/JsvAudio-164.zip", //插件下载地址
8
8
  packageName: "com.qcode.jsvaudio",
9
9
  name: "音频插件",
10
- version: "1.0.21", //插件需要的版本号
11
- versionCodeMin: 21,
12
- versionCodeMax: 21,
10
+ version: "1.0.24", //插件需要的版本号
11
+ versionCodeMin: 24,
12
+ versionCodeMax: 24,
13
13
  bridgeName: "jsvAudioBridge", //插件bridge注册到jsview的名称
14
14
  className: "com.qcode.jsvaudio.JsvAudio", //插件初始化类名称
15
15
  initMethod: "createInstance", //插件初始化方法
@@ -7,9 +7,9 @@ let PluginInfo = {
7
7
  // downloadUrl:"http://192.168.0.63:8080/plugin/JsvAudio-164.zip", //插件下载地址
8
8
  packageName: "com.qcode.jsvaudio",
9
9
  name: "音频插件",
10
- version: "1.0.21", //插件需要的版本号
11
- versionCodeMin: 21,
12
- versionCodeMax: 21,
10
+ version: "1.0.24", //插件需要的版本号
11
+ versionCodeMin: 24,
12
+ versionCodeMax: 24,
13
13
  bridgeName: "jsvAudioBridge", //插件bridge注册到jsview的名称
14
14
  className: "com.qcode.jsvaudio.JsvAudio", //插件初始化类名称
15
15
  initMethod: "createInstance", //插件初始化方法
@@ -37,12 +37,13 @@ const ensureInterface = ()=>{
37
37
  });
38
38
  }
39
39
 
40
- const enableNativeViewListener = (nativeViewId)=>{
40
+ const enableNativeViewListener = (nativeViewId,info)=>{
41
41
  let listenerId = idGeneratoer;
42
42
  idGeneratoer++;
43
43
  idObjectMap[listenerId] = {
44
44
  eventCallback: (ret)=>{
45
- console.log("JsvDemoTester nativeView info=" + ret.info);
45
+ info.value=ret.info
46
+ console.log(`JsvDemoTester nativeView info=${info.value}`);
46
47
  }
47
48
  };
48
49
 
@@ -76,5 +77,5 @@ const disableNativeViewListener = (listenerId)=>{
76
77
 
77
78
  export {
78
79
  enableNativeViewListener,
79
- disableNativeViewListener
80
+ disableNativeViewListener,
80
81
  }
@@ -67,6 +67,10 @@ class ActorControlBase {
67
67
  Forge.sRenderBridge.SetStepFpsSwitch(false);
68
68
  }
69
69
 
70
+ altStraightSpeedInner(newSpeedPerSecond, time) {
71
+ Forge.AnimFireCommand.AltSpeed(this._SpriteView, newSpeedPerSecond, time);
72
+ }
73
+
70
74
  _WrapBuildAnimation(
71
75
  repeat_start_array,
72
76
  current_array,
@@ -126,7 +130,6 @@ class ActorControlBase {
126
130
 
127
131
  const froms = [...this._Current];
128
132
  const tos = this._Target;
129
- const repeat_starts = this._Repeat ? [...this._RepeatStart] : null;
130
133
 
131
134
  // const token = this._Token++;
132
135
 
@@ -142,7 +145,7 @@ class ActorControlBase {
142
145
  const listener = new Forge.AnimationListener().OnFinalProgress(
143
146
  (progress) => {
144
147
  that._OnPaused(
145
- repeat_starts !== null ? repeat_starts : froms,
148
+ froms,
146
149
  memo_tos,
147
150
  progress,
148
151
  start_params
@@ -154,9 +157,6 @@ class ActorControlBase {
154
157
 
155
158
  anim.AddAnimationListener(listener);
156
159
  anim.Enable(Forge.AnimationEnable.KeepTransform);
157
- if (this._Repeat) {
158
- anim.EnableInfinite();
159
- }
160
160
  this._SpriteView.StartAnimation(anim);
161
161
 
162
162
  return true; // success
@@ -19,9 +19,9 @@
19
19
  * 功能:延X轴进行匀速运动
20
20
  * moveToY(target_y, speed, end_callback)
21
21
  * 功能:延Y轴进行匀速运动
22
- * repeatMoveAlongX(target_x, speed, repeat_start, repeat_callback)
22
+ * scrollMoveAlongX(target_x, speed, repeat_start, repeat_callback)
23
23
  * 功能:延X轴进行重复匀速运动
24
- * repeatMoveAlongY(target_y, speed, repeat_start, repeat_callback)
24
+ * scrollMoveAlongY(target_y, speed, repeat_start, repeat_callback)
25
25
  * 功能:延Y轴进行重复匀速运动
26
26
  * throwAlongX(init_v, acc, end_condition, end_callback, pole_callback)
27
27
  * 功能:延X轴进行回旋运动
@@ -31,6 +31,10 @@
31
31
  * 功能:直接将JsvActorMove移动到目标位置,无中间运动动画
32
32
  * pause(pause_callback)
33
33
  * 功能:暂停动画,并将JsvActorMove保持在暂停的位置上
34
+ * pause(pause_callback)
35
+ * 功能:暂停动画,并将JsvActorMove保持在暂停的位置上
36
+ * altStraightSpeed(newSpeedPerSecond, time)
37
+ * 功能: 将由moveToX, moveToY, scrollMoveAlongX, scrollMoveAlongY 发起的直线运动进行均匀变速
34
38
 
35
39
  -->
36
40
  <script setup>
@@ -50,14 +50,25 @@ class JsvActorMoveControl extends ActorControlBase {
50
50
  }
51
51
 
52
52
  /*
53
- * repeatMoveAlongX 参数说明:
53
+ * altStraightSpeed
54
+ * 将由moveToX, moveToY, scrollMoveAlongX, scrollMoveAlongY 发起的直线运动进行均匀变速
55
+ * 参数说明:
56
+ * spnewSpeedPerSecond (int) 带符号整数,符号表示方向,标识运动的运行速度,单位(pixel/s)
57
+ * end_callback (Function(x,y)) 运动到目标位置后的回调函数,回报当前JsvActorMove的相对x,y
58
+ */
59
+ altStraightSpeed(newSpeedPerSecond, time) {
60
+ this.altStraightSpeedInner(newSpeedPerSecond, time);
61
+ }
62
+
63
+ /*
64
+ * scrollMoveAlongX 用于背景无限滚轴动画效果的接口,参数说明:
54
65
  * target_x (int) 带符号整数,标识运动的目标位置,相对于JsvActorMove当前位置
55
66
  * speed (int) 带符号整数,符号表示方向,标识运动的运行速度,单位(pixel/s)
56
67
  * repeat_start (int) 带符号整数,标识往复运动的起始点,使用时注意,JsvActorMove的当前位置必须在
57
68
  * repeat_start和target_x之间
58
69
  * repeat_callback (Function(times)) 完整一个运动周期后的回调,返回当前运动的周期数times
59
70
  */
60
- repeatMoveAlongX(target_x, speed, repeat_start, repeat_callback) {
71
+ scrollMoveAlongX(target_x, speed, repeat_start, repeat_callback) {
61
72
  this._UniformMove(0,
62
73
  target_x, NaN, speed,
63
74
  {
@@ -68,14 +79,14 @@ class JsvActorMoveControl extends ActorControlBase {
68
79
  }
69
80
 
70
81
  /*
71
- * repeatMoveAlongY 参数说明:
82
+ * scrollMoveAlongY 用于背景无限滚轴动画效果的接口,参数说明:
72
83
  * target_y (int) 带符号整数,标识运动的目标位置,相对于JsvActorMove当前位置
73
84
  * speed (int) 带符号整数,符号表示方向,标识运动的运行速度,单位(pixel/s)
74
85
  * repeat_start (int) 带符号整数,标识往复运动的起始点,使用时注意,JsvActorMove的当前位置必须在
75
86
  * repeat_start和target_y之间
76
87
  * repeat_callback (Function(times)) 完整一个运动周期后的回调,返回当前运动的周期数times
77
88
  */
78
- repeatMoveAlongY(target_y, speed, repeat_start, repeat_callback) {
89
+ scrollMoveAlongY(target_y, speed, repeat_start, repeat_callback) {
79
90
  this._UniformMove(1,
80
91
  NaN, target_y, speed,
81
92
  {
@@ -354,11 +365,14 @@ class JsvActorMoveControl extends ActorControlBase {
354
365
 
355
366
 
356
367
  _ReCalculateUMoveCurrent(froms, tos, progress, start_params) {
368
+ let repeat_start = start_params?.repeatSet?.start;
357
369
  if (start_params.xOrY === 0) {
370
+ froms[0] = repeat_start ? repeat_start : froms[0];
358
371
  const value = (tos[0] - froms[0]) * progress + froms[0];
359
372
  const direction = value >= 0 ? 1 : -1;
360
373
  this._Current[0] = direction * Math.floor(Math.abs(value));
361
374
  } else {
375
+ froms[1] = repeat_start ? repeat_start : froms[1];
362
376
  const value = (tos[1] - froms[1]) * progress + froms[1];
363
377
  const direction = value >= 0 ? 1 : -1;
364
378
  this._Current[1] = direction * Math.floor(Math.abs(value));
@@ -596,6 +596,11 @@ export default {
596
596
  );
597
597
  key_used = true;
598
598
  break;
599
+ case 27:
600
+ case 10000:
601
+ //返回键吐出
602
+ key_used = false;
603
+ break;
599
604
  default:
600
605
  // 浏览器中,点击浏览器时input焦点丢失,JsvInput收到任意按键获取焦点
601
606
  if (!window.JsView) {
@@ -21,7 +21,7 @@
21
21
  * 自动启动模式:start:精灵图自动启动,结束后显示第一帧内容、
22
22
  * end:精灵图自动启动,结束后显示最后一帧内容,
23
23
  * none:不自动启动
24
- * loop {string} 动图的循环次数 infinite/数字,默认为infinite
24
+ * loop {string或number} 动图的循环次数 infinite/数字,默认为infinite
25
25
  * spriteName {string} 动图的名称,默认为null
26
26
  * controller {SpriteController} 控制动图start,stop的对象
27
27
  *
@@ -32,414 +32,396 @@
32
32
  * stop(end_frame)
33
33
  * 功能: 停止精灵图动画,可选择静止在第一帧或最后一帧
34
34
  -->
35
- <script>
36
- import { getKeyFramesGroup } from '../../JsViewVueTools/JsvDynamicKeyFrames.js'
37
-
38
-
39
-
40
- function _getTransformInfo(source_obj, target_obj, canvas_width, canvas_height) {
41
- const result = { csw: 1, csh: 1, cx: 0, cy: 0, sw: 1, sh: 1, x: 0, y: 0 };
42
-
43
- // Clip在Canvas div之内,以Canvas为基准进行缩放和平移
44
- // 图形左上角远离原点后再缩放,所以需要进行缩放补偿
45
- const clip_scale_w = target_obj.w / canvas_width;
46
- const clip_scale_h = target_obj.h / canvas_height;
47
- result.csw = clip_scale_w;
48
- result.csh = clip_scale_h;
49
- result.cx = target_obj.x / clip_scale_w;
50
- result.cy = target_obj.y / clip_scale_h;
51
-
52
- // Image在Clip div之内,所以以Clip为基准进行缩放和平移, 对clip的缩放进行反处理以还原尺寸
53
- // 将子图左上角对齐原点后再缩放,所以x,y不需要进行举例缩放补偿
54
- result.sw = source_obj.w / target_obj.w / clip_scale_w;
55
- result.sh = source_obj.h / target_obj.h / clip_scale_h;
56
- result.x = -source_obj.x;
57
- result.y = -source_obj.y;
58
-
59
- return result;
60
- }
61
-
62
- function _createTransformStyle(w_scale, h_scale, x, y) {
63
- let output = "";
64
- output = `${output}scale3d(${
65
- parseFloat(w_scale).toPrecision(5)},${
66
- parseFloat(h_scale).toPrecision(5)},1) `
67
- + `translate3d(${
68
- parseFloat(x).toPrecision(5)}px,${
69
- parseFloat(y).toPrecision(5)}px,0)`;
70
- return output;
71
- }
72
-
73
- export default {
74
- props: {
75
- spriteInfo: Object,
76
- viewSize: Object,
77
- imageUrl: String,
78
- duration: Number,
79
- onAnimEnd: Function,
80
- autostart: [Boolean, String],
81
- loop: Number,
82
- spriteName: String,
83
- controller: Object,
84
- },
85
-
86
- created() {
87
- let stopFrame = "start";
88
- if (typeof this.$props.autostart === "string" && this.$props.autostart !== "none") {
89
- stopFrame = this.$props.autostart;
90
- }
91
- this.TRANSFORM_ORIGIN_LEFT_TOP = "left top";
92
- this.keyFrameNames = {
93
- clip: null,
94
- image: null,
95
- valid: false
96
- };
97
- this.frozeFrameCache = {
98
- clipStyle: {},
99
- transStyle: {},
100
- imageStyle: {
101
- backgroundImage: null,
102
- }
103
- };
104
- this.animateFrameCache = {
105
- clipStyle: {},
106
- transStyle: {},
107
- imageStyle: {
108
- backgroundImage: null,
109
- },
110
- clipAnimName: null,
111
- imageAnimName: null,
112
- };
113
- this.keyFrameStyleSheet = getKeyFramesGroup(null);
114
- this.innerId = 0;
115
- this.stopped = false;
116
- this.spriteDuration = this.$props.duration;
117
- this.stopFrame = stopFrame;
118
- this.blinkAnim = null;
119
- this.blinkAnimCache = null;
120
- this.sAnimationToken = 0;
121
- this.transform_style = this._AnalyzeProp();
122
- },
123
-
124
- unmounted() {
125
- this._clearExpiredKeyFrames();
126
- },
127
-
128
- methods: {
129
- stop(end_frame) {
130
- if (this.$props.spriteInfo.frames && this.$props.spriteInfo.frames.length === 1) {
131
- return;
132
- }
133
- this.stopped = true;
134
- this.stopFrame = end_frame || this.stopFrame;
135
- },
136
-
137
- start(end_frame, duration) {
138
- if (this.$props.spriteInfo.frames && this.$props.spriteInfo.frames.length === 1) {
139
- return;
140
- }
141
- this.innerId += 1;
142
- this.stopped = false;
143
- this.spriteDuration = duration || this.$props.duration;
144
- this.stopFrame = end_frame || this.stopFrame;
145
- },
146
-
147
- blink(alphas, duration, ease, delay, repeat) {
148
- // 注意:比较数组是否相同仅在此场景下使用toString,其他场景
149
- if (!this.blinkAnimCache
150
- || (this.blinkAnimCache.alphas.toString() !== alphas.toString()
151
- || this.blinkAnimCache.duration !== duration
152
- || this.blinkAnimCache.ease !== ease
153
- || this.blinkAnimCache.delay !== delay
154
- || this.blinkAnimCache.repeat !== repeat)) {
155
- const anim_name_base = this._getAnimNameBase();
156
- const anim_name_blink = `${anim_name_base}-blink`;
157
- let image_keyframs = `@keyframes ${anim_name_blink} {`;
158
- const frame_percent = 1 / (alphas.length);
159
- for (let i = 0; i < alphas.length; i++) {
160
- const alpha = alphas[i];
161
- let header;
162
- if (i !== alphas.length - 1) {
163
- header = `${parseFloat(frame_percent * i * 100).toFixed(2)}% {`;
164
- } else {
165
- header = '100% {';
166
- }
167
- image_keyframs += header;
168
-
169
- if (alpha) {
170
- const tr_str = ` opacity:${alpha};`;
171
- image_keyframs += tr_str;
172
- }
173
-
174
- image_keyframs += '}';
175
- image_keyframs += "\n";
176
- }
177
- image_keyframs += '}';
178
- if (this.keyFrameStyleSheet) {
179
- this.keyFrameStyleSheet.insertRule(image_keyframs);
180
- }
181
- this.blinkAnimCache = {
182
- alphas,
183
- duration,
184
- ease,
185
- delay,
186
- repeat,
187
- blinkAnimName: anim_name_blink
188
- };
189
- }
190
-
191
- // 参数格式化
192
- ease = ease || "";
193
- delay = delay || 0;
194
- repeat = (repeat === -1) ? "infinite" : (repeat || 1);
195
-
196
- const animName = `${this.blinkAnimCache.blinkAnimName} ${duration}s ${ease} ${delay}s ${repeat}`;
197
- this.blinkAnim = animName;
198
- this.stopped = true;
199
- },
200
-
201
- _getAnimNameBase() {
202
- return this.$props.spriteName ? this.$props.spriteName : `sprite-anim-name-${this.sAnimationToken++}`;
203
- },
204
-
205
- _updateFrozeFrameCache(image_url, frame_info_list,
206
- canvas_width, canvas_height,
207
- source_width, source_height) {
208
- const cache = this.frozeFrameCache;
209
-
210
- const index = this.stopFrame === "start" ? 0 : frame_info_list.length - 1;
211
- const tr = _getTransformInfo(
212
- frame_info_list[index].source,
213
- frame_info_list[index].target,
214
- canvas_width,
215
- canvas_height
216
- );
217
-
218
- cache.clipStyle = {
219
- transform: _createTransformStyle(tr.csw, tr.csh, tr.cx, tr.cy),
220
- transformOrigin: this.TRANSFORM_ORIGIN_LEFT_TOP,
221
- overflow: "hidden",
222
- left: 0,
223
- top: 0,
224
- width: canvas_width,
225
- height: canvas_height,
226
- };
227
-
228
- cache.transStyle = {
229
- transformOrigin: this.TRANSFORM_ORIGIN_LEFT_TOP,
230
- transform: _createTransformStyle(tr.sw, tr.sh, tr.x, tr.y),
231
- width: source_width,
232
- height: source_height,
233
- };
234
-
235
- cache.imageStyle = {
236
- backgroundImage: image_url,
237
- width: source_width,
238
- height: source_height,
239
- };
240
- },
241
-
242
- _updateAnimateFrameCache(image_url, frame_info_list,
243
- canvas_width, canvas_height,
244
- source_width, source_height) {
245
- this._clearExpiredKeyFrames();
246
-
247
- if (!frame_info_list) { return; }
248
- const anim_name_base = this._getAnimNameBase();
249
- const frame_percent = 1 / (frame_info_list.length);
250
- const anim_name_clip = `${anim_name_base}-clip`;
251
- const anim_name_image = `${anim_name_base}-image`;
252
- let image_keyframs = `@keyframes ${anim_name_image} {`;
253
- let clip_keyframs = `@keyframes ${anim_name_clip} {`;
254
-
255
- for (let i = 0; i < frame_info_list.length + 1; i++) {
256
- let item;
257
- if (i !== frame_info_list.length) {
258
- item = frame_info_list[i];
259
- } else {
260
- // 追加一个最后一帧以保证最后一帧可见
261
- item = frame_info_list[frame_info_list.length - 1];
262
- }
263
-
264
- // Header
265
- let header;
266
- if (i !== frame_info_list.length) {
267
- header = `${parseFloat(frame_percent * i * 100).toFixed(2)}% {`;
268
- } else {
269
- header = '100% {';
270
- }
271
- image_keyframs += header;
272
- clip_keyframs += header;
273
-
274
- if (item.source) {
275
- const tr = _getTransformInfo(item.source, item.target, canvas_width, canvas_height);
276
- const clip_trans = _createTransformStyle(tr.csw, tr.csh, tr.cx, tr.cy);
277
- const image_trans = _createTransformStyle(tr.sw, tr.sh, tr.x, tr.y);
278
-
279
- let tr_str = "";
280
- tr_str = `${tr_str}transform:${clip_trans};transform-origin:left top;`;
281
- clip_keyframs += tr_str;
282
-
283
- tr_str = "";
284
- tr_str = `${tr_str}transform:${image_trans};transform-origin:left top;`;
285
- image_keyframs += tr_str;
286
- }
287
-
288
- image_keyframs += '}';
289
- clip_keyframs += '}';
290
- }
291
-
292
- image_keyframs += '}';
293
- clip_keyframs += '}';
294
-
295
- if (this.keyFrameStyleSheet) {
296
- this.keyFrameStyleSheet.insertRule(image_keyframs);
297
- this.keyFrameStyleSheet.insertRule(clip_keyframs);
298
-
299
- // 记录Keyframe设置,以便于界面关闭时进行清理
300
- this.keyFrameNames.clip = anim_name_clip;
301
- this.keyFrameNames.image = anim_name_image;
302
- this.keyFrameNames.valid = true;
303
- }
304
- const cache = this.animateFrameCache;
305
-
306
- cache.clipAnimName = `${anim_name_base}-clip`;
307
- cache.imageAnimName = `${anim_name_base}-image`;
308
-
309
- cache.clipStyle = {
310
- overflow: "hidden",
311
- width: canvas_width,
312
- height: canvas_height,
313
- transform: null, // 重置 transform,以免影响keyframe动画
314
- transformOrigin: this.TRANSFORM_ORIGIN_LEFT_TOP,
315
- animation: null, // 外部设置,需要时间和loop信息
316
- };
317
-
318
- cache.transStyle = {
319
- transform: null, // 重置 transform,以免影响keyframe动画
320
- transformOrigin: this.TRANSFORM_ORIGIN_LEFT_TOP,
321
- animation: null, // 外部设置,需要时间和loop信息
322
- width: source_width,
323
- height: source_height,
324
- };
325
-
326
- cache.imageStyle = {
327
- backgroundImage: image_url,
328
- width: source_width,
329
- height: source_height,
330
- };
331
- },
332
-
333
- _clearExpiredKeyFrames() {
334
- if (this.keyFrameNames.valid) {
335
- this._removeKeyFrame([this.keyFrameNames.clip, this.keyFrameNames.image]);
336
- this.keyFrameNames.valid = false;
337
- }
338
- },
339
-
340
- _removeKeyFrame(names_array) {
341
- if (this.keyFrameStyleSheet) {
342
- this.keyFrameStyleSheet.removeMultiRules(names_array);
343
- }
344
- },
345
-
346
- _IsAutoStart() {
347
- let autoStart = false;
348
- if (typeof this.$props.autostart === "boolean") {
349
- autoStart = this.$props.autostart;
350
- } if (typeof this.$props.autostart === "string" && this.$props.autostart !== "none") {
351
- autoStart = true;
352
- }
353
- return autoStart;
354
- },
355
-
356
- _AnalyzeProp() {
357
- const used = this.$props.controller && this.$props.controller.Used;
358
- if (this.$props.spriteInfo.frames.length === 1 || (!used && !this._IsAutoStart()) || this.stopped) {
359
- // 单图模式
360
- // 解析图片信息
361
- this._updateFrozeFrameCache(
362
- this.$props.imageUrl,
363
- this.$props.spriteInfo.frames,
364
- this.$props.viewSize.w,
365
- this.$props.viewSize.h,
366
- this.$props.spriteInfo.meta.size.w,
367
- this.$props.spriteInfo.meta.size.h
368
- );
369
-
370
- return {
371
- clipStyle: this.frozeFrameCache.clipStyle,
372
- transStyle: this.frozeFrameCache.transStyle,
373
- imageStyle: this.frozeFrameCache.imageStyle,
374
- };
375
- }
376
-
377
-
378
- // 动画模式
379
- if (this.animateFrameCache.imageStyle.backgroundImage !== this.$props.imageUrl) {
380
- // 解析图片信息
381
- this._updateAnimateFrameCache(
382
- this.$props.imageUrl,
383
- this.$props.spriteInfo.frames,
384
- this.$props.viewSize.w,
385
- this.$props.viewSize.h,
386
- this.$props.spriteInfo.meta.size.w,
387
- this.$props.spriteInfo.meta.size.h
388
- );
389
- }
390
-
391
- // 使用duration和loop信息更新动画设定
392
- this.animateFrameCache.clipStyle.animation =
393
- `${this.animateFrameCache.clipAnimName} ${this.spriteDuration}s steps(1,end) ${this.$props.loop}`;
394
- this.animateFrameCache.transStyle.animation =
395
- `${this.animateFrameCache.imageAnimName} ${this.spriteDuration}s steps(1,end) ${this.$props.loop}`;
396
-
397
- return {
398
- clipStyle: this.animateFrameCache.clipStyle,
399
- transStyle: this.animateFrameCache.transStyle,
400
- imageStyle: this.animateFrameCache.imageStyle,
401
- };
402
- },
403
-
404
- // shouldComponentUpdate(next_props, next_state) {
405
- // return (
406
- // (this.$props.imageUrl !== next_props.imageUrl)
407
- // || (this.$props.onAnimEnd !== next_props.onAnimEnd)
408
- // || (this.$props.loop !== next_props.loop)
409
- // || (this.spriteDuration !== next_state.duration)
410
- // || (this.$props.autostart !== next_props.autostart)
411
- // || this.innerId !== next_state.innerId
412
- // || this.stopped !== next_state.stopped
413
- // || this.blinkAnim !== next_state.blinkAnim
414
- // );
415
- // }
416
-
417
- onAnimEndDelegate() {
418
- // 在onAnimEnd之前进行Stop,以防onAnimEnd内部继续发生别的操作
419
- this.stopped = true;
420
- if (this.$props.onAnimEnd) {
421
- this.$props.onAnimEnd();
422
- }
423
- },
424
-
425
- onBlinkAnimEnd() {
426
- // 在onAnimEnd之前进行Stop,以防onAnimEnd内部继续发生别的操作
427
- this.blinkAnim = null;
428
- }
429
- }
430
- }
431
- </script>
432
-
433
- <template>
434
- <div id="canvas">
435
- <div id="clip" :style="{...transform_style.clipStyle}">
436
- <div id="trans" :style="{...transform_style.transStyle}" @animationend="onAnimEndDelegate">
437
- <div id="image" :style="{...transform_style.imageStyle, animation: blinkAnim}" @animationend="onBlinkAnimEnd"></div>
438
- </div>
439
- </div>
440
- </div>
441
- </template>
442
-
443
- <style scoped>
444
- @keyframes sprite-tag{}
445
- </style>
35
+ <script setup>
36
+ import { getKeyFramesGroup } from '../../JsViewVueTools/JsvDynamicKeyFrames.js'
37
+ import { reactive, shallowRef, onUnmounted, watch } from 'vue';
38
+
39
+
40
+ const _getTransformInfo = (source_obj, target_obj, canvas_width, canvas_height) => {
41
+ const result = { csw: 1, csh: 1, cx: 0, cy: 0, sw: 1, sh: 1, x: 0, y: 0 };
42
+
43
+ // Clip在Canvas div之内,以Canvas为基准进行缩放和平移
44
+ // 图形左上角远离原点后再缩放,所以需要进行缩放补偿
45
+ const clip_scale_w = target_obj.w / canvas_width;
46
+ const clip_scale_h = target_obj.h / canvas_height;
47
+ result.csw = clip_scale_w;
48
+ result.csh = clip_scale_h;
49
+ result.cx = target_obj.x / clip_scale_w;
50
+ result.cy = target_obj.y / clip_scale_h;
51
+
52
+ // Image在Clip div之内,所以以Clip为基准进行缩放和平移, 对clip的缩放进行反处理以还原尺寸
53
+ // 将子图左上角对齐原点后再缩放,所以x,y不需要进行举例缩放补偿
54
+ result.sw = source_obj.w / target_obj.w / clip_scale_w;
55
+ result.sh = source_obj.h / target_obj.h / clip_scale_h;
56
+ result.x = -source_obj.x;
57
+ result.y = -source_obj.y;
58
+
59
+ return result;
60
+ }
61
+
62
+ const _createTransformStyle = (w_scale, h_scale, x, y) => {
63
+ let output = "";
64
+ output = `${output}scale3d(${parseFloat(w_scale).toPrecision(5)},${parseFloat(h_scale).toPrecision(5)},1) `
65
+ + `translate3d(${parseFloat(x).toPrecision(5)}px,${parseFloat(y).toPrecision(5)}px,0)`;
66
+ return output;
67
+ }
68
+
69
+ const props = defineProps({
70
+ spriteInfo: Object,
71
+ viewSize: Object,
72
+ imageUrl: String,
73
+ duration: Number,
74
+ onAnimEnd: Function,
75
+ autostart: [Boolean, String],
76
+ loop: [String,Number],
77
+ spriteName: String,
78
+ controller: Object,
79
+ })
80
+
81
+
82
+ let stopFrame = shallowRef("start");
83
+ let TRANSFORM_ORIGIN_LEFT_TOP = shallowRef("left top")
84
+ let keyFrameNames = reactive({
85
+ clip: null,
86
+ image: null,
87
+ valid: false
88
+ });
89
+ let frozeFrameCache = reactive({
90
+ clipStyle: {},
91
+ transStyle: {},
92
+ imageStyle: {
93
+ backgroundImage: null,
94
+ }
95
+ });
96
+ let animateFrameCache = reactive({
97
+ clipStyle: {},
98
+ transStyle: {},
99
+ imageStyle: {
100
+ backgroundImage: null,
101
+ },
102
+ clipAnimName: null,
103
+ imageAnimName: null,
104
+ });
105
+ let keyFrameStyleSheet = getKeyFramesGroup(null);
106
+ let innerId = shallowRef(0);
107
+ let stopped = shallowRef(false);
108
+ let spriteDuration = shallowRef(props.duration);
109
+ let blinkAnim = shallowRef(null);
110
+ let blinkAnimCache = reactive({});
111
+ let sAnimationToken = shallowRef(0);
112
+
113
+ const _removeKeyFrame = (names_array) => {
114
+ if (keyFrameStyleSheet) {
115
+ keyFrameStyleSheet.removeMultiRules(names_array);
116
+ }
117
+ }
118
+
119
+ const _clearExpiredKeyFrames = () => {
120
+ if (keyFrameNames.valid) {
121
+ _removeKeyFrame([keyFrameNames.clip, keyFrameNames.image]);
122
+ keyFrameNames.valid = false;
123
+ }
124
+ }
125
+
126
+ onUnmounted(() => {
127
+ _clearExpiredKeyFrames()
128
+ })
129
+
130
+ const _getAnimNameBase = () => {
131
+ return props.spriteName ? props.spriteName : `sprite-anim-name-${sAnimationToken.value++}`;
132
+ }
133
+
134
+ const _updateFrozeFrameCache = (image_url, frame_info_list,
135
+ canvas_width, canvas_height,
136
+ source_width, source_height) => {
137
+ const cache = frozeFrameCache;
138
+
139
+ const index = stopFrame.value === "start" ? 0 : frame_info_list.length - 1;
140
+ const tr = _getTransformInfo(
141
+ frame_info_list[index].source,
142
+ frame_info_list[index].target,
143
+ canvas_width,
144
+ canvas_height
145
+ );
146
+
147
+ cache.clipStyle = {
148
+ transform: _createTransformStyle(tr.csw, tr.csh, tr.cx, tr.cy),
149
+ transformOrigin: TRANSFORM_ORIGIN_LEFT_TOP.value,
150
+ overflow: "hidden",
151
+ left: 0,
152
+ top: 0,
153
+ width: canvas_width,
154
+ height: canvas_height,
155
+ };
156
+
157
+ cache.transStyle = {
158
+ transformOrigin: TRANSFORM_ORIGIN_LEFT_TOP.value,
159
+ transform: _createTransformStyle(tr.sw, tr.sh, tr.x, tr.y),
160
+ width: source_width,
161
+ height: source_height,
162
+ };
163
+
164
+ cache.imageStyle = {
165
+ backgroundImage: image_url,
166
+ width: source_width,
167
+ height: source_height,
168
+ };
169
+ }
170
+
171
+ const _updateAnimateFrameCache = (image_url, frame_info_list,
172
+ canvas_width, canvas_height,
173
+ source_width, source_height) => {
174
+ _clearExpiredKeyFrames();
175
+
176
+ if (!frame_info_list) { return; }
177
+ const anim_name_base = _getAnimNameBase();
178
+ const frame_percent = 1 / (frame_info_list.length);
179
+ const anim_name_clip = `${anim_name_base}-clip`;
180
+ const anim_name_image = `${anim_name_base}-image`;
181
+ let image_keyframs = `@keyframes ${anim_name_image} {`;
182
+ let clip_keyframs = `@keyframes ${anim_name_clip} {`;
183
+
184
+ for (let i = 0; i < frame_info_list.length + 1; i++) {
185
+ let item;
186
+ if (i !== frame_info_list.length) {
187
+ item = frame_info_list[i];
188
+ } else {
189
+ // 追加一个最后一帧以保证最后一帧可见
190
+ item = frame_info_list[frame_info_list.length - 1];
191
+ }
192
+
193
+ // Header
194
+ let header;
195
+ if (i !== frame_info_list.length) {
196
+ header = `${parseFloat(frame_percent * i * 100).toFixed(2)}% {`;
197
+ } else {
198
+ header = '100% {';
199
+ }
200
+ image_keyframs += header;
201
+ clip_keyframs += header;
202
+
203
+ if (item.source) {
204
+ const tr = _getTransformInfo(item.source, item.target, canvas_width, canvas_height);
205
+ const clip_trans = _createTransformStyle(tr.csw, tr.csh, tr.cx, tr.cy);
206
+ const image_trans = _createTransformStyle(tr.sw, tr.sh, tr.x, tr.y);
207
+
208
+ let tr_str = "";
209
+ tr_str = `${tr_str}transform:${clip_trans};transform-origin:left top;`;
210
+ clip_keyframs += tr_str;
211
+
212
+ tr_str = "";
213
+ tr_str = `${tr_str}transform:${image_trans};transform-origin:left top;`;
214
+ image_keyframs += tr_str;
215
+ }
216
+
217
+ image_keyframs += '}';
218
+ clip_keyframs += '}';
219
+ }
220
+
221
+ image_keyframs += '}';
222
+ clip_keyframs += '}';
223
+
224
+ if (keyFrameStyleSheet) {
225
+ keyFrameStyleSheet.insertRule(image_keyframs);
226
+ keyFrameStyleSheet.insertRule(clip_keyframs);
227
+
228
+ // 记录Keyframe设置,以便于界面关闭时进行清理
229
+ keyFrameNames.clip = anim_name_clip;
230
+ keyFrameNames.image = anim_name_image;
231
+ keyFrameNames.valid = true;
232
+ }
233
+ const cache = animateFrameCache;
234
+
235
+ cache.clipAnimName = `${anim_name_base}-clip`;
236
+ cache.imageAnimName = `${anim_name_base}-image`;
237
+
238
+ cache.clipStyle = {
239
+ overflow: "hidden",
240
+ width: canvas_width,
241
+ height: canvas_height,
242
+ transform: null, // 重置 transform,以免影响keyframe动画
243
+ transformOrigin: TRANSFORM_ORIGIN_LEFT_TOP.value,
244
+ animation: null, // 外部设置,需要时间和loop信息
245
+ };
246
+
247
+ cache.transStyle = {
248
+ transform: null, // 重置 transform,以免影响keyframe动画
249
+ transformOrigin: TRANSFORM_ORIGIN_LEFT_TOP.value,
250
+ animation: null, // 外部设置,需要时间和loop信息
251
+ width: source_width,
252
+ height: source_height,
253
+ };
254
+
255
+ cache.imageStyle = {
256
+ backgroundImage: image_url,
257
+ width: source_width,
258
+ height: source_height,
259
+ };
260
+ }
261
+
262
+ const _IsAutoStart = () => {
263
+ let autoStart = false;
264
+ if (typeof props.autostart === "boolean") {
265
+ autoStart = props.autostart;
266
+ } if (typeof props.autostart === "string" && props.autostart !== "none") {
267
+ autoStart = true;
268
+ }
269
+ return autoStart;
270
+ }
271
+
272
+
273
+ const _AnalyzeProp = () => {
274
+ const used = props.controller && props.controller.Used;
275
+ if (props.spriteInfo.frames.length === 1 || (!used && !_IsAutoStart()) || stopped.value) {
276
+ // 单图模式
277
+ // 解析图片信息
278
+ _updateFrozeFrameCache(
279
+ props.imageUrl,
280
+ props.spriteInfo.frames,
281
+ props.viewSize.w,
282
+ props.viewSize.h,
283
+ props.spriteInfo.meta.size.w,
284
+ props.spriteInfo.meta.size.h
285
+ );
286
+
287
+ return {
288
+ clipStyle: frozeFrameCache.clipStyle,
289
+ transStyle: frozeFrameCache.transStyle,
290
+ imageStyle: frozeFrameCache.imageStyle,
291
+ };
292
+ }
293
+
294
+
295
+ // 动画模式
296
+ if (animateFrameCache.imageStyle.backgroundImage !== props.imageUrl) {
297
+ // 解析图片信息
298
+ _updateAnimateFrameCache(
299
+ props.imageUrl,
300
+ props.spriteInfo.frames,
301
+ props.viewSize.w,
302
+ props.viewSize.h,
303
+ props.spriteInfo.meta.size.w,
304
+ props.spriteInfo.meta.size.h
305
+ );
306
+ }
307
+
308
+ // 使用duration和loop信息更新动画设定
309
+ animateFrameCache.clipStyle.animation =
310
+ `${animateFrameCache.clipAnimName} ${spriteDuration.value}s steps(1,end) ${props.loop}`;
311
+ animateFrameCache.transStyle.animation =
312
+ `${animateFrameCache.imageAnimName} ${spriteDuration.value}s steps(1,end) ${props.loop}`;
313
+
314
+ return {
315
+ clipStyle: animateFrameCache.clipStyle,
316
+ transStyle: animateFrameCache.transStyle,
317
+ imageStyle: animateFrameCache.imageStyle,
318
+ };
319
+ }
320
+ let transform_style = reactive(_AnalyzeProp());
321
+
322
+ watch(props, (n, o) => {
323
+ transform_style = _AnalyzeProp();
324
+ })
325
+ const onAnimEndDelegate = () => {
326
+ // 在onAnimEnd之前进行Stop,以防onAnimEnd内部继续发生别的操作
327
+ stopped.value = true;
328
+ if (props.onAnimEnd) {
329
+ props.onAnimEnd();
330
+ }
331
+ }
332
+
333
+ const onBlinkAnimEnd = () => {
334
+ // 在onAnimEnd之前进行Stop,以防onAnimEnd内部继续发生别的操作
335
+ blinkAnim.value = null;
336
+ }
337
+
338
+ const op = {
339
+ start: (end_frame, duration) => {
340
+ if (props.spriteInfo.frames && props.spriteInfo.frames.length === 1) {
341
+ return;
342
+ }
343
+ innerId.value += 1;
344
+ stopped.value = false;
345
+ spriteDuration.value = duration || props.duration;
346
+ stopFrame.value = end_frame || stopFrame.value;
347
+ },
348
+ stop: (end_frame) => {
349
+ if (props.spriteInfo.frames && props.spriteInfo.frames.length === 1) {
350
+ return;
351
+ }
352
+ stopped.value = true;
353
+ stopFrame.value = end_frame || stopFrame.value;
354
+ },
355
+ blink: (alphas, duration, ease, delay, repeat) => {
356
+ // 注意:比较数组是否相同仅在此场景下使用toString,其他场景
357
+ if (!blinkAnimCache
358
+ || (blinkAnimCache.alphas.toString() !== alphas.toString()
359
+ || blinkAnimCache.duration !== duration
360
+ || blinkAnimCache.ease !== ease
361
+ || blinkAnimCache.delay !== delay
362
+ || blinkAnimCache.repeat !== repeat)) {
363
+ const anim_name_base = _getAnimNameBase();
364
+ const anim_name_blink = `${anim_name_base}-blink`;
365
+ let image_keyframs = `@keyframes ${anim_name_blink} {`;
366
+ const frame_percent = 1 / (alphas.length);
367
+ for (let i = 0; i < alphas.length; i++) {
368
+ const alpha = alphas[i];
369
+ let header;
370
+ if (i !== alphas.length - 1) {
371
+ header = `${parseFloat(frame_percent * i * 100).toFixed(2)}% {`;
372
+ } else {
373
+ header = '100% {';
374
+ }
375
+ image_keyframs += header;
376
+
377
+ if (alpha) {
378
+ const tr_str = ` opacity:${alpha};`;
379
+ image_keyframs += tr_str;
380
+ }
381
+
382
+ image_keyframs += '}';
383
+ image_keyframs += "\n";
384
+ }
385
+ image_keyframs += '}';
386
+ if (keyFrameStyleSheet) {
387
+ keyFrameStyleSheet.insertRule(image_keyframs);
388
+ }
389
+ blinkAnimCache = {
390
+ alphas,
391
+ duration,
392
+ ease,
393
+ delay,
394
+ repeat,
395
+ blinkAnimName: anim_name_blink
396
+ };
397
+ }
398
+
399
+ // 参数格式化
400
+ ease = ease || "";
401
+ delay = delay || 0;
402
+ repeat = (repeat === -1) ? "infinite" : (repeat || 1);
403
+
404
+ const animName = `${blinkAnimCache.blinkAnimName} ${duration}s ${ease} ${delay}s ${repeat}`;
405
+ blinkAnim.value = animName;
406
+ stopped.value = true;
407
+ }
408
+
409
+ };
410
+ //TODO:
411
+ props.controller?._setSpriteImg(op);
412
+ </script>
413
+
414
+ <template>
415
+ <div id="canvas">
416
+ <div id="clip" :style="{ ...transform_style.clipStyle }">
417
+ <div id="trans" :style="{ ...transform_style.transStyle }" @animationend="onAnimEndDelegate">
418
+ <div id="image" :style="{ ...transform_style.imageStyle, animation: blinkAnim }"
419
+ @animationend="onBlinkAnimEnd"></div>
420
+ </div>
421
+ </div>
422
+ </div>
423
+ </template>
424
+
425
+ <style scoped>
426
+ @keyframes sprite-tag {}
427
+ </style>
@@ -11,7 +11,7 @@
11
11
  -->
12
12
 
13
13
  <script setup>
14
- import { ref, computed } from "vue";
14
+ import { computed } from "vue";
15
15
 
16
16
  const props = defineProps({
17
17
  style: Object,