@zcomponent/core 1.12.0 → 1.14.0-beta

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.
@@ -51,12 +51,12 @@ export declare class Animation {
51
51
  tick(): void;
52
52
  registerEntityPath(path: string, entity: any, pathParts: (string | number)[]): void;
53
53
  evaluateTouchedPaths(paths: string[], t?: number): void;
54
+ private _evaluateLayer;
54
55
  get spotlightLayer(): Layer | undefined;
55
56
  set spotlightLayer(l: Layer | undefined);
56
57
  clearCachedDefault(entityID: string, prop: string): void;
57
58
  private _emitPendingEvents;
58
59
  private _resolvePath;
59
- private _evaluatePath;
60
60
  start(): void;
61
61
  }
62
62
  export interface EntityPath {
@@ -1,5 +1,4 @@
1
1
  import { Observable } from '../observable';
2
- import { clone } from '../data';
3
2
  import { Event } from '../event';
4
3
  /** @internal */
5
4
  export var AnimationEvents;
@@ -109,11 +108,58 @@ export class Animation {
109
108
  return;
110
109
  t = t ?? this.timeSource?.() ?? performance.now();
111
110
  const unique = new Set([...paths]);
112
- for (const entry of unique.values()) {
113
- this._evaluatePath(t, entry);
111
+ const uniqueAsArray = unique.values();
112
+ const resolvedPaths = [...uniqueAsArray].map(entry => this._resolvePath(entry));
113
+ for (const entry of resolvedPaths) {
114
+ if (!entry)
115
+ continue;
116
+ let byEntity = this._defaultValues.get(entry[0]);
117
+ if (!byEntity) {
118
+ byEntity = new Map();
119
+ this._defaultValues.set(entry[0], byEntity);
120
+ }
121
+ let observable = byEntity.get(entry[1][0]);
122
+ if (!observable) {
123
+ let val = entry[0][entry[1][0]];
124
+ if (val instanceof Observable)
125
+ val = val.value;
126
+ observable = new Observable(val);
127
+ byEntity.set(entry[1][0], observable);
128
+ }
129
+ entry[2] = observable;
130
+ }
131
+ // Reset our working values to default
132
+ for (const p of resolvedPaths) {
133
+ if (p?.[2])
134
+ p[2].value = undefined;
135
+ }
136
+ if (this._spotlightLayer)
137
+ this._evaluateLayer(t, this._spotlightLayer, resolvedPaths);
138
+ else {
139
+ for (const layer of this._layers)
140
+ this._evaluateLayer(t, layer, resolvedPaths);
141
+ }
142
+ for (const entry of resolvedPaths) {
143
+ if (!entry?.[2])
144
+ continue;
145
+ if (entry[0][entry[1][0]] instanceof Observable)
146
+ entry[0][entry[1][0]].value = entry[2].value;
147
+ else
148
+ entry[0][entry[1][0]] = entry[2].value;
114
149
  }
115
150
  this._emitPendingEvents();
116
151
  }
152
+ _evaluateLayer(t, layer, paths) {
153
+ for (const p of paths) {
154
+ if (!p?.[2])
155
+ continue;
156
+ if (!layer.influencedPaths.has(p[3]))
157
+ continue;
158
+ let val = getResolved(p[2], p[1]);
159
+ val = layer.computePathProperty(t, p[3], val);
160
+ setResolved(p[2], p[1], val);
161
+ }
162
+ }
117
163
  get spotlightLayer() {
118
164
  return this._spotlightLayer;
119
165
  }
@@ -153,38 +199,7 @@ export class Animation {
153
199
  const path = this._entityPaths.get(p);
154
200
  if (!path || path.pathParts.length === 0)
155
201
  return;
156
- let parent = path.entity;
157
- let currentKey = path.pathParts[0];
158
- if (parent[currentKey] instanceof Observable) {
159
- parent = parent[currentKey];
160
- currentKey = 'value';
161
- }
162
- for (let i = 1; i < path.pathParts.length; i++) {
163
- parent = parent[currentKey];
164
- currentKey = path.pathParts[i];
165
- }
166
- return [parent, currentKey];
167
- }
168
- _evaluatePath(t, p) {
169
- const resolved = this._resolvePath(p);
170
- if (!resolved)
171
- return;
172
- if (!this._defaultValues.has(p)) {
173
- this._defaultValues.set(p, clone(resolved[0][resolved[1]]));
174
- }
175
- let val = this._defaultValues.get(p);
176
- if (this._spotlightLayer) {
177
- if (this._spotlightLayer.influencedPaths.has(p))
178
- val = this._spotlightLayer.computePathProperty(t, p, val);
179
- }
180
- else {
181
- for (const layer of this._layers) {
182
- if (!layer.influencedPaths.has(p))
183
- continue;
184
- val = layer.computePathProperty(t, p, val);
185
- }
186
- }
187
- resolved[0][resolved[1]] = val;
202
+ return [path.entity, path.pathParts, undefined, p];
188
203
  }
189
204
  start() {
190
205
  for (const layer of this._layers) {
@@ -195,3 +210,18 @@ export class Animation {
195
210
  }
196
211
  }
197
212
  }
213
+ function getResolved(o, pathParts) {
214
+ let current = o.value;
215
+ for (let i = 1; i < pathParts.length; i++)
216
+ current = current[pathParts[i]];
217
+ return current;
218
+ }
219
+ function setResolved(o, pathParts, val) {
220
+ let target = o;
221
+ let key = 'value';
222
+ for (let i = 1; i < pathParts.length; i++) {
223
+ target = target[key];
224
+ key = pathParts[i];
225
+ }
226
+ target[key] = val;
227
+ }
@@ -33,5 +33,6 @@ export declare class Clip {
33
33
  resolveClipTime(t: number, t0: number, s0: number, rate: number, t1?: number): number;
34
34
  streamPause(isActive: boolean, active: LayerClip | null | undefined): void;
35
35
  streamSeek(t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined): void;
36
- streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, parentWeight?: number): void;
36
+ streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, stalled?: boolean, parentWeight?: number): void;
37
+ isStalled(): boolean;
37
38
  }
