@eva/plugin-tween 2.1.0-beta.2 → 2.1.0-beta.3

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,351 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ var eva_js = require('@eva/eva.js');
6
+ var pluginSignalBus = require('@eva/plugin-signal-bus');
7
+
8
+ /******************************************************************************
9
+ Copyright (c) Microsoft Corporation.
10
+
11
+ Permission to use, copy, modify, and/or distribute this software for any
12
+ purpose with or without fee is hereby granted.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
15
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
16
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
17
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
18
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
19
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20
+ PERFORMANCE OF THIS SOFTWARE.
21
+ ***************************************************************************** */
22
+
23
+ function __decorate(decorators, target, key, desc) {
24
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
25
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
26
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
27
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
28
+ }
29
+
30
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
31
+ var e = new Error(message);
32
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
33
+ };
34
+
35
+ const PI = Math.PI;
36
+ const c1 = 1.70158;
37
+ const c2 = c1 * 1.525;
38
+ const c3 = c1 + 1;
39
+ const c4 = (2 * PI) / 3;
40
+ const c5 = (2 * PI) / 4.5;
41
+ const n1 = 7.5625;
42
+ const d1 = 2.75;
43
+ function easeInBounce(x) {
44
+ return 1 - easeOutBounce(1 - x);
45
+ }
46
+ function easeOutBounce(x) {
47
+ if (x < 1 / d1)
48
+ return n1 * x * x;
49
+ if (x < 2 / d1)
50
+ return n1 * (x -= 1.5 / d1) * x + 0.75;
51
+ if (x < 2.5 / d1)
52
+ return n1 * (x -= 2.25 / d1) * x + 0.9375;
53
+ return n1 * (x -= 2.625 / d1) * x + 0.984375;
54
+ }
55
+ const Easing = {
56
+ linear: (t) => t,
57
+ easeInQuad: (t) => t * t,
58
+ easeOutQuad: (t) => 1 - (1 - t) * (1 - t),
59
+ easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2),
60
+ easeInCubic: (t) => t * t * t,
61
+ easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
62
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2),
63
+ easeInElastic: (t) => (t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * c4)),
64
+ easeOutElastic: (t) => (t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1),
65
+ easeInOutElastic: (t) => {
66
+ if (t === 0)
67
+ return 0;
68
+ if (t === 1)
69
+ return 1;
70
+ return t < 0.5
71
+ ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2
72
+ : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5)) / 2 + 1;
73
+ },
74
+ easeInBack: (t) => c3 * t * t * t - c1 * t * t,
75
+ easeOutBack: (t) => 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2),
76
+ easeInOutBack: (t) => t < 0.5
77
+ ? (Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
78
+ : (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2,
79
+ easeInBounce,
80
+ easeOutBounce,
81
+ easeInOutBounce: (t) => (t < 0.5 ? (1 - easeOutBounce(1 - 2 * t)) / 2 : (1 + easeOutBounce(2 * t - 1)) / 2),
82
+ };
83
+
84
+ function bindPath(go, path) {
85
+ const parts = path.split('.');
86
+ if (parts[0] === 'transform') {
87
+ const obj = go.transform;
88
+ return diveIntoObject(obj, parts.slice(1));
89
+ }
90
+ if (parts[0] === 'components' && parts.length >= 3) {
91
+ const compName = parts[1];
92
+ const comp = findComponent(go, compName);
93
+ if (!comp)
94
+ return null;
95
+ return diveIntoObject(comp, parts.slice(2));
96
+ }
97
+ if (parts[0] === 'store' && parts.length >= 2) {
98
+ return bindStore(parts.slice(1).join('.'));
99
+ }
100
+ return null;
101
+ }
102
+ function diveIntoObject(root, keys) {
103
+ if (!root || keys.length === 0)
104
+ return null;
105
+ let parent = root;
106
+ for (let i = 0; i < keys.length - 1; i++) {
107
+ const k = keys[i];
108
+ if (parent[k] == null)
109
+ return null;
110
+ parent = parent[k];
111
+ }
112
+ const last = keys[keys.length - 1];
113
+ return {
114
+ read: () => { var _a; return Number((_a = parent[last]) !== null && _a !== void 0 ? _a : 0); },
115
+ write: (v) => {
116
+ parent[last] = v;
117
+ },
118
+ };
119
+ }
120
+ function findComponent(go, name) {
121
+ var _a, _b;
122
+ const comps = go.components || [];
123
+ for (const c of comps) {
124
+ const cn = (_a = c === null || c === void 0 ? void 0 : c.constructor) === null || _a === void 0 ? void 0 : _a.componentName;
125
+ if (cn === name)
126
+ return c;
127
+ }
128
+ // 退路:Eva.js 部分版本支持 getComponent(name)
129
+ if (typeof go.getComponent === 'function') {
130
+ return (_b = go.getComponent(name)) !== null && _b !== void 0 ? _b : null;
131
+ }
132
+ return null;
133
+ }
134
+ function bindStore(key) {
135
+ // mx.store 是宿主全局,不强依赖
136
+ const mx = globalThis.mx;
137
+ if (!(mx === null || mx === void 0 ? void 0 : mx.store))
138
+ return null;
139
+ return {
140
+ read: () => { var _a, _b, _c; return Number((_c = (_b = (_a = mx.store).get) === null || _b === void 0 ? void 0 : _b.call(_a, key)) !== null && _c !== void 0 ? _c : 0); },
141
+ write: (v) => {
142
+ var _a, _b;
143
+ (_b = (_a = mx.store).update) === null || _b === void 0 ? void 0 : _b.call(_a, { [key]: v });
144
+ },
145
+ };
146
+ }
147
+
148
+ /**
149
+ * Tween 组件 — Godot Tween 等价物。
150
+ *
151
+ * 与 plugin-transition 区别:
152
+ * - 不限制 target 类型(transform/Component/store 都行)
153
+ * - 内置 sequence 与 parallel 编排
154
+ * - 完成 emit 命名信号
155
+ *
156
+ * DSL 用法:
157
+ * ```json
158
+ * {
159
+ * "type": "Tween",
160
+ * "props": {
161
+ * "step": {
162
+ * "target": "components.RocketAimer.currentAngleDeg",
163
+ * "from": -60, "to": 60, "duration": 1500, "easing": "easeInOutQuad"
164
+ * },
165
+ * "yoyo": true, "loop": -1, "autostart": true
166
+ * }
167
+ * }
168
+ * ```
169
+ */
170
+ exports.Tween = class Tween extends eva_js.Component {
171
+ constructor() {
172
+ super(...arguments);
173
+ this.steps = [];
174
+ this.parallel = false;
175
+ this.yoyo = false;
176
+ this.loop = 0;
177
+ this.autostart = false;
178
+ this.running = [];
179
+ this.isPlaying = false;
180
+ this.playedLoops = 0;
181
+ this.cursor = 0; // sequence 当前 step
182
+ this.direction = 1;
183
+ }
184
+ init(params) {
185
+ var _a, _b, _c, _d, _e;
186
+ if (!params)
187
+ return;
188
+ this.steps = params.step ? [params.step] : ((_a = params.steps) !== null && _a !== void 0 ? _a : []);
189
+ this.parallel = (_b = params.parallel) !== null && _b !== void 0 ? _b : false;
190
+ this.yoyo = (_c = params.yoyo) !== null && _c !== void 0 ? _c : false;
191
+ this.loop = (_d = params.loop) !== null && _d !== void 0 ? _d : 0;
192
+ this.autostart = (_e = params.autostart) !== null && _e !== void 0 ? _e : false;
193
+ this.signal = params.signal;
194
+ }
195
+ awake() {
196
+ if (this.autostart)
197
+ this.play();
198
+ }
199
+ /** 重新开始 */
200
+ play() {
201
+ this.buildRunning();
202
+ this.isPlaying = true;
203
+ this.playedLoops = 0;
204
+ this.cursor = 0;
205
+ this.direction = 1;
206
+ }
207
+ pause() {
208
+ this.isPlaying = false;
209
+ }
210
+ resume() {
211
+ if (this.running.length)
212
+ this.isPlaying = true;
213
+ }
214
+ stop() {
215
+ this.isPlaying = false;
216
+ this.running = [];
217
+ }
218
+ buildRunning() {
219
+ var _a, _b, _c, _d;
220
+ this.running = [];
221
+ for (const s of this.steps) {
222
+ const binding = bindPath(this.gameObject, s.target);
223
+ if (!binding) {
224
+ // eslint-disable-next-line no-console
225
+ console.warn(`[plugin-tween] binding miss: ${s.target}`);
226
+ continue;
227
+ }
228
+ const from = (_a = s.from) !== null && _a !== void 0 ? _a : binding.read();
229
+ this.running.push({
230
+ binding,
231
+ from,
232
+ to: s.to,
233
+ duration: Math.max(1, s.duration),
234
+ easing: (_c = Easing[((_b = s.easing) !== null && _b !== void 0 ? _b : 'linear')]) !== null && _c !== void 0 ? _c : Easing.linear,
235
+ delay: (_d = s.delay) !== null && _d !== void 0 ? _d : 0,
236
+ elapsed: 0,
237
+ done: false,
238
+ });
239
+ }
240
+ }
241
+ update(e) {
242
+ if (!this.isPlaying || this.running.length === 0)
243
+ return;
244
+ const dt = e.deltaTime;
245
+ if (this.parallel) {
246
+ this.advanceParallel(dt);
247
+ }
248
+ else {
249
+ this.advanceSequence(dt);
250
+ }
251
+ }
252
+ advanceParallel(dt) {
253
+ let allDone = true;
254
+ for (const r of this.running) {
255
+ if (r.done)
256
+ continue;
257
+ this.tickStep(r, dt);
258
+ if (!r.done)
259
+ allDone = false;
260
+ }
261
+ if (allDone)
262
+ this.onCycleEnd();
263
+ }
264
+ advanceSequence(dt) {
265
+ while (dt > 0 && this.cursor < this.running.length) {
266
+ const r = this.running[this.cursor];
267
+ if (r.done) {
268
+ this.cursor++;
269
+ continue;
270
+ }
271
+ const before = r.elapsed;
272
+ this.tickStep(r, dt);
273
+ const used = r.elapsed - before;
274
+ dt -= used;
275
+ if (!r.done)
276
+ break;
277
+ this.cursor++;
278
+ }
279
+ if (this.cursor >= this.running.length)
280
+ this.onCycleEnd();
281
+ }
282
+ tickStep(r, dt) {
283
+ if (r.delay > 0) {
284
+ const used = Math.min(r.delay, dt);
285
+ r.delay -= used;
286
+ // 把延时算进 elapsed 不合适,留给 sequence 减 dt
287
+ // 这里直接消耗 dt 后返回
288
+ return;
289
+ }
290
+ r.elapsed += dt;
291
+ const t = Math.min(1, r.elapsed / r.duration);
292
+ const v = r.from + (r.to - r.from) * r.easing(t);
293
+ r.binding.write(v);
294
+ if (t >= 1) {
295
+ r.done = true;
296
+ }
297
+ }
298
+ onCycleEnd() {
299
+ this.playedLoops++;
300
+ const shouldLoop = this.loop === -1 || this.playedLoops <= this.loop;
301
+ if (this.yoyo) {
302
+ this.direction = (this.direction === 1 ? -1 : 1);
303
+ // yoyo:反向重置
304
+ for (const r of this.running) {
305
+ const a = r.from;
306
+ r.from = r.to;
307
+ r.to = a;
308
+ r.elapsed = 0;
309
+ r.done = false;
310
+ }
311
+ this.cursor = 0;
312
+ if (!shouldLoop && this.direction === 1) {
313
+ this.finish();
314
+ }
315
+ return;
316
+ }
317
+ if (shouldLoop) {
318
+ for (const r of this.running) {
319
+ r.elapsed = 0;
320
+ r.done = false;
321
+ }
322
+ this.cursor = 0;
323
+ return;
324
+ }
325
+ this.finish();
326
+ }
327
+ finish() {
328
+ this.isPlaying = false;
329
+ if (this.signal)
330
+ pluginSignalBus.getSignalBus().emit(this.signal, { component: this });
331
+ pluginSignalBus.getSignalBus().emit('tween:finish', { component: this });
332
+ }
333
+ };
334
+ exports.Tween.componentName = 'Tween';
335
+ exports.Tween = __decorate([
336
+ eva_js.decorators.componentObserver({})
337
+ ], exports.Tween);
338
+
339
+ /**
340
+ * Tween 没有跨实体编排,System 仅用于注册 plugin。
341
+ */
342
+ class TweenSystem extends eva_js.System {
343
+ constructor() {
344
+ super(...arguments);
345
+ this.name = 'Tween';
346
+ }
347
+ }
348
+ TweenSystem.systemName = 'Tween';
349
+
350
+ exports.Easing = Easing;
351
+ exports.TweenSystem = TweenSystem;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var t=require("@eva/eva.js"),e=require("@eva/plugin-signal-bus");"function"==typeof SuppressedError&&SuppressedError;const n=Math.PI,i=1.70158,s=1.525*i,o=i+1,r=2*n/3,a=2*n/4.5,l=7.5625,u=2.75;function c(t){return t<1/u?l*t*t:t<2/u?l*(t-=1.5/u)*t+.75:t<2.5/u?l*(t-=2.25/u)*t+.9375:l*(t-=2.625/u)*t+.984375}const h={linear:t=>t,easeInQuad:t=>t*t,easeOutQuad:t=>1-(1-t)*(1-t),easeInOutQuad:t=>t<.5?2*t*t:1-Math.pow(-2*t+2,2)/2,easeInCubic:t=>t*t*t,easeOutCubic:t=>1-Math.pow(1-t,3),easeInOutCubic:t=>t<.5?4*t*t*t:1-Math.pow(-2*t+2,3)/2,easeInElastic:t=>0===t?0:1===t?1:-Math.pow(2,10*t-10)*Math.sin((10*t-10.75)*r),easeOutElastic:t=>0===t?0:1===t?1:Math.pow(2,-10*t)*Math.sin((10*t-.75)*r)+1,easeInOutElastic:t=>0===t?0:1===t?1:t<.5?-Math.pow(2,20*t-10)*Math.sin((20*t-11.125)*a)/2:Math.pow(2,-20*t+10)*Math.sin((20*t-11.125)*a)/2+1,easeInBack:t=>o*t*t*t-i*t*t,easeOutBack:t=>1+o*Math.pow(t-1,3)+i*Math.pow(t-1,2),easeInOutBack:t=>t<.5?Math.pow(2*t,2)*(7.189819*t-s)/2:(Math.pow(2*t-2,2)*((s+1)*(2*t-2)+s)+2)/2,easeInBounce:function(t){return 1-c(1-t)},easeOutBounce:c,easeInOutBounce:t=>t<.5?(1-c(1-2*t))/2:(1+c(2*t-1))/2};function p(t,e){const n=e.split(".");if("transform"===n[0]){return d(t.transform,n.slice(1))}if("components"===n[0]&&n.length>=3){const e=function(t,e){var n,i;const s=t.components||[];for(const t of s){if((null===(n=null==t?void 0:t.constructor)||void 0===n?void 0:n.componentName)===e)return t}if("function"==typeof t.getComponent)return null!==(i=t.getComponent(e))&&void 0!==i?i:null;return null}(t,n[1]);return e?d(e,n.slice(2)):null}return"store"===n[0]&&n.length>=2?function(t){const e=globalThis.mx;return(null==e?void 0:e.store)?{read:()=>{var n,i,s;return Number(null!==(s=null===(i=(n=e.store).get)||void 0===i?void 0:i.call(n,t))&&void 0!==s?s:0)},write:n=>{var i,s;null===(s=(i=e.store).update)||void 0===s||s.call(i,{[t]:n})}}:null}(n.slice(1).join(".")):null}function d(t,e){if(!t||0===e.length)return null;let n=t;for(let t=0;t<e.length-1;t++){const i=e[t];if(null==n[i])return null;n=n[i]}const i=e[e.length-1];return{read:()=>{var t;return Number(null!==(t=n[i])&&void 0!==t?t:0)},write:t=>{n[i]=t}}}exports.Tween=class extends t.Component{constructor(){super(...arguments),this.steps=[],this.parallel=!1,this.yoyo=!1,this.loop=0,this.autostart=!1,this.running=[],this.isPlaying=!1,this.playedLoops=0,this.cursor=0,this.direction=1}init(t){var e,n,i,s,o;t&&(this.steps=t.step?[t.step]:null!==(e=t.steps)&&void 0!==e?e:[],this.parallel=null!==(n=t.parallel)&&void 0!==n&&n,this.yoyo=null!==(i=t.yoyo)&&void 0!==i&&i,this.loop=null!==(s=t.loop)&&void 0!==s?s:0,this.autostart=null!==(o=t.autostart)&&void 0!==o&&o,this.signal=t.signal)}awake(){this.autostart&&this.play()}play(){this.buildRunning(),this.isPlaying=!0,this.playedLoops=0,this.cursor=0,this.direction=1}pause(){this.isPlaying=!1}resume(){this.running.length&&(this.isPlaying=!0)}stop(){this.isPlaying=!1,this.running=[]}buildRunning(){var t,e,n,i;this.running=[];for(const s of this.steps){const o=p(this.gameObject,s.target);if(!o){console.warn(`[plugin-tween] binding miss: ${s.target}`);continue}const r=null!==(t=s.from)&&void 0!==t?t:o.read();this.running.push({binding:o,from:r,to:s.to,duration:Math.max(1,s.duration),easing:null!==(n=h[null!==(e=s.easing)&&void 0!==e?e:"linear"])&&void 0!==n?n:h.linear,delay:null!==(i=s.delay)&&void 0!==i?i:0,elapsed:0,done:!1})}}update(t){if(!this.isPlaying||0===this.running.length)return;const e=t.deltaTime;this.parallel?this.advanceParallel(e):this.advanceSequence(e)}advanceParallel(t){let e=!0;for(const n of this.running)n.done||(this.tickStep(n,t),n.done||(e=!1));e&&this.onCycleEnd()}advanceSequence(t){for(;t>0&&this.cursor<this.running.length;){const e=this.running[this.cursor];if(e.done){this.cursor++;continue}const n=e.elapsed;this.tickStep(e,t);if(t-=e.elapsed-n,!e.done)break;this.cursor++}this.cursor>=this.running.length&&this.onCycleEnd()}tickStep(t,e){if(t.delay>0){const n=Math.min(t.delay,e);return void(t.delay-=n)}t.elapsed+=e;const n=Math.min(1,t.elapsed/t.duration),i=t.from+(t.to-t.from)*t.easing(n);t.binding.write(i),n>=1&&(t.done=!0)}onCycleEnd(){this.playedLoops++;const t=-1===this.loop||this.playedLoops<=this.loop;if(this.yoyo){this.direction=1===this.direction?-1:1;for(const t of this.running){const e=t.from;t.from=t.to,t.to=e,t.elapsed=0,t.done=!1}return this.cursor=0,void(t||1!==this.direction||this.finish())}if(t){for(const t of this.running)t.elapsed=0,t.done=!1;this.cursor=0}else this.finish()}finish(){this.isPlaying=!1,this.signal&&e.getSignalBus().emit(this.signal,{component:this}),e.getSignalBus().emit("tween:finish",{component:this})}},exports.Tween.componentName="Tween",exports.Tween=function(t,e,n,i){var s,o=arguments.length,r=o<3?e:null===i?i=Object.getOwnPropertyDescriptor(e,n):i;if("object"==typeof Reflect&&"function"==typeof Reflect.decorate)r=Reflect.decorate(t,e,n,i);else for(var a=t.length-1;a>=0;a--)(s=t[a])&&(r=(o<3?s(r):o>3?s(e,n,r):s(e,n))||r);return o>3&&r&&Object.defineProperty(e,n,r),r}([t.decorators.componentObserver({})],exports.Tween);class f extends t.System{constructor(){super(...arguments),this.name="Tween"}}f.systemName="Tween",exports.Easing=h,exports.TweenSystem=f;
@@ -0,0 +1,99 @@
1
+ import { Component } from '@eva/eva.js';
2
+ import { System } from '@eva/eva.js';
3
+
4
+ export declare const Easing: Record<EasingName, (t: number) => number>;
5
+
6
+ export declare type EasingName = 'linear' | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad' | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic' | 'easeInElastic' | 'easeOutElastic' | 'easeInOutElastic' | 'easeInBack' | 'easeOutBack' | 'easeInOutBack' | 'easeInBounce' | 'easeOutBounce' | 'easeInOutBounce';
7
+
8
+ /**
9
+ * Tween 组件 — Godot Tween 等价物。
10
+ *
11
+ * 与 plugin-transition 区别:
12
+ * - 不限制 target 类型(transform/Component/store 都行)
13
+ * - 内置 sequence 与 parallel 编排
14
+ * - 完成 emit 命名信号
15
+ *
16
+ * DSL 用法:
17
+ * ```json
18
+ * {
19
+ * "type": "Tween",
20
+ * "props": {
21
+ * "step": {
22
+ * "target": "components.RocketAimer.currentAngleDeg",
23
+ * "from": -60, "to": 60, "duration": 1500, "easing": "easeInOutQuad"
24
+ * },
25
+ * "yoyo": true, "loop": -1, "autostart": true
26
+ * }
27
+ * }
28
+ * ```
29
+ */
30
+ export declare class Tween extends Component<TweenParams> {
31
+ static componentName: string;
32
+ private steps;
33
+ private parallel;
34
+ private yoyo;
35
+ private loop;
36
+ private autostart;
37
+ private signal?;
38
+ private running;
39
+ private isPlaying;
40
+ private playedLoops;
41
+ private cursor;
42
+ private direction;
43
+ init(params?: TweenParams): void;
44
+ awake(): void;
45
+ /** 重新开始 */
46
+ play(): void;
47
+ pause(): void;
48
+ resume(): void;
49
+ stop(): void;
50
+ private buildRunning;
51
+ update(e: {
52
+ deltaTime: number;
53
+ }): void;
54
+ private advanceParallel;
55
+ private advanceSequence;
56
+ private tickStep;
57
+ private onCycleEnd;
58
+ private finish;
59
+ }
60
+
61
+ export declare interface TweenParams {
62
+ /** 单步 = 一次性 to;steps = 多步序列 */
63
+ step?: TweenStep;
64
+ steps?: TweenStep[];
65
+ /** 是否并行执行 steps(默认 sequence) */
66
+ parallel?: boolean;
67
+ yoyo?: boolean;
68
+ /** -1 = 无限循环 */
69
+ loop?: number;
70
+ /** 是否自动开始 */
71
+ autostart?: boolean;
72
+ /** 完成后 emit 的信号 */
73
+ signal?: string;
74
+ }
75
+
76
+ export declare interface TweenStep {
77
+ /** 目标路径,例如:
78
+ * - "transform.position.x"
79
+ * - "transform.rotation"
80
+ * - "components.RocketAimer.currentAngleDeg"
81
+ * - "store.score" (走 mx.store)
82
+ */
83
+ target: string;
84
+ from?: number;
85
+ to: number;
86
+ duration: number;
87
+ easing?: EasingName;
88
+ delay?: number;
89
+ }
90
+
91
+ /**
92
+ * Tween 没有跨实体编排,System 仅用于注册 plugin。
93
+ */
94
+ export declare class TweenSystem extends System {
95
+ static systemName: string;
96
+ readonly name = "Tween";
97
+ }
98
+
99
+ export { }
@@ -0,0 +1,346 @@
1
+ import { Component, decorators, System } from '@eva/eva.js';
2
+ import { getSignalBus } from '@eva/plugin-signal-bus';
3
+
4
+ /******************************************************************************
5
+ Copyright (c) Microsoft Corporation.
6
+
7
+ Permission to use, copy, modify, and/or distribute this software for any
8
+ purpose with or without fee is hereby granted.
9
+
10
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
11
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
13
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
15
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16
+ PERFORMANCE OF THIS SOFTWARE.
17
+ ***************************************************************************** */
18
+
19
+ function __decorate(decorators, target, key, desc) {
20
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
21
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
22
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
23
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
24
+ }
25
+
26
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
27
+ var e = new Error(message);
28
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
29
+ };
30
+
31
+ const PI = Math.PI;
32
+ const c1 = 1.70158;
33
+ const c2 = c1 * 1.525;
34
+ const c3 = c1 + 1;
35
+ const c4 = (2 * PI) / 3;
36
+ const c5 = (2 * PI) / 4.5;
37
+ const n1 = 7.5625;
38
+ const d1 = 2.75;
39
+ function easeInBounce(x) {
40
+ return 1 - easeOutBounce(1 - x);
41
+ }
42
+ function easeOutBounce(x) {
43
+ if (x < 1 / d1)
44
+ return n1 * x * x;
45
+ if (x < 2 / d1)
46
+ return n1 * (x -= 1.5 / d1) * x + 0.75;
47
+ if (x < 2.5 / d1)
48
+ return n1 * (x -= 2.25 / d1) * x + 0.9375;
49
+ return n1 * (x -= 2.625 / d1) * x + 0.984375;
50
+ }
51
+ const Easing = {
52
+ linear: (t) => t,
53
+ easeInQuad: (t) => t * t,
54
+ easeOutQuad: (t) => 1 - (1 - t) * (1 - t),
55
+ easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2),
56
+ easeInCubic: (t) => t * t * t,
57
+ easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
58
+ easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2),
59
+ easeInElastic: (t) => (t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * c4)),
60
+ easeOutElastic: (t) => (t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1),
61
+ easeInOutElastic: (t) => {
62
+ if (t === 0)
63
+ return 0;
64
+ if (t === 1)
65
+ return 1;
66
+ return t < 0.5
67
+ ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2
68
+ : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5)) / 2 + 1;
69
+ },
70
+ easeInBack: (t) => c3 * t * t * t - c1 * t * t,
71
+ easeOutBack: (t) => 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2),
72
+ easeInOutBack: (t) => t < 0.5
73
+ ? (Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
74
+ : (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2,
75
+ easeInBounce,
76
+ easeOutBounce,
77
+ easeInOutBounce: (t) => (t < 0.5 ? (1 - easeOutBounce(1 - 2 * t)) / 2 : (1 + easeOutBounce(2 * t - 1)) / 2),
78
+ };
79
+
80
+ function bindPath(go, path) {
81
+ const parts = path.split('.');
82
+ if (parts[0] === 'transform') {
83
+ const obj = go.transform;
84
+ return diveIntoObject(obj, parts.slice(1));
85
+ }
86
+ if (parts[0] === 'components' && parts.length >= 3) {
87
+ const compName = parts[1];
88
+ const comp = findComponent(go, compName);
89
+ if (!comp)
90
+ return null;
91
+ return diveIntoObject(comp, parts.slice(2));
92
+ }
93
+ if (parts[0] === 'store' && parts.length >= 2) {
94
+ return bindStore(parts.slice(1).join('.'));
95
+ }
96
+ return null;
97
+ }
98
+ function diveIntoObject(root, keys) {
99
+ if (!root || keys.length === 0)
100
+ return null;
101
+ let parent = root;
102
+ for (let i = 0; i < keys.length - 1; i++) {
103
+ const k = keys[i];
104
+ if (parent[k] == null)
105
+ return null;
106
+ parent = parent[k];
107
+ }
108
+ const last = keys[keys.length - 1];
109
+ return {
110
+ read: () => { var _a; return Number((_a = parent[last]) !== null && _a !== void 0 ? _a : 0); },
111
+ write: (v) => {
112
+ parent[last] = v;
113
+ },
114
+ };
115
+ }
116
+ function findComponent(go, name) {
117
+ var _a, _b;
118
+ const comps = go.components || [];
119
+ for (const c of comps) {
120
+ const cn = (_a = c === null || c === void 0 ? void 0 : c.constructor) === null || _a === void 0 ? void 0 : _a.componentName;
121
+ if (cn === name)
122
+ return c;
123
+ }
124
+ // 退路:Eva.js 部分版本支持 getComponent(name)
125
+ if (typeof go.getComponent === 'function') {
126
+ return (_b = go.getComponent(name)) !== null && _b !== void 0 ? _b : null;
127
+ }
128
+ return null;
129
+ }
130
+ function bindStore(key) {
131
+ // mx.store 是宿主全局,不强依赖
132
+ const mx = globalThis.mx;
133
+ if (!(mx === null || mx === void 0 ? void 0 : mx.store))
134
+ return null;
135
+ return {
136
+ read: () => { var _a, _b, _c; return Number((_c = (_b = (_a = mx.store).get) === null || _b === void 0 ? void 0 : _b.call(_a, key)) !== null && _c !== void 0 ? _c : 0); },
137
+ write: (v) => {
138
+ var _a, _b;
139
+ (_b = (_a = mx.store).update) === null || _b === void 0 ? void 0 : _b.call(_a, { [key]: v });
140
+ },
141
+ };
142
+ }
143
+
144
+ /**
145
+ * Tween 组件 — Godot Tween 等价物。
146
+ *
147
+ * 与 plugin-transition 区别:
148
+ * - 不限制 target 类型(transform/Component/store 都行)
149
+ * - 内置 sequence 与 parallel 编排
150
+ * - 完成 emit 命名信号
151
+ *
152
+ * DSL 用法:
153
+ * ```json
154
+ * {
155
+ * "type": "Tween",
156
+ * "props": {
157
+ * "step": {
158
+ * "target": "components.RocketAimer.currentAngleDeg",
159
+ * "from": -60, "to": 60, "duration": 1500, "easing": "easeInOutQuad"
160
+ * },
161
+ * "yoyo": true, "loop": -1, "autostart": true
162
+ * }
163
+ * }
164
+ * ```
165
+ */
166
+ let Tween = class Tween extends Component {
167
+ constructor() {
168
+ super(...arguments);
169
+ this.steps = [];
170
+ this.parallel = false;
171
+ this.yoyo = false;
172
+ this.loop = 0;
173
+ this.autostart = false;
174
+ this.running = [];
175
+ this.isPlaying = false;
176
+ this.playedLoops = 0;
177
+ this.cursor = 0; // sequence 当前 step
178
+ this.direction = 1;
179
+ }
180
+ init(params) {
181
+ var _a, _b, _c, _d, _e;
182
+ if (!params)
183
+ return;
184
+ this.steps = params.step ? [params.step] : ((_a = params.steps) !== null && _a !== void 0 ? _a : []);
185
+ this.parallel = (_b = params.parallel) !== null && _b !== void 0 ? _b : false;
186
+ this.yoyo = (_c = params.yoyo) !== null && _c !== void 0 ? _c : false;
187
+ this.loop = (_d = params.loop) !== null && _d !== void 0 ? _d : 0;
188
+ this.autostart = (_e = params.autostart) !== null && _e !== void 0 ? _e : false;
189
+ this.signal = params.signal;
190
+ }
191
+ awake() {
192
+ if (this.autostart)
193
+ this.play();
194
+ }
195
+ /** 重新开始 */
196
+ play() {
197
+ this.buildRunning();
198
+ this.isPlaying = true;
199
+ this.playedLoops = 0;
200
+ this.cursor = 0;
201
+ this.direction = 1;
202
+ }
203
+ pause() {
204
+ this.isPlaying = false;
205
+ }
206
+ resume() {
207
+ if (this.running.length)
208
+ this.isPlaying = true;
209
+ }
210
+ stop() {
211
+ this.isPlaying = false;
212
+ this.running = [];
213
+ }
214
+ buildRunning() {
215
+ var _a, _b, _c, _d;
216
+ this.running = [];
217
+ for (const s of this.steps) {
218
+ const binding = bindPath(this.gameObject, s.target);
219
+ if (!binding) {
220
+ // eslint-disable-next-line no-console
221
+ console.warn(`[plugin-tween] binding miss: ${s.target}`);
222
+ continue;
223
+ }
224
+ const from = (_a = s.from) !== null && _a !== void 0 ? _a : binding.read();
225
+ this.running.push({
226
+ binding,
227
+ from,
228
+ to: s.to,
229
+ duration: Math.max(1, s.duration),
230
+ easing: (_c = Easing[((_b = s.easing) !== null && _b !== void 0 ? _b : 'linear')]) !== null && _c !== void 0 ? _c : Easing.linear,
231
+ delay: (_d = s.delay) !== null && _d !== void 0 ? _d : 0,
232
+ elapsed: 0,
233
+ done: false,
234
+ });
235
+ }
236
+ }
237
+ update(e) {
238
+ if (!this.isPlaying || this.running.length === 0)
239
+ return;
240
+ const dt = e.deltaTime;
241
+ if (this.parallel) {
242
+ this.advanceParallel(dt);
243
+ }
244
+ else {
245
+ this.advanceSequence(dt);
246
+ }
247
+ }
248
+ advanceParallel(dt) {
249
+ let allDone = true;
250
+ for (const r of this.running) {
251
+ if (r.done)
252
+ continue;
253
+ this.tickStep(r, dt);
254
+ if (!r.done)
255
+ allDone = false;
256
+ }
257
+ if (allDone)
258
+ this.onCycleEnd();
259
+ }
260
+ advanceSequence(dt) {
261
+ while (dt > 0 && this.cursor < this.running.length) {
262
+ const r = this.running[this.cursor];
263
+ if (r.done) {
264
+ this.cursor++;
265
+ continue;
266
+ }
267
+ const before = r.elapsed;
268
+ this.tickStep(r, dt);
269
+ const used = r.elapsed - before;
270
+ dt -= used;
271
+ if (!r.done)
272
+ break;
273
+ this.cursor++;
274
+ }
275
+ if (this.cursor >= this.running.length)
276
+ this.onCycleEnd();
277
+ }
278
+ tickStep(r, dt) {
279
+ if (r.delay > 0) {
280
+ const used = Math.min(r.delay, dt);
281
+ r.delay -= used;
282
+ // 把延时算进 elapsed 不合适,留给 sequence 减 dt
283
+ // 这里直接消耗 dt 后返回
284
+ return;
285
+ }
286
+ r.elapsed += dt;
287
+ const t = Math.min(1, r.elapsed / r.duration);
288
+ const v = r.from + (r.to - r.from) * r.easing(t);
289
+ r.binding.write(v);
290
+ if (t >= 1) {
291
+ r.done = true;
292
+ }
293
+ }
294
+ onCycleEnd() {
295
+ this.playedLoops++;
296
+ const shouldLoop = this.loop === -1 || this.playedLoops <= this.loop;
297
+ if (this.yoyo) {
298
+ this.direction = (this.direction === 1 ? -1 : 1);
299
+ // yoyo:反向重置
300
+ for (const r of this.running) {
301
+ const a = r.from;
302
+ r.from = r.to;
303
+ r.to = a;
304
+ r.elapsed = 0;
305
+ r.done = false;
306
+ }
307
+ this.cursor = 0;
308
+ if (!shouldLoop && this.direction === 1) {
309
+ this.finish();
310
+ }
311
+ return;
312
+ }
313
+ if (shouldLoop) {
314
+ for (const r of this.running) {
315
+ r.elapsed = 0;
316
+ r.done = false;
317
+ }
318
+ this.cursor = 0;
319
+ return;
320
+ }
321
+ this.finish();
322
+ }
323
+ finish() {
324
+ this.isPlaying = false;
325
+ if (this.signal)
326
+ getSignalBus().emit(this.signal, { component: this });
327
+ getSignalBus().emit('tween:finish', { component: this });
328
+ }
329
+ };
330
+ Tween.componentName = 'Tween';
331
+ Tween = __decorate([
332
+ decorators.componentObserver({})
333
+ ], Tween);
334
+
335
+ /**
336
+ * Tween 没有跨实体编排,System 仅用于注册 plugin。
337
+ */
338
+ class TweenSystem extends System {
339
+ constructor() {
340
+ super(...arguments);
341
+ this.name = 'Tween';
342
+ }
343
+ }
344
+ TweenSystem.systemName = 'Tween';
345
+
346
+ export { Easing, Tween, TweenSystem };
package/index.js ADDED
@@ -0,0 +1,7 @@
1
+ 'use strict';
2
+
3
+ if (process.env.NODE_ENV === 'production') {
4
+ module.exports = require('./dist/plugin-tween.cjs.prod.js');
5
+ } else {
6
+ module.exports = require('./dist/plugin-tween.cjs.js');
7
+ }
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@eva/plugin-tween",
3
- "version": "2.1.0-beta.2",
3
+ "version": "2.1.0-beta.3",
4
4
  "description": "Godot Tween 等价物 — 任意字段补间(transform / 自定义 Component / mx.store),支持 sequence/parallel/yoyo/loop。",
5
- "main": "lib/index.ts",
6
- "module": "lib/index.ts",
7
- "types": "lib/index.ts",
5
+ "main": "index.js",
6
+ "module": "dist/plugin-tween.esm.js",
7
+ "types": "dist/plugin-tween.d.ts",
8
8
  "files": [
9
- "lib"
9
+ "index.js",
10
+ "dist"
10
11
  ],
11
12
  "keywords": [
12
13
  "eva.js",
@@ -16,7 +17,7 @@
16
17
  ],
17
18
  "license": "MIT",
18
19
  "dependencies": {
19
- "@eva/eva.js": "2.1.0-beta.2",
20
- "@eva/plugin-signal-bus": "2.1.0-beta.2"
20
+ "@eva/eva.js": "2.1.0-beta.3",
21
+ "@eva/plugin-signal-bus": "2.1.0-beta.3"
21
22
  }
22
23
  }
