ngx-spine
Version:
[](https://travis-ci.org/PoiScript/ngx-spine)
1,327 lines • 135 kB
JavaScript
/**
* @fileoverview added by tsickle
* @suppress {checkTypes,constantProperty,extraRequire,missingOverride,missingReturn,unusedPrivateMembers,uselessCode} checked by tsc
*/
/******************************************************************************
* Spine Runtimes License Agreement
* Last updated May 1, 2019. Replaces all prior versions.
*
* Copyright (c) 2013-2019, Esoteric Software LLC
*
* Integration of the Spine Runtimes into software or otherwise creating
* derivative works of the Spine Runtimes is permitted under the terms and
* conditions of Section 2 of the Spine Editor License Agreement:
* http://esotericsoftware.com/spine-editor-license
*
* Otherwise, it is permitted to integrate the Spine Runtimes into software
* or otherwise create derivative works of the Spine Runtimes (collectively,
* "Products"), provided that each user of the Products must obtain their own
* Spine Editor license and redistribution of the Products in any form must
* include this license and copyright notice.
*
* THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS
* INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*****************************************************************************/
import { MathUtils, Utils, IntSet, Pool } from "./Utils";
import { Animation, MixBlend, AttachmentTimeline, MixDirection, DrawOrderTimeline, RotateTimeline, EventTimeline } from "./Animation";
export class AnimationState {
/**
* @param {?} data
*/
constructor(data) {
this.tracks = new Array();
this.events = new Array();
this.listeners = new Array();
this.queue = new EventQueue(this);
this.propertyIDs = new IntSet();
this.animationsChanged = false;
this.timeScale = 1;
this.trackEntryPool = new Pool((/**
* @return {?}
*/
() => new TrackEntry()));
this.data = data;
}
/**
* @param {?} delta
* @return {?}
*/
update(delta) {
delta *= this.timeScale;
/** @type {?} */
let tracks = this.tracks;
for (let i = 0, n = tracks.length; i < n; i++) {
/** @type {?} */
let current = tracks[i];
if (current == null)
continue;
current.animationLast = current.nextAnimationLast;
current.trackLast = current.nextTrackLast;
/** @type {?} */
let currentDelta = delta * current.timeScale;
if (current.delay > 0) {
current.delay -= currentDelta;
if (current.delay > 0)
continue;
currentDelta = -current.delay;
current.delay = 0;
}
/** @type {?} */
let next = current.next;
if (next != null) {
// When the next entry's delay is passed, change to the next entry, preserving leftover time.
/** @type {?} */
let nextTime = current.trackLast - next.delay;
if (nextTime >= 0) {
next.delay = 0;
next.trackTime +=
current.timeScale == 0
? 0
: (nextTime / current.timeScale + delta) * next.timeScale;
current.trackTime += currentDelta;
this.setCurrent(i, next, true);
while (next.mixingFrom != null) {
next.mixTime += delta;
next = next.mixingFrom;
}
continue;
}
}
else if (current.trackLast >= current.trackEnd &&
current.mixingFrom == null) {
tracks[i] = null;
this.queue.end(current);
this.disposeNext(current);
continue;
}
if (current.mixingFrom != null && this.updateMixingFrom(current, delta)) {
// End mixing from entries once all have completed.
/** @type {?} */
let from = current.mixingFrom;
current.mixingFrom = null;
if (from != null)
from.mixingTo = null;
while (from != null) {
this.queue.end(from);
from = from.mixingFrom;
}
}
current.trackTime += currentDelta;
}
this.queue.drain();
}
/**
* @param {?} to
* @param {?} delta
* @return {?}
*/
updateMixingFrom(to, delta) {
/** @type {?} */
let from = to.mixingFrom;
if (from == null)
return true;
/** @type {?} */
let finished = this.updateMixingFrom(from, delta);
from.animationLast = from.nextAnimationLast;
from.trackLast = from.nextTrackLast;
// Require mixTime > 0 to ensure the mixing from entry was applied at least once.
if (to.mixTime > 0 && to.mixTime >= to.mixDuration) {
// Require totalAlpha == 0 to ensure mixing is complete, unless mixDuration == 0 (the transition is a single frame).
if (from.totalAlpha == 0 || to.mixDuration == 0) {
to.mixingFrom = from.mixingFrom;
if (from.mixingFrom != null)
from.mixingFrom.mixingTo = to;
to.interruptAlpha = from.interruptAlpha;
this.queue.end(from);
}
return finished;
}
from.trackTime += delta * from.timeScale;
to.mixTime += delta;
return false;
}
/**
* @param {?} skeleton
* @return {?}
*/
apply(skeleton) {
if (skeleton == null)
throw new Error("skeleton cannot be null.");
if (this.animationsChanged)
this._animationsChanged();
/** @type {?} */
let events = this.events;
/** @type {?} */
let tracks = this.tracks;
/** @type {?} */
let applied = false;
for (let i = 0, n = tracks.length; i < n; i++) {
/** @type {?} */
let current = tracks[i];
if (current == null || current.delay > 0)
continue;
applied = true;
/** @type {?} */
let blend = i == 0 ? MixBlend.first : current.mixBlend;
// Apply mixing from entries first.
/** @type {?} */
let mix = current.alpha;
if (current.mixingFrom != null)
mix *= this.applyMixingFrom(current, skeleton, blend);
else if (current.trackTime >= current.trackEnd && current.next == null)
mix = 0;
// Apply current entry.
/** @type {?} */
let animationLast = current.animationLast;
/** @type {?} */
let animationTime = current.getAnimationTime();
/** @type {?} */
let timelineCount = current.animation.timelines.length;
/** @type {?} */
let timelines = current.animation.timelines;
if ((i == 0 && mix == 1) || blend == MixBlend.add) {
for (let ii = 0; ii < timelineCount; ii++) {
// Fixes issue #302 on IOS9 where mix, blend sometimes became undefined and caused assets
// to sometimes stop rendering when using color correction, as their RGBA values become NaN.
// (https://github.com/pixijs/pixi-spine/issues/302)
Utils.webkit602BugfixHelper(mix, blend);
timelines[ii].apply(skeleton, animationLast, animationTime, events, mix, blend, MixDirection.mixIn);
}
}
else {
/** @type {?} */
let timelineMode = current.timelineMode;
/** @type {?} */
let firstFrame = current.timelinesRotation.length == 0;
if (firstFrame)
Utils.setArraySize(current.timelinesRotation, timelineCount << 1, null);
/** @type {?} */
let timelinesRotation = current.timelinesRotation;
for (let ii = 0; ii < timelineCount; ii++) {
/** @type {?} */
let timeline = timelines[ii];
/** @type {?} */
let timelineBlend = (timelineMode[ii] & (AnimationState.NOT_LAST - 1)) ==
AnimationState.SUBSEQUENT
? blend
: MixBlend.setup;
if (timeline instanceof RotateTimeline) {
this.applyRotateTimeline(timeline, skeleton, animationTime, mix, timelineBlend, timelinesRotation, ii << 1, firstFrame);
}
else {
// This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109
Utils.webkit602BugfixHelper(mix, blend);
timeline.apply(skeleton, animationLast, animationTime, events, mix, timelineBlend, MixDirection.mixIn);
}
}
}
this.queueEvents(current, animationTime);
events.length = 0;
current.nextAnimationLast = animationTime;
current.nextTrackLast = current.trackTime;
}
this.queue.drain();
return applied;
}
/**
* @param {?} to
* @param {?} skeleton
* @param {?} blend
* @return {?}
*/
applyMixingFrom(to, skeleton, blend) {
/** @type {?} */
let from = to.mixingFrom;
if (from.mixingFrom != null)
this.applyMixingFrom(from, skeleton, blend);
/** @type {?} */
let mix = 0;
if (to.mixDuration == 0) {
// Single frame mix to undo mixingFrom changes.
mix = 1;
if (blend == MixBlend.first)
blend = MixBlend.setup;
}
else {
mix = to.mixTime / to.mixDuration;
if (mix > 1)
mix = 1;
if (blend != MixBlend.first)
blend = from.mixBlend;
}
/** @type {?} */
let events = mix < from.eventThreshold ? this.events : null;
/** @type {?} */
let attachments = mix < from.attachmentThreshold;
/** @type {?} */
let drawOrder = mix < from.drawOrderThreshold;
/** @type {?} */
let animationLast = from.animationLast;
/** @type {?} */
let animationTime = from.getAnimationTime();
/** @type {?} */
let timelineCount = from.animation.timelines.length;
/** @type {?} */
let timelines = from.animation.timelines;
/** @type {?} */
let alphaHold = from.alpha * to.interruptAlpha;
/** @type {?} */
let alphaMix = alphaHold * (1 - mix);
if (blend == MixBlend.add) {
for (let i = 0; i < timelineCount; i++)
timelines[i].apply(skeleton, animationLast, animationTime, events, alphaMix, blend, MixDirection.mixOut);
}
else {
/** @type {?} */
let timelineMode = from.timelineMode;
/** @type {?} */
let timelineHoldMix = from.timelineHoldMix;
/** @type {?} */
let firstFrame = from.timelinesRotation.length == 0;
if (firstFrame)
Utils.setArraySize(from.timelinesRotation, timelineCount << 1, null);
/** @type {?} */
let timelinesRotation = from.timelinesRotation;
from.totalAlpha = 0;
for (let i = 0; i < timelineCount; i++) {
/** @type {?} */
let timeline = timelines[i];
/** @type {?} */
let direction = MixDirection.mixOut;
/** @type {?} */
let timelineBlend;
/** @type {?} */
let alpha = 0;
switch (timelineMode[i] & (AnimationState.NOT_LAST - 1)) {
case AnimationState.SUBSEQUENT:
timelineBlend = blend;
if (!attachments && timeline instanceof AttachmentTimeline) {
if ((timelineMode[i] & AnimationState.NOT_LAST) ==
AnimationState.NOT_LAST)
continue;
timelineBlend = MixBlend.setup;
}
if (!drawOrder && timeline instanceof DrawOrderTimeline)
continue;
alpha = alphaMix;
break;
case AnimationState.FIRST:
timelineBlend = MixBlend.setup;
alpha = alphaMix;
break;
case AnimationState.HOLD:
timelineBlend = MixBlend.setup;
alpha = alphaHold;
break;
default:
timelineBlend = MixBlend.setup;
/** @type {?} */
let holdMix = timelineHoldMix[i];
alpha =
alphaHold *
Math.max(0, 1 - holdMix.mixTime / holdMix.mixDuration);
break;
}
from.totalAlpha += alpha;
if (timeline instanceof RotateTimeline)
this.applyRotateTimeline(timeline, skeleton, animationTime, alpha, timelineBlend, timelinesRotation, i << 1, firstFrame);
else {
// This fixes the WebKit 602 specific issue described at http://esotericsoftware.com/forum/iOS-10-disappearing-graphics-10109
Utils.webkit602BugfixHelper(alpha, blend);
if (timelineBlend == MixBlend.setup) {
if (timeline instanceof AttachmentTimeline) {
if (attachments ||
(timelineMode[i] & AnimationState.NOT_LAST) ==
AnimationState.NOT_LAST)
direction = MixDirection.mixIn;
}
else if (timeline instanceof DrawOrderTimeline) {
if (drawOrder)
direction = MixDirection.mixIn;
}
}
timeline.apply(skeleton, animationLast, animationTime, events, alpha, timelineBlend, direction);
}
}
}
if (to.mixDuration > 0)
this.queueEvents(from, animationTime);
this.events.length = 0;
from.nextAnimationLast = animationTime;
from.nextTrackLast = from.trackTime;
return mix;
}
/**
* @param {?} timeline
* @param {?} skeleton
* @param {?} time
* @param {?} alpha
* @param {?} blend
* @param {?} timelinesRotation
* @param {?} i
* @param {?} firstFrame
* @return {?}
*/
applyRotateTimeline(timeline, skeleton, time, alpha, blend, timelinesRotation, i, firstFrame) {
if (firstFrame)
timelinesRotation[i] = 0;
if (alpha == 1) {
timeline.apply(skeleton, 0, time, null, 1, blend, MixDirection.mixIn);
return;
}
/** @type {?} */
let rotateTimeline = (/** @type {?} */ (timeline));
/** @type {?} */
let frames = rotateTimeline.frames;
/** @type {?} */
let bone = skeleton.bones[rotateTimeline.boneIndex];
if (!bone.active)
return;
/** @type {?} */
let r1 = 0;
/** @type {?} */
let r2 = 0;
if (time < frames[0]) {
switch (blend) {
case MixBlend.setup:
bone.rotation = bone.data.rotation;
default:
return;
case MixBlend.first:
r1 = bone.rotation;
r2 = bone.data.rotation;
}
}
else {
r1 = blend == MixBlend.setup ? bone.data.rotation : bone.rotation;
if (time >= frames[frames.length - RotateTimeline.ENTRIES])
// Time is after last frame.
r2 =
bone.data.rotation +
frames[frames.length + RotateTimeline.PREV_ROTATION];
else {
// Interpolate between the previous frame and the current frame.
/** @type {?} */
let frame = Animation.binarySearch(frames, time, RotateTimeline.ENTRIES);
/** @type {?} */
let prevRotation = frames[frame + RotateTimeline.PREV_ROTATION];
/** @type {?} */
let frameTime = frames[frame];
/** @type {?} */
let percent = rotateTimeline.getCurvePercent((frame >> 1) - 1, 1 -
(time - frameTime) /
(frames[frame + RotateTimeline.PREV_TIME] - frameTime));
r2 = frames[frame + RotateTimeline.ROTATION] - prevRotation;
r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360;
r2 = prevRotation + r2 * percent + bone.data.rotation;
r2 -= (16384 - ((16384.499999999996 - r2 / 360) | 0)) * 360;
}
}
// Mix between rotations using the direction of the shortest route on the first frame while detecting crosses.
/** @type {?} */
let total = 0;
/** @type {?} */
let diff = r2 - r1;
diff -= (16384 - ((16384.499999999996 - diff / 360) | 0)) * 360;
if (diff == 0) {
total = timelinesRotation[i];
}
else {
/** @type {?} */
let lastTotal = 0;
/** @type {?} */
let lastDiff = 0;
if (firstFrame) {
lastTotal = 0;
lastDiff = diff;
}
else {
lastTotal = timelinesRotation[i]; // Angle and direction of mix, including loops.
lastDiff = timelinesRotation[i + 1]; // Difference between bones.
}
/** @type {?} */
let current = diff > 0;
/** @type {?} */
let dir = lastTotal >= 0;
// Detect cross at 0 (not 180).
if (MathUtils.signum(lastDiff) != MathUtils.signum(diff) &&
Math.abs(lastDiff) <= 90) {
// A cross after a 360 rotation is a loop.
if (Math.abs(lastTotal) > 180)
lastTotal += 360 * MathUtils.signum(lastTotal);
dir = current;
}
total = diff + lastTotal - (lastTotal % 360); // Store loops as part of lastTotal.
if (dir != current)
total += 360 * MathUtils.signum(lastTotal);
timelinesRotation[i] = total;
}
timelinesRotation[i + 1] = diff;
r1 += total * alpha;
bone.rotation = r1 - (16384 - ((16384.499999999996 - r1 / 360) | 0)) * 360;
}
/**
* @param {?} entry
* @param {?} animationTime
* @return {?}
*/
queueEvents(entry, animationTime) {
/** @type {?} */
let animationStart = entry.animationStart;
/** @type {?} */
let animationEnd = entry.animationEnd;
/** @type {?} */
let duration = animationEnd - animationStart;
/** @type {?} */
let trackLastWrapped = entry.trackLast % duration;
// Queue events before complete.
/** @type {?} */
let events = this.events;
/** @type {?} */
let i = 0;
/** @type {?} */
let n = events.length;
for (; i < n; i++) {
/** @type {?} */
let event = events[i];
if (event.time < trackLastWrapped)
break;
if (event.time > animationEnd)
continue; // Discard events outside animation start/end.
this.queue.event(entry, event);
}
// Queue complete if completed a loop iteration or the animation.
/** @type {?} */
let complete = false;
if (entry.loop)
complete = duration == 0 || trackLastWrapped > entry.trackTime % duration;
else
complete =
animationTime >= animationEnd && entry.animationLast < animationEnd;
if (complete)
this.queue.complete(entry);
// Queue events after complete.
for (; i < n; i++) {
/** @type {?} */
let event = events[i];
if (event.time < animationStart)
continue; // Discard events outside animation start/end.
this.queue.event(entry, events[i]);
}
}
/**
* @return {?}
*/
clearTracks() {
/** @type {?} */
let oldDrainDisabled = this.queue.drainDisabled;
this.queue.drainDisabled = true;
for (let i = 0, n = this.tracks.length; i < n; i++)
this.clearTrack(i);
this.tracks.length = 0;
this.queue.drainDisabled = oldDrainDisabled;
this.queue.drain();
}
/**
* @param {?} trackIndex
* @return {?}
*/
clearTrack(trackIndex) {
if (trackIndex >= this.tracks.length)
return;
/** @type {?} */
let current = this.tracks[trackIndex];
if (current == null)
return;
this.queue.end(current);
this.disposeNext(current);
/** @type {?} */
let entry = current;
while (true) {
/** @type {?} */
let from = entry.mixingFrom;
if (from == null)
break;
this.queue.end(from);
entry.mixingFrom = null;
entry.mixingTo = null;
entry = from;
}
this.tracks[current.trackIndex] = null;
this.queue.drain();
}
/**
* @param {?} index
* @param {?} current
* @param {?} interrupt
* @return {?}
*/
setCurrent(index, current, interrupt) {
/** @type {?} */
let from = this.expandToIndex(index);
this.tracks[index] = current;
if (from != null) {
if (interrupt)
this.queue.interrupt(from);
current.mixingFrom = from;
from.mixingTo = current;
current.mixTime = 0;
// Store the interrupted mix percentage.
if (from.mixingFrom != null && from.mixDuration > 0)
current.interruptAlpha *= Math.min(1, from.mixTime / from.mixDuration);
from.timelinesRotation.length = 0; // Reset rotation for mixing out, in case entry was mixed in.
}
this.queue.start(current);
}
/**
* @param {?} trackIndex
* @param {?} animationName
* @param {?} loop
* @return {?}
*/
setAnimation(trackIndex, animationName, loop) {
/** @type {?} */
let animation = this.data.skeletonData.findAnimation(animationName);
if (animation == null)
throw new Error("Animation not found: " + animationName);
return this.setAnimationWith(trackIndex, animation, loop);
}
/**
* @param {?} trackIndex
* @param {?} animation
* @param {?} loop
* @return {?}
*/
setAnimationWith(trackIndex, animation, loop) {
if (animation == null)
throw new Error("animation cannot be null.");
/** @type {?} */
let interrupt = true;
/** @type {?} */
let current = this.expandToIndex(trackIndex);
if (current != null) {
if (current.nextTrackLast == -1) {
// Don't mix from an entry that was never applied.
this.tracks[trackIndex] = current.mixingFrom;
this.queue.interrupt(current);
this.queue.end(current);
this.disposeNext(current);
current = current.mixingFrom;
interrupt = false;
}
else
this.disposeNext(current);
}
/** @type {?} */
let entry = this.trackEntry(trackIndex, animation, loop, current);
this.setCurrent(trackIndex, entry, interrupt);
this.queue.drain();
return entry;
}
/**
* @param {?} trackIndex
* @param {?} animationName
* @param {?} loop
* @param {?} delay
* @return {?}
*/
addAnimation(trackIndex, animationName, loop, delay) {
/** @type {?} */
let animation = this.data.skeletonData.findAnimation(animationName);
if (animation == null)
throw new Error("Animation not found: " + animationName);
return this.addAnimationWith(trackIndex, animation, loop, delay);
}
/**
* @param {?} trackIndex
* @param {?} animation
* @param {?} loop
* @param {?} delay
* @return {?}
*/
addAnimationWith(trackIndex, animation, loop, delay) {
if (animation == null)
throw new Error("animation cannot be null.");
/** @type {?} */
let last = this.expandToIndex(trackIndex);
if (last != null) {
while (last.next != null)
last = last.next;
}
/** @type {?} */
let entry = this.trackEntry(trackIndex, animation, loop, last);
if (last == null) {
this.setCurrent(trackIndex, entry, true);
this.queue.drain();
}
else {
last.next = entry;
if (delay <= 0) {
/** @type {?} */
let duration = last.animationEnd - last.animationStart;
if (duration != 0) {
if (last.loop)
delay += duration * (1 + ((last.trackTime / duration) | 0));
else
delay += Math.max(duration, last.trackTime);
delay -= this.data.getMix(last.animation, animation);
}
else
delay = last.trackTime;
}
}
entry.delay = delay;
return entry;
}
/**
* @param {?} trackIndex
* @param {?} mixDuration
* @return {?}
*/
setEmptyAnimation(trackIndex, mixDuration) {
/** @type {?} */
let entry = this.setAnimationWith(trackIndex, AnimationState.emptyAnimation, false);
entry.mixDuration = mixDuration;
entry.trackEnd = mixDuration;
return entry;
}
/**
* @param {?} trackIndex
* @param {?} mixDuration
* @param {?} delay
* @return {?}
*/
addEmptyAnimation(trackIndex, mixDuration, delay) {
if (delay <= 0)
delay -= mixDuration;
/** @type {?} */
let entry = this.addAnimationWith(trackIndex, AnimationState.emptyAnimation, false, delay);
entry.mixDuration = mixDuration;
entry.trackEnd = mixDuration;
return entry;
}
/**
* @param {?} mixDuration
* @return {?}
*/
setEmptyAnimations(mixDuration) {
/** @type {?} */
let oldDrainDisabled = this.queue.drainDisabled;
this.queue.drainDisabled = true;
for (let i = 0, n = this.tracks.length; i < n; i++) {
/** @type {?} */
let current = this.tracks[i];
if (current != null)
this.setEmptyAnimation(current.trackIndex, mixDuration);
}
this.queue.drainDisabled = oldDrainDisabled;
this.queue.drain();
}
/**
* @param {?} index
* @return {?}
*/
expandToIndex(index) {
if (index < this.tracks.length)
return this.tracks[index];
Utils.ensureArrayCapacity(this.tracks, index + 1, null);
this.tracks.length = index + 1;
return null;
}
/**
* @param {?} trackIndex
* @param {?} animation
* @param {?} loop
* @param {?} last
* @return {?}
*/
trackEntry(trackIndex, animation, loop, last) {
/** @type {?} */
let entry = this.trackEntryPool.obtain();
entry.trackIndex = trackIndex;
entry.animation = animation;
entry.loop = loop;
entry.holdPrevious = false;
entry.eventThreshold = 0;
entry.attachmentThreshold = 0;
entry.drawOrderThreshold = 0;
entry.animationStart = 0;
entry.animationEnd = animation.duration;
entry.animationLast = -1;
entry.nextAnimationLast = -1;
entry.delay = 0;
entry.trackTime = 0;
entry.trackLast = -1;
entry.nextTrackLast = -1;
entry.trackEnd = Number.MAX_VALUE;
entry.timeScale = 1;
entry.alpha = 1;
entry.interruptAlpha = 1;
entry.mixTime = 0;
entry.mixDuration =
last == null ? 0 : this.data.getMix(last.animation, animation);
return entry;
}
/**
* @param {?} entry
* @return {?}
*/
disposeNext(entry) {
/** @type {?} */
let next = entry.next;
while (next != null) {
this.queue.dispose(next);
next = next.next;
}
entry.next = null;
}
/**
* @return {?}
*/
_animationsChanged() {
this.animationsChanged = false;
this.propertyIDs.clear();
for (let i = 0, n = this.tracks.length; i < n; i++) {
/** @type {?} */
let entry = this.tracks[i];
if (entry == null)
continue;
while (entry.mixingFrom != null)
entry = entry.mixingFrom;
do {
if (entry.mixingFrom == null || entry.mixBlend != MixBlend.add)
this.computeHold(entry);
entry = entry.mixingTo;
} while (entry != null);
}
this.propertyIDs.clear();
for (let i = this.tracks.length - 1; i >= 0; i--) {
/** @type {?} */
let entry = this.tracks[i];
while (entry != null) {
this.computeNotLast(entry);
entry = entry.mixingFrom;
}
}
}
/**
* @param {?} entry
* @return {?}
*/
computeHold(entry) {
/** @type {?} */
let to = entry.mixingTo;
/** @type {?} */
let timelines = entry.animation.timelines;
/** @type {?} */
let timelinesCount = entry.animation.timelines.length;
/** @type {?} */
let timelineMode = Utils.setArraySize(entry.timelineMode, timelinesCount);
entry.timelineHoldMix.length = 0;
/** @type {?} */
let timelineDipMix = Utils.setArraySize(entry.timelineHoldMix, timelinesCount);
/** @type {?} */
let propertyIDs = this.propertyIDs;
if (to != null && to.holdPrevious) {
for (let i = 0; i < timelinesCount; i++) {
propertyIDs.add(timelines[i].getPropertyId());
timelineMode[i] = AnimationState.HOLD;
}
return;
}
outer: for (let i = 0; i < timelinesCount; i++) {
/** @type {?} */
let timeline = timelines[i];
/** @type {?} */
let id = timeline.getPropertyId();
if (!propertyIDs.add(id))
timelineMode[i] = AnimationState.SUBSEQUENT;
else if (to == null ||
timeline instanceof AttachmentTimeline ||
timeline instanceof DrawOrderTimeline ||
timeline instanceof EventTimeline ||
!to.animation.hasTimeline(id)) {
timelineMode[i] = AnimationState.FIRST;
}
else {
for (let next = to.mixingTo; next != null; next = next.mixingTo) {
if (next.animation.hasTimeline(id))
continue;
if (entry.mixDuration > 0) {
timelineMode[i] = AnimationState.HOLD_MIX;
timelineDipMix[i] = next;
continue outer;
}
break;
}
timelineMode[i] = AnimationState.HOLD;
}
}
}
/**
* @param {?} entry
* @return {?}
*/
computeNotLast(entry) {
/** @type {?} */
let timelines = entry.animation.timelines;
/** @type {?} */
let timelinesCount = entry.animation.timelines.length;
/** @type {?} */
let timelineMode = entry.timelineMode;
/** @type {?} */
let propertyIDs = this.propertyIDs;
for (let i = 0; i < timelinesCount; i++) {
if (timelines[i] instanceof AttachmentTimeline) {
/** @type {?} */
let timeline = (/** @type {?} */ (timelines[i]));
if (!propertyIDs.add(timeline.slotIndex))
timelineMode[i] |= AnimationState.NOT_LAST;
}
}
}
/**
* @param {?} trackIndex
* @return {?}
*/
getCurrent(trackIndex) {
if (trackIndex >= this.tracks.length)
return null;
return this.tracks[trackIndex];
}
/**
* @param {?} listener
* @return {?}
*/
addListener(listener) {
if (listener == null)
throw new Error("listener cannot be null.");
this.listeners.push(listener);
}
/**
* Removes the listener added with {\@link #addListener(AnimationStateListener)}.
* @param {?} listener
* @return {?}
*/
removeListener(listener) {
/** @type {?} */
let index = this.listeners.indexOf(listener);
if (index >= 0)
this.listeners.splice(index, 1);
}
/**
* @return {?}
*/
clearListeners() {
this.listeners.length = 0;
}
/**
* @return {?}
*/
clearListenerNotifications() {
this.queue.clear();
}
}
AnimationState.emptyAnimation = new Animation("<empty>", [], 0);
AnimationState.SUBSEQUENT = 0;
AnimationState.FIRST = 1;
AnimationState.HOLD = 2;
AnimationState.HOLD_MIX = 3;
AnimationState.NOT_LAST = 4;
if (false) {
/** @type {?} */
AnimationState.emptyAnimation;
/** @type {?} */
AnimationState.SUBSEQUENT;
/** @type {?} */
AnimationState.FIRST;
/** @type {?} */
AnimationState.HOLD;
/** @type {?} */
AnimationState.HOLD_MIX;
/** @type {?} */
AnimationState.NOT_LAST;
/** @type {?} */
AnimationState.prototype.data;
/** @type {?} */
AnimationState.prototype.tracks;
/** @type {?} */
AnimationState.prototype.events;
/** @type {?} */
AnimationState.prototype.listeners;
/** @type {?} */
AnimationState.prototype.queue;
/** @type {?} */
AnimationState.prototype.propertyIDs;
/** @type {?} */
AnimationState.prototype.animationsChanged;
/** @type {?} */
AnimationState.prototype.timeScale;
/** @type {?} */
AnimationState.prototype.trackEntryPool;
}
export class TrackEntry {
constructor() {
this.mixBlend = MixBlend.replace;
this.timelineMode = new Array();
this.timelineHoldMix = new Array();
this.timelinesRotation = new Array();
}
/**
* @return {?}
*/
reset() {
this.next = null;
this.mixingFrom = null;
this.mixingTo = null;
this.animation = null;
this.listener = null;
this.timelineMode.length = 0;
this.timelineHoldMix.length = 0;
this.timelinesRotation.length = 0;
}
/**
* @return {?}
*/
getAnimationTime() {
if (this.loop) {
/** @type {?} */
let duration = this.animationEnd - this.animationStart;
if (duration == 0)
return this.animationStart;
return (this.trackTime % duration) + this.animationStart;
}
return Math.min(this.trackTime + this.animationStart, this.animationEnd);
}
/**
* @param {?} animationLast
* @return {?}
*/
setAnimationLast(animationLast) {
this.animationLast = animationLast;
this.nextAnimationLast = animationLast;
}
/**
* @return {?}
*/
isComplete() {
return this.trackTime >= this.animationEnd - this.animationStart;
}
/**
* @return {?}
*/
resetRotationDirections() {
this.timelinesRotation.length = 0;
}
}
if (false) {
/** @type {?} */
TrackEntry.prototype.animation;
/** @type {?} */
TrackEntry.prototype.next;
/** @type {?} */
TrackEntry.prototype.mixingFrom;
/** @type {?} */
TrackEntry.prototype.mixingTo;
/** @type {?} */
TrackEntry.prototype.listener;
/** @type {?} */
TrackEntry.prototype.trackIndex;
/** @type {?} */
TrackEntry.prototype.loop;
/** @type {?} */
TrackEntry.prototype.holdPrevious;
/** @type {?} */
TrackEntry.prototype.eventThreshold;
/** @type {?} */
TrackEntry.prototype.attachmentThreshold;
/** @type {?} */
TrackEntry.prototype.drawOrderThreshold;
/** @type {?} */
TrackEntry.prototype.animationStart;
/** @type {?} */
TrackEntry.prototype.animationEnd;
/** @type {?} */
TrackEntry.prototype.animationLast;
/** @type {?} */
TrackEntry.prototype.nextAnimationLast;
/** @type {?} */
TrackEntry.prototype.delay;
/** @type {?} */
TrackEntry.prototype.trackTime;
/** @type {?} */
TrackEntry.prototype.trackLast;
/** @type {?} */
TrackEntry.prototype.nextTrackLast;
/** @type {?} */
TrackEntry.prototype.trackEnd;
/** @type {?} */
TrackEntry.prototype.timeScale;
/** @type {?} */
TrackEntry.prototype.alpha;
/** @type {?} */
TrackEntry.prototype.mixTime;
/** @type {?} */
TrackEntry.prototype.mixDuration;
/** @type {?} */
TrackEntry.prototype.interruptAlpha;
/** @type {?} */
TrackEntry.prototype.totalAlpha;
/** @type {?} */
TrackEntry.prototype.mixBlend;
/** @type {?} */
TrackEntry.prototype.timelineMode;
/** @type {?} */
TrackEntry.prototype.timelineHoldMix;
/** @type {?} */
TrackEntry.prototype.timelinesRotation;
}
export class EventQueue {
/**
* @param {?} animState
*/
constructor(animState) {
this.objects = [];
this.drainDisabled = false;
this.animState = animState;
}
/**
* @param {?} entry
* @return {?}
*/
start(entry) {
this.objects.push(EventType.start);
this.objects.push(entry);
this.animState.animationsChanged = true;
}
/**
* @param {?} entry
* @return {?}
*/
interrupt(entry) {
this.objects.push(EventType.interrupt);
this.objects.push(entry);
}
/**
* @param {?} entry
* @return {?}
*/
end(entry) {
this.objects.push(EventType.end);
this.objects.push(entry);
this.animState.animationsChanged = true;
}
/**
* @param {?} entry
* @return {?}
*/
dispose(entry) {
this.objects.push(EventType.dispose);
this.objects.push(entry);
}
/**
* @param {?} entry
* @return {?}
*/
complete(entry) {
this.objects.push(EventType.complete);
this.objects.push(entry);
}
/**
* @param {?} entry
* @param {?} event
* @return {?}
*/
event(entry, event) {
this.objects.push(EventType.event);
this.objects.push(entry);
this.objects.push(event);
}
/**
* @return {?}
*/
drain() {
if (this.drainDisabled)
return;
this.drainDisabled = true;
/** @type {?} */
let objects = this.objects;
/** @type {?} */
let listeners = this.animState.listeners;
for (let i = 0; i < objects.length; i += 2) {
/** @type {?} */
let type = (/** @type {?} */ (objects[i]));
/** @type {?} */
let entry = (/** @type {?} */ (objects[i + 1]));
switch (type) {
case EventType.start:
if (entry.listener != null && entry.listener.start)
entry.listener.start(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].start)
listeners[ii].start(entry);
break;
case EventType.interrupt:
if (entry.listener != null && entry.listener.interrupt)
entry.listener.interrupt(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].interrupt)
listeners[ii].interrupt(entry);
break;
case EventType.end:
if (entry.listener != null && entry.listener.end)
entry.listener.end(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].end)
listeners[ii].end(entry);
// Fall through.
case EventType.dispose:
if (entry.listener != null && entry.listener.dispose)
entry.listener.dispose(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].dispose)
listeners[ii].dispose(entry);
this.animState.trackEntryPool.free(entry);
break;
case EventType.complete:
if (entry.listener != null && entry.listener.complete)
entry.listener.complete(entry);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].complete)
listeners[ii].complete(entry);
break;
case EventType.event:
/** @type {?} */
let event = (/** @type {?} */ (objects[i++ + 2]));
if (entry.listener != null && entry.listener.event)
entry.listener.event(entry, event);
for (let ii = 0; ii < listeners.length; ii++)
if (listeners[ii].event)
listeners[ii].event(entry, event);
break;
}
}
this.clear();
this.drainDisabled = false;
}
/**
* @return {?}
*/
clear() {
this.objects.length = 0;
}
}
if (false) {
/** @type {?} */
EventQueue.prototype.objects;
/** @type {?} */
EventQueue.prototype.drainDisabled;
/** @type {?} */
EventQueue.prototype.animState;
}
/** @enum {number} */
const EventType = {
start: 0,
interrupt: 1,
end: 2,
dispose: 3,
complete: 4,
event: 5,
};
export { EventType };
EventType[EventType.start] = 'start';
EventType[EventType.interrupt] = 'interrupt';
EventType[EventType.end] = 'end';
EventType[EventType.dispose] = 'dispose';
EventType[EventType.complete] = 'complete';
EventType[EventType.event] = 'event';
/**
* @record
*/
export function AnimationStateListener2() { }
if (false) {
/**
* Invoked when this entry has been set as the current entry.
* @param {?} entry
* @return {?}
*/
AnimationStateListener2.prototype.start = function (entry) { };
/**
* Invoked when another entry has replaced this entry as the current entry. This entry may continue being applied for
* mixing.
* @param {?} entry
* @return {?}
*/
AnimationStateListener2.prototype.interrupt = function (entry) { };
/**
* Invoked when this entry is no longer the current entry and will never be applied again.
* @param {?} entry
* @return {?}
*/
AnimationStateListener2.prototype.end = function (entry) { };
/**
* Invoked when this entry will be disposed. This may occur without the entry ever being set as the current entry.
* References to the entry should not be kept after dispose is called, as it may be destroyed or reused.
* @param {?} entry
* @return {?}
*/
AnimationStateListener2.prototype.dispose = function (entry) { };
/**
* Invoked every time this entry's animation completes a loop.
* @param {?} entry
* @return {?}
*/
AnimationStateListener2.prototype.complete = function (entry) { };
/**
* Invoked when this entry's animation triggers an event.
* @param {?} entry
* @param {?} event
* @return {?}
*/
AnimationStateListener2.prototype.event = function (entry, event) { };
}
/**
* @abstract
*/
export class AnimationStateAdapter2 {
/**
* @param {?} entry
* @return {?}
*/
start(entry) { }
/**
* @param {?} entry
* @return {?}
*/
interrupt(entry) { }
/**
* @param {?} entry
* @return {?}
*/
end(entry) { }
/**
* @param {?} entry
* @return {?}
*/
dispose(entry) { }
/**
* @param {?} entry
* @return {?}
*/
complete(entry) { }
/**
* @param {?} entry
* @param {?} event
* @return {?}
*/
event(entry, event) { }
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQW5pbWF0aW9uU3RhdGUuanMiLCJzb3VyY2VSb290Ijoibmc6Ly9uZ3gtc3BpbmUvIiwic291cmNlcyI6WyJsaWIvc3BpbmUtdHMvY29yZS9BbmltYXRpb25TdGF0ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQTRCQSxPQUFPLEVBQUUsU0FBUyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBRXpELE9BQU8sRUFDTCxTQUFTLEVBQ1QsUUFBUSxFQUNSLGtCQUFrQixFQUNsQixZQUFZLEVBQ1osaUJBQWlCLEVBQ2pCLGNBQWMsRUFDZCxhQUFhLEVBRWQsTUFBTSxhQUFhLENBQUM7QUFJckIsTUFBTSxPQUFPLGNBQWM7Ozs7SUFnQnpCLFlBQVksSUFBd0I7UUFScEMsV0FBTSxHQUFHLElBQUksS0FBSyxFQUFjLENBQUM7UUFDakMsV0FBTSxHQUFHLElBQUksS0FBSyxFQUFTLENBQUM7UUFDNUIsY0FBUyxHQUFHLElBQUksS0FBSyxFQUEyQixDQUFDO1FBQ2pELFVBQUssR0FBRyxJQUFJLFVBQVUsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUM3QixnQkFBVyxHQUFHLElBQUksTUFBTSxFQUFFLENBQUM7UUFDM0Isc0JBQWlCLEdBQUcsS0FBSyxDQUFDO1FBQzFCLGNBQVMsR0FBRyxDQUFDLENBQUM7UUFDZCxtQkFBYyxHQUFHLElBQUksSUFBSTs7O1FBQWEsR0FBRyxFQUFFLENBQUMsSUFBSSxVQUFVLEVBQUUsRUFBQyxDQUFDO1FBRTVELElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0lBQ25CLENBQUM7Ozs7O0lBQ0QsTUFBTSxDQUFDLEtBQWE7UUFDbEIsS0FBSyxJQUFJLElBQUksQ0FBQyxTQUFTLENBQUM7O1lBQ3BCLE1BQU0sR0FBRyxJQUFJLENBQUMsTUFBTTtRQUN4QixLQUFLLElBQUksQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLEdBQUcs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