@@ -126,9 +126,16 @@ export class Clip {
126
126
  track.streamSeek(t, rate, isActive, active);
127
127
  }
128
128
  }
129
- streamTick(clipState, t, rate, isActive, active, force = false, parentWeight) {
129
+ streamTick(clipState, t, rate, isActive, active, force = false, stalled = false, parentWeight) {
130
130
  for (const track of this._streamTracks) {
131
- track.streamTick(clipState, t, rate, isActive, active, force, parentWeight);
131
+ track.streamTick(clipState, t, rate, isActive, active, force, stalled, parentWeight);
132
132
  }
133
133
  }
134
+ isStalled() {
135
+ for (const track of this._tracks) {
136
+ if (track.isStalled())
137
+ return true;
138
+ }
139
+ return false;
140
+ }
134
141
  }
@@ -45,7 +45,7 @@ export class Layer {
45
45
  this._queue.splice(i, 1);
46
46
  }
47
47
  if (entry.layerClip && entry === this._active) {
48
- if (next && (entry.layerClip.state === StreamState.Ended || next.fadeTime >= entry.layerClip.getRemainingTimeEstimate())) {
48
+ if (next && (entry.layerClip.state.value === StreamState.Ended || next.fadeTime >= entry.layerClip.getRemainingTimeEstimate())) {
49
49
  next.startTime = t;
50
50
  this._active = next;
51
51
  this.animation._pendingEvents.push({ evt: AnimationEvents.onLayerClipActive, args: [this._active.layerClip] });
@@ -1,4 +1,6 @@
1
1
  import { FadeParameters } from '../data';
2
+ import { Event } from '../event';
3
+ import { Observable } from '../observable';
2
4
  import { Animation } from './animation';
3
5
  import { AnimationState } from './animationstate';
4
6
  import { Clip } from './clips/clip';
@@ -16,6 +18,7 @@ export declare class LayerClip implements Stream {
16
18
  readonly animation: Animation;
17
19
  private _t0;
18
20
  private _pauseTime;
21
+ private _stallStartTime;
19
22
  private _t1;
20
23
  private _rate;
21
24
  private _loop;
@@ -23,17 +26,26 @@ export declare class LayerClip implements Stream {
23
26
  private _lastClipTime;
24
27
  private _lastLoop;
25
28
  private _stopped;
26
- state: StreamState;
29
+ private _state;
30
+ state: Observable<StreamState, never>;
31
+ playing: Observable<boolean, never>;
32
+ stalled: Observable<boolean, never>;
33
+ onPlaying: Event<[LayerClip]>;
34
+ onPaused: Event<[LayerClip]>;
35
+ onEnded: Event<[LayerClip]>;
36
+ onLoop: Event<[LayerClip]>;
27
37
  constructor(layer: Layer, clip: Clip, scriptName?: string | undefined, id?: string | undefined);
28
38
  private _clipDirty;
29
39
  tick(touchedPaths: string[], force?: boolean): void;
30
40
  computePathProperty(p: string, valueBefore: any): any;
31
41
  getRemainingTimeEstimate(): number;
32
42
  private _getTime;
43
+ private _stateUpdate;
33
44
  queue(opts?: LayerClipPlayOptions): void;
34
45
  length(): number;
35
46
  play(opts?: LayerClipPlayOptions): void;
36
47
  pause(): void;
48
+ private _cancelStall;
37
49
  seek(ct: number, opts?: LayerClipSeekOptions): void;
38
50
  /** @internal */
39
51
  _serialize(state: AnimationState): void;
@@ -41,6 +53,7 @@ export declare class LayerClip implements Stream {
41
53
  _restore(state: AnimationState): void;
42
54
  get clipTime(): number;
43
55
  stop(): void;
56
+ isStalled(): boolean;
44
57
  }
45
58
  export interface LayerClipSeekOptions {
46
59
  fade?: FadeParameters | null;
@@ -1,3 +1,5 @@
1
+ import { Event } from '../event';
2
+ import { Observable } from '../observable';
1
3
  import { AnimationEvents } from './animation';
2
4
  import { StreamState } from './stream';
3
5
  import { resolveClipTimeLoops } from './tracks/track';
@@ -16,52 +18,88 @@ export class LayerClip {
16
18
  this._rate = 1;
17
19
  this._loop = false;
18
20
  this._stopped = false;
19
- this.state = StreamState.Paused;
21
+ this._state = StreamState.Paused;
22
+ this.state = new Observable(this._state);
23
+ this.playing = new Observable(false);
24
+ this.stalled = new Observable(false);
25
+ this.onPlaying = new Event();
26
+ this.onPaused = new Event();
27
+ this.onEnded = new Event();
28
+ this.onLoop = new Event();
20
29
  this._clipDirty = (v) => {
21
30
  if (v)
22
31
  this._lastClipTime = undefined;
23
32
  };
33
+ this._stateUpdate = (s) => {
34
+ this.playing.value = s === StreamState.Playing;
35
+ switch (s) {
36
+ case StreamState.Ended:
37
+ this.onEnded.emit(this);
38
+ break;
39
+ case StreamState.Playing:
40
+ this.onPlaying.emit(this);
41
+ break;
42
+ case StreamState.Paused:
43
+ this.onPaused.emit(this);
44
+ break;
45
+ }
46
+ };
24
47
  this.animation = layer.animation;
25
48
  this._t1 = clip.length;
26
49
  layer._registerLayerClip(this);
27
50
  this.clip.influencedPathsDirty.addListener(this._clipDirty);
28
51
  if (id)
29
52
  layer.animation.layerClipByID.set(id, this);
53
+ this.state.addListener(this._stateUpdate);
30
54
  }
31
55
  tick(touchedPaths, force) {
32
56
  const t = this._getTime();
33
- const t0 = this._t0 + (t - (this._pauseTime ?? t));
34
- const t1 = this._t1 + (t - (this._pauseTime ?? t));
57
+ if (this._stallStartTime !== undefined) {
58
+ if (!this.clip.isStalled())
59
+ this._cancelStall();
60
+ }
61
+ else if (this._state === StreamState.Playing) {
62
+ if (this.clip.isStalled()) {
63
+ this._stallStartTime = t;
64
+ this.stalled.value = true;
65
+ }
66
+ }
67
+ const t0 = this._t0 + (t - (this._pauseTime ?? t)) + (t - (this._stallStartTime ?? t));
68
+ const t1 = this._t1 + (t - (this._pauseTime ?? t)) + (t - (this._stallStartTime ?? t));
35
69
  const ct = this.clip.resolveClipTime(t, t0, this._rate >= 0 ? 0 : this.clip.length, this._rate, t1);
36
70
  this._lastTickTime = ct;
37
71
  if (ct !== this._lastClipTime || force) {
38
72
  touchedPaths.push(...this.clip.influencedPaths);
39
73
  }
40
- if (this.state === StreamState.Playing && t >= this._t1) {
74
+ if (this._state === StreamState.Playing && t >= this._t1) {
41
75
  this.pause();
42
- this.state = StreamState.Ended;
76
+ this._state = StreamState.Ended;
77
+ this.state.value = this._state;
43
78
  }
44
79
  else {
45
80
  const [bt, loop] = resolveClipTimeLoops(t, t0, this._rate >= 0 ? 0 : this.clip.length, this._rate, this.clip.length, this._rate < 0, t1);
46
- this.clip.streamTick(this.state, bt, this._rate, this.layer.active === this, this.layer.active, loop !== this._lastLoop, 1);
81
+ this.clip.streamTick(this._state, bt, this._rate, this.layer.active === this, this.layer.active, loop !== this._lastLoop, this._stallStartTime !== undefined, 1);
82
+ const isNewLoop = this._lastLoop !== undefined && this._lastLoop !== loop;
47
83
  this._lastLoop = loop;
84
+ if (isNewLoop)
85
+ this.onLoop.emit(this);
48
86
  }
49
87
  }
50
88
  computePathProperty(p, valueBefore) {
51
89
  const t = this._getTime();
52
- const t0 = this._t0 + (t - (this._pauseTime ?? t));
53
- const t1 = this._t1 + (t - (this._pauseTime ?? t));
90
+ const t0 = this._t0 + (t - (this._pauseTime ?? t)) + (t - (this._stallStartTime ?? t));
91
+ const t1 = this._t1 + (t - (this._pauseTime ?? t)) + (t - (this._stallStartTime ?? t));
54
92
  const ct = this.clip.resolveClipTime(t, t0, this._rate >= 0 ? 0 : this.clip.length, this._rate, t1);
55
93
  this._lastClipTime = ct;
56
94
  return this.clip.computePathProperty(ct ?? 0, p, valueBefore);
57
95
  }
58
96
  getRemainingTimeEstimate() {
59
97
  const t = this._getTime();
60
- const t1 = this._t1 + (t - (this._pauseTime ?? t));
98
+ const t1 = this._t1 + (t - (this._pauseTime ?? t)) + (t - (this._stallStartTime ?? t));
61
99
  return t1 - t;
62
100
  }
63
101
  _getTime() {
64
- return this.timeSource?.() ?? this.animation.timeSource?.() ?? performance.now();
102
+ return this.timeSource?.() ?? this.animation?.timeSource?.() ?? performance.now();
65
103
  }
66
104
  queue(opts) {
67
105
  this.layer.queue(this, opts);
@@ -77,7 +115,7 @@ export class LayerClip {
77
115
  this._stopped = false;
78
116
  this._loop = opts?.loop ?? this.defaultLoop;
79
117
  const t = this._getTime();
80
- if (this.state === StreamState.Ended) {
118
+ if (this._state === StreamState.Ended) {
81
119
  this._t0 = t;
82
120
  }
83
121
  else if (this._pauseTime !== undefined) {
@@ -100,7 +138,7 @@ export class LayerClip {
100
138
  }
101
139
  const t0 = this._t0 + (t - (this._pauseTime ?? t));
102
140
  const t1 = this._t1 + (t - (this._pauseTime ?? t));
103
- this.state = StreamState.Playing;
141
+ this._state = StreamState.Playing;
104
142
  this.animation._pendingEvents.push({ evt: AnimationEvents.onLayerClipState, args: [this] });
105
143
  this._lastClipTime = undefined;
106
144
  this._lastTickTime = undefined;
@@ -108,18 +146,34 @@ export class LayerClip {
108
146
  this.layer._activateLayerClip(this, opts);
109
147
  this.layer._evaulate();
110
148
  }
149
+ if (this.clip.isStalled()) {
150
+ this._stallStartTime = t;
151
+ this.stalled.value = true;
152
+ }
111
153
  const [bt, loop] = resolveClipTimeLoops(t, t0, this._rate >= 0 ? 0 : this.clip.length, this._rate, this.clip.length, this._rate < 0, t1);
112
- this.clip.streamTick(this.state, bt, this._rate, this.layer.active === this, this.layer.active, loop !== this._lastLoop, 1);
154
+ this.clip.streamTick(this._state, bt, this._rate, this.layer.active === this, this.layer.active, loop !== this._lastLoop, this._stallStartTime !== undefined, 1);
113
155
  this._lastLoop = loop;
156
+ this.state.value = this._state;
114
157
  }
115
158
  pause() {
116
159
  delete this._lastLoop;
117
- if (this._pauseTime !== undefined || this.state !== StreamState.Playing)
160
+ if (this._pauseTime !== undefined || this._state !== StreamState.Playing)
118
161
  return;
119
- this.state = StreamState.Paused;
162
+ this._state = StreamState.Paused;
120
163
  this.animation._pendingEvents.push({ evt: AnimationEvents.onLayerClipState, args: [this] });
121
164
  this._pauseTime = this._getTime();
165
+ this._cancelStall();
122
166
  this.clip.streamPause(this.layer.active === this, this.layer.active);
167
+ this.state.value = this._state;
168
+ }
169
+ _cancelStall() {
170
+ if (this._stallStartTime === undefined)
171
+ return;
172
+ const t = this._getTime();
173
+ this._t0 += t - this._stallStartTime;
174
+ this._t1 += t - this._stallStartTime;
175
+ delete this._stallStartTime;
176
+ this.stalled.value = false;
123
177
  }
124
178
  seek(ct, opts) {
125
179
  const t = this._getTime();
@@ -143,6 +197,7 @@ export class LayerClip {
143
197
  this.layer._activateLayerClip(this, { fade: opts?.fade });
144
198
  }
145
199
  delete this._lastLoop;
200
+ delete this._stallStartTime;
146
201
  this.clip.streamSeek(ct, this._rate, this.layer.active === this, this.layer.active);
147
202
  this.layer._evaulate();
148
203
  }
@@ -161,7 +216,7 @@ export class LayerClip {
161
216
  rate: this._rate,
162
217
  loop: this._loop,
163
218
  stopped: this._stopped,
164
- state: this.state,
219
+ state: this._state,
165
220
  };
166
221
  state.byLayerClip[this.id] = ret;
167
222
  }
@@ -179,7 +234,8 @@ export class LayerClip {
179
234
  this._rate = entry.rate;
180
235
  this._loop = entry.loop;
181
236
  this._stopped = entry.stopped;
182
- this.state = entry.state;
237
+ this._state = entry.state;
238
+ this.state.value = this._state;
183
239
  }
184
240
  get clipTime() {
185
241
  return this._lastTickTime !== undefined ? this._lastTickTime : this.defaultPlaySpeed > 0 ? 0 : this.clip.length;
@@ -190,9 +246,14 @@ export class LayerClip {
190
246
  this._lastClipTime = undefined;
191
247
  this._lastTickTime = undefined;
192
248
  delete this._lastLoop;
249
+ delete this._stallStartTime;
193
250
  this._rate = 1;
194
251
  this.clip.streamTick(StreamState.Ended, this._t0, 1, this.layer.active === this, this.layer.active, true);
195
- this.state = StreamState.Ended;
252
+ this._state = StreamState.Ended;
196
253
  this.animation._pendingEvents.push({ evt: AnimationEvents.onLayerClipState, args: [this] });
254
+ this.state.value = this._state;
255
+ }
256
+ isStalled() {
257
+ return this._stallStartTime !== undefined;
197
258
  }
198
259
  }
@@ -9,6 +9,7 @@ export interface Stream {
9
9
  seek: (t: number) => void;
10
10
  stop: () => void;
11
11
  length?: () => number | undefined;
12
+ isStalled?: () => boolean;
12
13
  }
13
14
  export interface PlayOptions {
14
15
  speed?: number;
@@ -29,6 +29,7 @@ export declare class ClipTrack extends Track {
29
29
  computePathProperty(t: number, p: string, valueBefore: any, parentWeight?: number): any;
30
30
  streamPause(isActive: boolean, active: LayerClip | null | undefined): void;
31
31
  streamSeek(t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined): void;
32
- streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, parentWeight?: number): void;
32
+ streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, stalled?: boolean, parentWeight?: number): void;
33
33
  private _resolveBlock;
34
+ isStalled(): boolean;
34
35
  }
@@ -79,7 +79,7 @@ export class ClipTrack extends Track {
79
79
  return;
80
80
  }
81
81
  }
82
- streamTick(clipState, t, rate, isActive, active, force = false, parentWeight) {
82
+ streamTick(clipState, t, rate, isActive, active, force = false, stalled = false, parentWeight) {
83
83
  if (force)
84
84
  delete this._lastLoopNumber;
85
85
  const block = this._resolveBlock(t, rate);
@@ -94,7 +94,7 @@ export class ClipTrack extends Track {
94
94
  this.clip.streamPause(isActive, active);
95
95
  }
96
96
  else {
97
- this.clip.streamTick(clipState, bt, rate * block.rate, isActive, active, this._lastLoopNumber !== loopNumber || block !== lastBlock, parentWeight);
97
+ this.clip.streamTick(clipState, bt, rate * block.rate, isActive, active, this._lastLoopNumber !== loopNumber || block !== lastBlock, stalled, parentWeight);
98
98
  this._lastLoopNumber = loopNumber;
99
99
  }
100
100
  }
@@ -113,4 +113,7 @@ export class ClipTrack extends Track {
113
113
  }
114
114
  return undefined;
115
115
  }
116
+ isStalled() {
117
+ return this.clip.isStalled();
118
+ }
116
119
  }
@@ -25,7 +25,7 @@ export declare class FunctionTrack extends Track {
25
25
  }): void;
26
26
  get sortedBlocks(): ResolvedFunctionTrackEntity[];
27
27
  streamSeek(t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined): void;
28
- streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, parentWeight?: number): void;
28
+ streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, stalled?: boolean, parentWeight?: number): void;
29
29
  streamPause(isActive: boolean, active: LayerClip | null | undefined): void;
30
30
  private _resolveBlocksBetween;
31
31
  private _getBoundedT;
@@ -32,7 +32,7 @@ export class FunctionTrack extends Track {
32
32
  streamSeek(t, rate, isActive, active) {
33
33
  this._lastTick = t;
34
34
  }
35
- streamTick(clipState, t, rate, isActive, active, force = false, parentWeight) {
35
+ streamTick(clipState, t, rate, isActive, active, force = false, stalled = false, parentWeight) {
36
36
  // if (clipState !== StreamState.Playing || this._lastTick === undefined) {
37
37
  // this._lastTick = t;
38
38
  // return;
@@ -36,7 +36,8 @@ export declare class StreamTrack extends Track {
36
36
  streamSeek(t: number, resolvedRate: number, isActive: boolean, active: LayerClip | null | undefined): void;
37
37
  private _isPlayingInClip;
38
38
  private _isPlayingInLayerClip;
39
- streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, parentWeight?: number): void;
39
+ streamTick(clipState: StreamState, t: number, rate: number, isActive: boolean, active: LayerClip | null | undefined, force?: boolean, stalled?: boolean, parentWeight?: number): void;
40
40
  private _applyState;
41
41
  private _resolveBlock;
42
+ isStalled(): boolean;
42
43
  }
@@ -99,7 +99,7 @@ export class StreamTrack extends Track {
99
99
  return false;
100
100
  return this._isPlayingInClip(stream, layerClip.clip);
101
101
  }
102
- streamTick(clipState, t, rate, isActive, active, force = false, parentWeight) {
102
+ streamTick(clipState, t, rate, isActive, active, force = false, stalled = false, parentWeight) {
103
103
  if (!isActive && this._isPlayingInLayerClip(active)) {
104
104
  delete this._lastLoopNumber;
105
105
  return;
@@ -110,7 +110,7 @@ export class StreamTrack extends Track {
110
110
  let desiredState = clipState;
111
111
  const lastBlock = this._lastTick === undefined ? undefined : this._resolveBlock(this._lastTick, rate);
112
112
  this._lastTick = t;
113
- if (!block) {
113
+ if (!block || stalled) {
114
114
  desiredState = StreamState.Paused;
115
115
  }
116
116
  else {
@@ -158,4 +158,7 @@ export class StreamTrack extends Track {
158
158
  }
159
159
  return undefined;
160
160
  }
161
+ isStalled() {
162
+ return this.stream?.isStalled?.() ?? false;
163
+ }
161
164
  }
@@ -7,6 +7,7 @@ export declare abstract class Track {
7
7
  constructor(anim: Animation, id?: string | undefined);
8
8
  abstract get influencedPaths(): string[];
9
9
  abstract computePathProperty(t: number, p: string, valueBefore: any, parentWeight?: number): any;
10
+ isStalled(): boolean;
10
11
  }
11
12
  export declare function resolveKeyframeValue(t: number, before: Keyframe, after: Keyframe | undefined): any;
12
13
  export declare function resolveKeyframes(keyframes: Keyframe[], t: number): [before: Keyframe | undefined, after: Keyframe | undefined];
@@ -8,6 +8,9 @@ export class Track {
8
8
  if (id)
9
9
  anim.trackByID.set(id, this);
10
10
  }
11
+ isStalled() {
12
+ return false;
13
+ }
11
14
  }
12
15
  export function resolveKeyframeValue(t, before, after) {
13
16
  if (!after || !before.easing)
package/lib/inflate.js CHANGED
@@ -152,7 +152,17 @@ export function updateClipFromData(zcomp, clip, data) {
152
152
  clip.defaultFadeParameters = resolvedFadeParameters(data.defaultFadeParameters, zcomp.animation.curves);
153
153
  clip.length = data.length;
154
154
  clip.clearTracks();
155
- for (const track of Object.values(data.tracks)) {
155
+ const tracks = Object.values(data.tracks);
156
+ tracks.sort((atrack, btrack) => {
157
+ let a = atrack.order ?? 'A0';
158
+ let b = btrack.order ?? 'A0';
159
+ if (a === b) {
160
+ a = atrack.id;
161
+ b = btrack.id;
162
+ }
163
+ return a < b ? -1 : 1;
164
+ });
165
+ for (const track of Object.values(tracks)) {
156
166
  if (!track)
157
167
  continue;
158
168
  const instance = createTrackFromData(zcomp, clip.animation, track);
@@ -195,7 +205,17 @@ export function populateAnimationTracksFromData(zcomp, anim, data) {
195
205
  const clip = anim.clipByID.get(clipData.id);
196
206
  if (!clip)
197
207
  continue;
198
- for (const track of Object.values(clipData.tracks)) {
208
+ const tracks = Object.values(clipData.tracks);
209
+ tracks.sort((atrack, btrack) => {
210
+ let a = atrack.order ?? 'A0';
211
+ let b = btrack.order ?? 'A0';
212
+ if (a === b) {
213
+ a = atrack.id;
214
+ b = btrack.id;
215
+ }
216
+ return a < b ? -1 : 1;
217
+ });
218
+ for (const track of Object.values(tracks)) {
199
219
  if (!track)
200
220
  continue;
201
221
  const instance = createTrackFromData(zcomp, anim, track);
@@ -33,10 +33,10 @@ type Resolved<T, M> = [T] extends [never] ? M : T;
33
33
  * @typeParam TypeInternal - An internal type used to improve automatic type detection
34
34
  */
35
35
  export declare class Observable<Type = never, TypeInternal extends any[] | [] | never = never> extends Emitter<[Resolved<Type, TypeInternal>]> {
36
- private _default;
37
36
  private _deep;
38
37
  private _proxies;
39
38
  private _value;
39
+ private _default;
40
40
  /**
41
41
  * Constructs a new Observable
42
42
  *
package/lib/observable.js CHANGED
@@ -32,21 +32,12 @@ import { Emitter } from "./emitter";
32
32
  * @typeParam TypeInternal - An internal type used to improve automatic type detection
33
33
  */
34
34
  export class Observable extends Emitter {
35
- constructor(_default, withHandler, _deep = true, callWithImmediately = false) {
35
+ constructor(def, withHandler, _deep = true, callWithImmediately = false) {
36
36
  super();
37
- this._default = _default;
38
37
  this._deep = _deep;
39
38
  this._proxies = new WeakMap();
40
- let initial = this._default;
41
- if (Array.isArray(this._default) && this._deep) {
42
- initial = this._default.slice();
43
- this._default = this._default.slice();
44
- }
45
- else if (typeof this._default === 'object' && this._deep && this._default !== null && Object.getPrototypeOf(this._default) !== Object.prototype) {
46
- initial = { ...this._default };
47
- this._default = { ...this._default };
48
- }
49
- this._value = this._wrap(initial);
39
+ this._default = clone(def, _deep);
40
+ this._value = this._wrap(clone(def, _deep));
50
41
  if (withHandler)
51
42
  this.addListener(withHandler, 0, callWithImmediately);
52
43
  }
@@ -59,48 +50,43 @@ export class Observable extends Emitter {
59
50
  return this._value.proxy;
60
51
  }
61
52
  _wrap(v) {
62
- if (!Array.isArray(v) || this._deep === false) {
63
- if (typeof v !== 'object' ||
64
- v === null ||
65
- Object.getPrototypeOf(v) !== Object.prototype ||
66
- this._deep === false ||
67
- typeof v === 'function') {
68
- return { proxy: v, target: v };
69
- }
70
- }
53
+ if (!shouldWrap(v) || !this._deep)
54
+ return { proxy: v, target: v };
71
55
  const that = this;
72
56
  return {
73
57
  target: v,
74
58
  proxy: new Proxy(v, {
75
59
  get(target, p, receiver) {
76
60
  const val = target[p];
61
+ if (!shouldWrap(val))
62
+ return val;
77
63
  if (Array.isArray(target) && p === "splice") {
78
- return function (...args) { val.apply(target, args); that._emitValue(); };
79
- }
80
- if (!Array.isArray(val)) {
81
- if (typeof val !== 'object')
82
- return val;
83
- if (val === null)
84
- return val;
85
- if (Object.getPrototypeOf(val) !== Object.prototype)
86
- return val;
87
- if (typeof val === 'function')
88
- return val;
64
+ return function (...args) {
65
+ for (let i = 2; i < args.length; i++) {
66
+ args[i] = clone(args[i], true);
67
+ }
68
+ val.apply(target, args);
69
+ that._emitValue();
70
+ };
89
71
  }
90
72
  if (val['__z_is_observable'])
91
73
  return val;
92
74
  const proxy = that._proxies.get(val) || that._wrap(val);
93
75
  that._proxies.set(val, proxy);
94
- return proxy;
76
+ return proxy.proxy;
95
77
  },
96
78
  set(target, p, val) {
97
- target[p] = val;
98
- that._emitValue();
79
+ const changed = target[p] !== val;
80
+ target[p] = clone(val, true);
81
+ if (changed)
82
+ that._emitValue();
99
83
  return true;
100
84
  },
101
85
  deleteProperty(target, p) {
86
+ const changed = target[p] !== undefined;
102
87
  delete target[p];
103
- that._emitValue();
88
+ if (changed)
89
+ that._emitValue();
104
90
  return true;
105
91
  }
106
92
  })
@@ -108,13 +94,10 @@ export class Observable extends Emitter {
108
94
  }
109
95
  set value(v) {
110
96
  v = v ?? this._default;
111
- if (Array.isArray(this._value.target) && Array.isArray(v)) {
112
- this._value.target.splice(0, Infinity, ...v);
97
+ const changed = v !== this._value.proxy;
98
+ this._value = this._wrap(clone(v, this._deep));
99
+ if (changed)
113
100
  this._emitValue();
114
- return;
115
- }
116
- this._value = this._wrap(Array.isArray(v) ? [...v] : v);
117
- this._emitValue();
118
101
  }
119
102
  addListener(fn, priority = 0, callImmediately = true) {
120
103
  super.addListener(fn);
@@ -131,3 +114,48 @@ export class Observable extends Emitter {
131
114
  this._emit(this._value.proxy);
132
115
  }
133
116
  }
117
+ function clone(a, deep) {
118
+ if (!deep) {
119
+ if (Array.isArray(a))
120
+ a.slice();
121
+ if (typeof a === 'object' && a !== null) {
122
+ const proto = Object.getPrototypeOf(a);
123
+ if (proto === Object.prototype || proto === null) {
124
+ return { ...a };
125
+ }
126
+ }
127
+ return a;
128
+ }
129
+ if (Array.isArray(a)) {
130
+ const ret = new Array(a.length);
131
+ for (let i = 0; i < ret.length; i++) {
132
+ ret[i] = clone(a[i], true);
133
+ }
134
+ return ret;
135
+ }
136
+ if (typeof a === 'object' && a !== null) {
137
+ const proto = Object.getPrototypeOf(a);
138
+ if (proto === Object.prototype || proto === null) {
139
+ const ret = Object.create(null);
140
+ for (const entry of Object.getOwnPropertyNames(a)) {
141
+ ret[entry] = clone(a[entry], true);
142
+ }
143
+ return ret;
144
+ }
145
+ }
146
+ return a;
147
+ }
148
+ function shouldWrap(a) {
149
+ if (Array.isArray(a))
150
+ return true;
151
+ if (a === null)
152
+ return false;
153
+ if (typeof a === 'object' && a !== null) {
154
+ const proto = Object.getPrototypeOf(a);
155
+ if (proto === Object.prototype || proto === null)
156
+ return true;
157
+ }
158
+ if (typeof a === 'function')
159
+ return false;
160
+ return false;
161
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@zcomponent/core",
3
- "version": "1.12.0",
4
- "description": "",
3
+ "version": "1.14.0-beta",
4
+ "description": "The core component model and built-in functionality for Mattercraft.",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
7
7
  "engines": {