package/lib/Tween.ts DELETED
@@ -1,206 +0,0 @@
1
- import { Component, decorators } from '@eva/eva.js';
2
- import { getSignalBus } from '@eva/plugin-signal-bus';
3
- import { Easing } from './easing';
4
- import { bindPath, PathBinding } from './path';
5
- import type { TweenParams, TweenStep, EasingName } from './types';
6
-
7
- interface RunningStep {
8
- binding: PathBinding;
9
- from: number;
10
- to: number;
11
- duration: number;
12
- easing: (t: number) => number;
13
- delay: number;
14
- elapsed: number;
15
- done: boolean;
16
- }
17
-
18
- /**
19
- * Tween 组件 — Godot Tween 等价物。
20
- *
21
- * 与 plugin-transition 区别:
22
- * - 不限制 target 类型(transform/Component/store 都行)
23
- * - 内置 sequence 与 parallel 编排
24
- * - 完成 emit 命名信号
25
- *
26
- * DSL 用法:
27
- * ```json
28
- * {
29
- * "type": "Tween",
30
- * "props": {
31
- * "step": {
32
- * "target": "components.RocketAimer.currentAngleDeg",
33
- * "from": -60, "to": 60, "duration": 1500, "easing": "easeInOutQuad"
34
- * },
35
- * "yoyo": true, "loop": -1, "autostart": true
36
- * }
37
- * }
38
- * ```
39
- */
40
- @decorators.componentObserver({})
41
- export class Tween extends Component<TweenParams> {
42
- static componentName = 'Tween';
43
-
44
- private steps: TweenStep[] = [];
45
- private parallel = false;
46
- private yoyo = false;
47
- private loop = 0;
48
- private autostart = false;
49
- private signal?: string;
50
-
51
- private running: RunningStep[] = [];
52
- private isPlaying = false;
53
- private playedLoops = 0;
54
- private cursor = 0; // sequence 当前 step
55
- private direction: 1 | -1 = 1;
56
-
57
- init(params?: TweenParams) {
58
- if (!params) return;
59
- this.steps = params.step ? [params.step] : (params.steps ?? []);
60
- this.parallel = params.parallel ?? false;
61
- this.yoyo = params.yoyo ?? false;
62
- this.loop = params.loop ?? 0;
63
- this.autostart = params.autostart ?? false;
64
- this.signal = params.signal;
65
- }
66
-
67
- awake() {
68
- if (this.autostart) this.play();
69
- }
70
-
71
- /** 重新开始 */
72
- play() {
73
- this.buildRunning();
74
- this.isPlaying = true;
75
- this.playedLoops = 0;
76
- this.cursor = 0;
77
- this.direction = 1;
78
- }
79
-
80
- pause() {
81
- this.isPlaying = false;
82
- }
83
-
84
- resume() {
85
- if (this.running.length) this.isPlaying = true;
86
- }
87
-
88
- stop() {
89
- this.isPlaying = false;
90
- this.running = [];
91
- }
92
-
93
- private buildRunning() {
94
- this.running = [];
95
- for (const s of this.steps) {
96
- const binding = bindPath(this.gameObject, s.target);
97
- if (!binding) {
98
- // eslint-disable-next-line no-console
99
- console.warn(`[plugin-tween] binding miss: ${s.target}`);
100
- continue;
101
- }
102
- const from = s.from ?? binding.read();
103
- this.running.push({
104
- binding,
105
- from,
106
- to: s.to,
107
- duration: Math.max(1, s.duration),
108
- easing: Easing[(s.easing ?? 'linear') as EasingName] ?? Easing.linear,
109
- delay: s.delay ?? 0,
110
- elapsed: 0,
111
- done: false,
112
- });
113
- }
114
- }
115
-
116
- update(e: { deltaTime: number }) {
117
- if (!this.isPlaying || this.running.length === 0) return;
118
- const dt = e.deltaTime;
119
-
120
- if (this.parallel) {
121
- this.advanceParallel(dt);
122
- } else {
123
- this.advanceSequence(dt);
124
- }
125
- }
126
-
127
- private advanceParallel(dt: number) {
128
- let allDone = true;
129
- for (const r of this.running) {
130
- if (r.done) continue;
131
- this.tickStep(r, dt);
132
- if (!r.done) allDone = false;
133
- }
134
- if (allDone) this.onCycleEnd();
135
- }
136
-
137
- private advanceSequence(dt: number) {
138
- while (dt > 0 && this.cursor < this.running.length) {
139
- const r = this.running[this.cursor];
140
- if (r.done) {
141
- this.cursor++;
142
- continue;
143
- }
144
- const before = r.elapsed;
145
- this.tickStep(r, dt);
146
- const used = r.elapsed - before;
147
- dt -= used;
148
- if (!r.done) break;
149
- this.cursor++;
150
- }
151
- if (this.cursor >= this.running.length) this.onCycleEnd();
152
- }
153
-
154
- private tickStep(r: RunningStep, dt: number) {
155
- if (r.delay > 0) {
156
- const used = Math.min(r.delay, dt);
157
- r.delay -= used;
158
- // 把延时算进 elapsed 不合适,留给 sequence 减 dt
159
- // 这里直接消耗 dt 后返回
160
- return;
161
- }
162
- r.elapsed += dt;
163
- const t = Math.min(1, r.elapsed / r.duration);
164
- const v = r.from + (r.to - r.from) * r.easing(t);
165
- r.binding.write(v);
166
- if (t >= 1) {
167
- r.done = true;
168
- }
169
- }
170
-
171
- private onCycleEnd() {
172
- this.playedLoops++;
173
- const shouldLoop = this.loop === -1 || this.playedLoops <= this.loop;
174
- if (this.yoyo) {
175
- this.direction = (this.direction === 1 ? -1 : 1) as 1 | -1;
176
- // yoyo:反向重置
177
- for (const r of this.running) {
178
- const a = r.from;
179
- r.from = r.to;
180
- r.to = a;
181
- r.elapsed = 0;
182
- r.done = false;
183
- }
184
- this.cursor = 0;
185
- if (!shouldLoop && this.direction === 1) {
186
- this.finish();
187
- }
188
- return;
189
- }
190
- if (shouldLoop) {
191
- for (const r of this.running) {
192
- r.elapsed = 0;
193
- r.done = false;
194
- }
195
- this.cursor = 0;
196
- return;
197
- }
198
- this.finish();
199
- }
200
-
201
- private finish() {
202
- this.isPlaying = false;
203
- if (this.signal) getSignalBus().emit(this.signal, { component: this });
204
- getSignalBus().emit('tween:finish', { component: this });
205
- }
206
- }
@@ -1,9 +0,0 @@
1
- import { System } from '@eva/eva.js';
2
-
3
- /**
4
- * Tween 没有跨实体编排,System 仅用于注册 plugin。
5
- */
6
- export class TweenSystem extends System {
7
- static systemName = 'Tween';
8
- readonly name = 'Tween';
9
- }
package/lib/easing.ts DELETED
@@ -1,48 +0,0 @@
1
- import type { EasingName } from './types';
2
-
3
- const PI = Math.PI;
4
- const c1 = 1.70158;
5
- const c2 = c1 * 1.525;
6
- const c3 = c1 + 1;
7
- const c4 = (2 * PI) / 3;
8
- const c5 = (2 * PI) / 4.5;
9
- const n1 = 7.5625;
10
- const d1 = 2.75;
11
-
12
- function easeInBounce(x: number): number {
13
- return 1 - easeOutBounce(1 - x);
14
- }
15
- function easeOutBounce(x: number): number {
16
- if (x < 1 / d1) return n1 * x * x;
17
- if (x < 2 / d1) return n1 * (x -= 1.5 / d1) * x + 0.75;
18
- if (x < 2.5 / d1) return n1 * (x -= 2.25 / d1) * x + 0.9375;
19
- return n1 * (x -= 2.625 / d1) * x + 0.984375;
20
- }
21
-
22
- export const Easing: Record<EasingName, (t: number) => number> = {
23
- linear: (t) => t,
24
- easeInQuad: (t) => t * t,
25
- easeOutQuad: (t) => 1 - (1 - t) * (1 - t),
26
- easeInOutQuad: (t) => (t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2),
27
- easeInCubic: (t) => t * t * t,
28
- easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
29
- easeInOutCubic: (t) => (t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2),
30
- easeInElastic: (t) => (t === 0 ? 0 : t === 1 ? 1 : -Math.pow(2, 10 * t - 10) * Math.sin((t * 10 - 10.75) * c4)),
31
- easeOutElastic: (t) => (t === 0 ? 0 : t === 1 ? 1 : Math.pow(2, -10 * t) * Math.sin((t * 10 - 0.75) * c4) + 1),
32
- easeInOutElastic: (t) => {
33
- if (t === 0) return 0;
34
- if (t === 1) return 1;
35
- return t < 0.5
36
- ? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * c5)) / 2
37
- : (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * c5)) / 2 + 1;
38
- },
39
- easeInBack: (t) => c3 * t * t * t - c1 * t * t,
40
- easeOutBack: (t) => 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2),
41
- easeInOutBack: (t) =>
42
- t < 0.5
43
- ? (Math.pow(2 * t, 2) * ((c2 + 1) * 2 * t - c2)) / 2
44
- : (Math.pow(2 * t - 2, 2) * ((c2 + 1) * (t * 2 - 2) + c2) + 2) / 2,
45
- easeInBounce,
46
- easeOutBounce,
47
- easeInOutBounce: (t) => (t < 0.5 ? (1 - easeOutBounce(1 - 2 * t)) / 2 : (1 + easeOutBounce(2 * t - 1)) / 2),
48
- };
package/lib/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export { Tween } from './Tween';
2
- export { TweenSystem } from './TweenSystem';
3
- export { Easing } from './easing';
4
- export type { TweenParams, TweenStep, EasingName } from './types';
package/lib/path.ts DELETED
@@ -1,75 +0,0 @@
1
- import type { Component, GameObject } from '@eva/eva.js';
2
-
3
- /**
4
- * 把字符串路径解析成 (target, key) 二元组,以便就地写入。
5
- *
6
- * 支持:
7
- * - "transform.position.x"
8
- * - "transform.rotation"
9
- * - "components.<ComponentName>.<key>" // 通过 componentName 找
10
- * - "store.<keyPath>" // 走 mx.store(若存在)
11
- */
12
- export interface PathBinding {
13
- read(): number;
14
- write(v: number): void;
15
- }
16
-
17
- export function bindPath(go: GameObject, path: string): PathBinding | null {
18
- const parts = path.split('.');
19
- if (parts[0] === 'transform') {
20
- const obj = go.transform as any;
21
- return diveIntoObject(obj, parts.slice(1));
22
- }
23
- if (parts[0] === 'components' && parts.length >= 3) {
24
- const compName = parts[1];
25
- const comp = findComponent(go, compName);
26
- if (!comp) return null;
27
- return diveIntoObject(comp as any, parts.slice(2));
28
- }
29
- if (parts[0] === 'store' && parts.length >= 2) {
30
- return bindStore(parts.slice(1).join('.'));
31
- }
32
- return null;
33
- }
34
-
35
- function diveIntoObject(root: any, keys: string[]): PathBinding | null {
36
- if (!root || keys.length === 0) return null;
37
- let parent = root;
38
- for (let i = 0; i < keys.length - 1; i++) {
39
- const k = keys[i];
40
- if (parent[k] == null) return null;
41
- parent = parent[k];
42
- }
43
- const last = keys[keys.length - 1];
44
- return {
45
- read: () => Number(parent[last] ?? 0),
46
- write: (v: number) => {
47
- parent[last] = v;
48
- },
49
- };
50
- }
51
-
52
- function findComponent(go: GameObject, name: string): Component | null {
53
- const comps: any[] = (go as any).components || [];
54
- for (const c of comps) {
55
- const cn = c?.constructor?.componentName;
56
- if (cn === name) return c;
57
- }
58
- // 退路:Eva.js 部分版本支持 getComponent(name)
59
- if (typeof (go as any).getComponent === 'function') {
60
- return ((go as any).getComponent(name) as Component) ?? null;
61
- }
62
- return null;
63
- }
64
-
65
- function bindStore(key: string): PathBinding | null {
66
- // mx.store 是宿主全局,不强依赖
67
- const mx: any = (globalThis as any).mx;
68
- if (!mx?.store) return null;
69
- return {
70
- read: () => Number(mx.store.get?.(key) ?? 0),
71
- write: (v: number) => {
72
- mx.store.update?.({ [key]: v });
73
- },
74
- };
75
- }
package/lib/types.ts DELETED
@@ -1,37 +0,0 @@
1
- export type EasingName =
2
- | 'linear'
3
- | 'easeInQuad' | 'easeOutQuad' | 'easeInOutQuad'
4
- | 'easeInCubic' | 'easeOutCubic' | 'easeInOutCubic'
5
- | 'easeInElastic' | 'easeOutElastic' | 'easeInOutElastic'
6
- | 'easeInBack' | 'easeOutBack' | 'easeInOutBack'
7
- | 'easeInBounce' | 'easeOutBounce' | 'easeInOutBounce';
8
-
9
- export interface TweenStep {
10
- /** 目标路径,例如:
11
- * - "transform.position.x"
12
- * - "transform.rotation"
13
- * - "components.RocketAimer.currentAngleDeg"
14
- * - "store.score" (走 mx.store)
15
- */
16
- target: string;
17
- from?: number;
18
- to: number;
19
- duration: number;
20
- easing?: EasingName;
21
- delay?: number;
22
- }
23
-
24
- export interface TweenParams {
25
- /** 单步 = 一次性 to;steps = 多步序列 */
26
- step?: TweenStep;
27
- steps?: TweenStep[];
28
- /** 是否并行执行 steps(默认 sequence) */
29
- parallel?: boolean;
30
- yoyo?: boolean;
31
- /** -1 = 无限循环 */
32
- loop?: number;
33
- /** 是否自动开始 */
34
- autostart?: boolean;
35
- /** 完成后 emit 的信号 */
36
- signal?: string;
37
- }