superfly-timeline
Version:
Resolver for defining objects with temporal boolean logic relationships on a timeline
115 lines • 5.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.StateHandler = void 0;
const instance_1 = require("./lib/instance");
const lib_1 = require("./lib/lib");
const performance_1 = require("./lib/performance");
const timeline_1 = require("./lib/timeline");
class StateHandler {
getState(resolvedTimeline, time, eventLimit = 0) {
const toc = (0, performance_1.tic)('getState');
const state = {
time: time,
layers: {},
nextEvents: resolvedTimeline.nextEvents.filter((e) => e.time > time),
};
if (eventLimit)
state.nextEvents = state.nextEvents.slice(0, eventLimit);
for (const obj of Object.values(resolvedTimeline.objects)) {
if (!(0, timeline_1.objHasLayer)(obj))
continue;
// Note: We can assume that it is not a keyframe here, because keyframes don't have layers
for (const instance of obj.resolved.instances) {
if ((0, instance_1.instanceIsActive)(instance, time)) {
let contentIsOriginal = true;
const objInstance = {
...obj,
instance,
};
/* istanbul ignore if */
if (state.layers[`${obj.layer}`]) {
// There is already an object on this layer!
console.error(`layer "${obj.layer}": ${JSON.stringify(state.layers[`${obj.layer}`])}`);
console.error(`object "${objInstance.id}": ${JSON.stringify(objInstance)}`);
throw new Error(`Internal Error: There is already an object on layer "${obj.layer}"!`);
}
state.layers[`${obj.layer}`] = objInstance;
// Now, apply keyframes:
const objectKeyframes = obj.keyframes
? obj.keyframes.map((kf) => resolvedTimeline.objects[kf.id])
: [];
for (const keyframe of this.getActiveKeyframeInstances(objectKeyframes, time)) {
if (contentIsOriginal) {
// We don't want to modify the original content, so we deep-clone it before modifying it:
objInstance.content = (0, lib_1.clone)(obj.content);
contentIsOriginal = false;
}
StateHandler.applyKeyframeContent(objInstance.content, keyframe.content);
}
}
}
}
toc();
return state;
}
/**
* Apply keyframe content onto its parent content.
* The keyframe content is deeply-applied onto the parent content.
*/
static applyKeyframeContent(parentContent, keyframeContent) {
const toc = (0, performance_1.tic)(' applyKeyframeContent');
for (const [attr, value] of Object.entries(keyframeContent)) {
if ((0, lib_1.isObject)(value)) {
if ((0, lib_1.isArray)(value)) {
// Value is an array
if (!Array.isArray(parentContent[attr]))
parentContent[attr] = [];
this.applyKeyframeContent(parentContent[attr], value);
parentContent[attr].splice(value.length, Infinity);
}
else {
// Value is an object
if (!(0, lib_1.isObject)(parentContent[attr]) || Array.isArray(parentContent[attr]))
parentContent[attr] = {};
this.applyKeyframeContent(parentContent[attr], value);
}
}
else {
parentContent[attr] = value;
}
}
toc();
}
getActiveKeyframeInstances(keyframes, time) {
const keyframeInstances = [];
for (const keyframe of keyframes) {
for (const instance of keyframe.resolved.instances) {
if ((0, instance_1.instanceIsActive)(instance, time)) {
keyframeInstances.push({
...keyframe,
instance,
});
}
}
}
keyframeInstances.sort((a, b) => {
// Highest priority is applied last:
const aPriority = a.priority ?? 0;
const bPriority = b.priority ?? 0;
if (aPriority < bPriority)
return -1;
if (aPriority > bPriority)
return 1;
// Last start time is applied last:
if (a.instance.start < b.instance.start)
return -1;
if (a.instance.start > b.instance.start)
return 1;
/* istanbul ignore next */
return 0;
});
return keyframeInstances;
}
}
exports.StateHandler = StateHandler;
//# sourceMappingURL=StateHandler.js.map