@visactor/vgrammar-core
Version:
VGrammar is a visual grammar library
269 lines (264 loc) • 15.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: !0
}), exports.Animate = void 0;
const vutils_1 = require("@visactor/vutils"), animator_1 = require("./animator"), config_1 = require("./config"), constants_1 = require("../constants"), util_1 = require("../../parse/util"), arranger_1 = require("./arranger"), enums_1 = require("../enums");
class Animate {
constructor(mark, config) {
this.state = null, this.immediateConfigs = [], this.isEnabled = !0, this.disabledStates = [],
this.animators = new Map, this.elementRecorder = new WeakMap, this.timelineCount = {},
this.mark = mark, this.configs = (0, config_1.normalizeAnimationConfig)(null != config ? config : {});
}
getAnimationConfigs(animationState) {
var _a;
return this.isEnabled ? (null !== (_a = this.configs) && void 0 !== _a ? _a : []).filter((config => config.state === animationState)) : [];
}
updateConfig(config) {
this.configs = (0, config_1.normalizeAnimationConfig)(null != config ? config : {});
}
updateState(state) {
this.state = state;
}
animate() {
if (!this.isEnabled || !this.configs || !this.configs.length) return;
const elements = this.mark.getAllElements(), parameters = this.mark.parameters();
return elements.forEach((element => {
var _a;
element.isReserved && element.diffState !== enums_1.DiffState.exit && (element.isReserved = !1);
const prevElementState = null === (_a = this.elementRecorder.get(element)) || void 0 === _a ? void 0 : _a.prevState;
(this.configs.some((config => prevElementState !== element.diffState && config.state === prevElementState && config.timeline.controlOptions.stopWhenStateChange)) || element.diffState === enums_1.DiffState.exit && prevElementState !== element.diffState) && this.clearElementAnimation(element, !1);
})), this.configs.forEach((config => {
this.animateByTimeline(config, elements, parameters);
})), this.mark.cleanExitElements(), this;
}
runAnimationByState(animationState) {
if (!this.isEnabled) return;
const stateConfigs = this.configs.filter((config => config.state === animationState)), elements = this.mark.getAllElements(), parameters = this.mark.parameters(), animators = stateConfigs.reduce(((animators, config) => animators.concat(this.animateByTimeline(config, elements, parameters, !0))), []);
return new arranger_1.Arranger(animators);
}
stopAnimationByState(animationState) {
const animators = this.animators.get(animationState);
return animators && animators.forEach((animator => animator.stop("end"))), this;
}
pauseAnimationByState(animationState) {
const animators = this.animators.get(animationState);
return animators && animators.forEach((animator => animator.pause())), this;
}
resumeAnimationByState(animationState) {
const animators = this.animators.get(animationState);
return animators && animators.forEach((animator => animator.resume())), this;
}
run(config) {
if (!this.isEnabled) return;
const parsedConfigs = (0, config_1.normalizeStateAnimationConfig)(constants_1.ImmediateAnimationState, config, this.immediateConfigs.length);
this.immediateConfigs = this.immediateConfigs.concat(parsedConfigs);
const elements = this.mark.getAllElements(), parameters = this.mark.parameters(), animators = parsedConfigs.reduce(((animators, config) => animators.concat(this.animateByTimeline(config, elements, parameters, !0))), []);
return new arranger_1.Arranger(animators);
}
stop() {
return this.animators.forEach((animators => {
animators.forEach((animator => animator.stop("end")));
})), this;
}
pause() {
return this.animators.forEach((stateAnimators => stateAnimators.forEach((animator => animator.pause())))),
this;
}
resume() {
return this.animators.forEach((stateAnimators => stateAnimators.forEach((animator => animator.resume())))),
this;
}
reverse() {
return this;
}
restart() {
return this;
}
record() {
return this;
}
recordEnd() {
return this;
}
isAnimating() {
let isAnimating = !1;
return this.animators.forEach((animators => {
isAnimating = isAnimating || animators.some((animator => animator.isAnimating));
})), isAnimating;
}
isElementAnimating(element) {
var _a;
const stateAnimationCounts = null === (_a = this.elementRecorder.get(element)) || void 0 === _a ? void 0 : _a.count;
return (0, vutils_1.isNil)(stateAnimationCounts) || Object.values(stateAnimationCounts).every((count => 0 === count));
}
getAnimatorCount() {
let count = 0;
return this.animators.forEach((animators => count += animators.length)), count;
}
getAllAnimators() {
const allAnimators = [];
return this.animators.forEach((animators => {
allAnimators.push(...animators);
})), allAnimators;
}
getElementAnimators(element, animationState) {
var _a;
const elements = (0, vutils_1.array)(element);
let animators = [];
return animationState ? animators = null !== (_a = this.animators.get(animationState)) && void 0 !== _a ? _a : [] : this.animators.forEach((stateAnimators => {
animators = animators.concat(stateAnimators);
})), animators.filter((animator => elements.includes(animator.element)));
}
enable() {
return this.isEnabled = !0, this;
}
disable() {
return this.isEnabled = !1, this.stop(), this.animators.clear(), this;
}
enableAnimationState(state) {
const states = (0, vutils_1.array)(state);
return this.disabledStates = this.disabledStates.filter((state => !states.includes(state))),
this;
}
disableAnimationState(state) {
const states = (0, vutils_1.array)(state);
return this.disabledStates = this.disabledStates.concat(states), this;
}
release() {
this.stop(), this.animators.clear(), this.configs = null, this.animators = null,
this.elementRecorder = null, this.timelineCount = null;
}
animateByTimeline(config, elements, parameters, forceState = !1) {
var _a;
const animators = [], animatedElements = elements.filter((element => {
const checkExit = !(element.isReserved && element.diffState === enums_1.DiffState.exit), state = this.getAnimationState(element), checkDisabled = !this.disabledStates.includes(state), checkState = forceState || state === config.state, checkPartitioner = !config.timeline.partitioner || config.timeline.partitioner(element.getDatum(), element, parameters);
return checkExit && checkDisabled && checkState && checkPartitioner;
}));
if (animatedElements.length) {
(0, vutils_1.isNil)(this.timelineCount[config.id]) && (this.timelineCount[config.id] = 0),
config.timeline.sort && animatedElements.sort(((elementA, elementB) => config.timeline.sort(elementA.getDatum(), elementB.getDatum(), elementA, elementB, parameters)));
const animationParameters = {
width: this.mark.view.width(),
height: this.mark.view.height(),
group: null !== (_a = this.mark.group) && void 0 !== _a ? _a : null,
mark: this.mark,
view: this.mark.view,
elementCount: animatedElements.length,
elementIndex: 0
};
animatedElements.forEach((element => {
var _a;
(null !== (_a = this.animators.get(config.state)) && void 0 !== _a ? _a : []).filter((animator => animator.element === element && animator.animationOptions.id === config.id)).forEach((animator => {
animator.stop(null, !1), this.handleAnimatorEnd(animator, !1);
}));
})), animatedElements.forEach(((element, index) => {
animationParameters.elementIndex = index;
const mergedParameters = Object.assign({
[constants_1.DefaultAnimationParameters]: animationParameters
}, parameters), animationUnit = this.getAnimationUnit(config.timeline, element, index, animatedElements.length, mergedParameters);
animators.push(this.animateElement(config, animationUnit, element, animationParameters, mergedParameters));
}));
}
return animators;
}
animateElement(config, animationUnit, element, animationParameters, parameters) {
var _a, _b;
const animator = new animator_1.Animator(element, animationUnit, config);
if (animator.animate(animationParameters, parameters), !animator.isAnimating) return;
element.diffState === enums_1.DiffState.exit && (element.isReserved = !0);
const isFirstAnimator = 0 === this.timelineCount[config.id];
this.timelineCount[config.id] += 1;
const elementRecord = null !== (_a = this.elementRecorder.get(element)) && void 0 !== _a ? _a : {
prevState: config.state,
count: {}
};
elementRecord.prevState = config.state, elementRecord.count[config.state] = (null !== (_b = elementRecord.count[config.state]) && void 0 !== _b ? _b : 0) + 1,
this.elementRecorder.set(element, elementRecord);
const stateData = this.animators.get(config.state);
stateData ? stateData.push(animator) : this.animators.set(config.state, [ animator ]),
animator.callback((() => {
this.handleAnimatorEnd(animator);
}));
const animationEvent = {
mark: this.mark,
animationState: config.state,
animationConfig: config.originConfig
};
return isFirstAnimator && this.mark.emit(enums_1.HOOK_EVENT.ANIMATION_START, animationEvent),
this.mark.emit(enums_1.HOOK_EVENT.ELEMENT_ANIMATION_START, animationEvent, element),
animator;
}
getAnimationState(element) {
const customState = (0, util_1.invokeFunctionType)(this.state, this.mark.parameters(), element.getDatum(), element);
return null != customState ? customState : element.diffState;
}
getAnimationUnit(timeline, element, index, elementCount, parameters) {
const timeSlices = [], startTime = (0, config_1.invokeAnimateSpec)(timeline.startTime, element, parameters), totalTime = (0,
config_1.invokeAnimateSpec)(timeline.totalTime, element, parameters), oneByOne = (0,
config_1.invokeAnimateSpec)(timeline.oneByOne, element, parameters), loop = (0,
config_1.invokeAnimateSpec)(timeline.loop, element, parameters);
let loopTime = 0;
timeline.timeSlices.forEach((timeSlice => {
var _a;
const delay = (0, config_1.invokeAnimateSpec)(timeSlice.delay, element, parameters), delayAfter = (0,
config_1.invokeAnimateSpec)(timeSlice.delayAfter, element, parameters), duration = null !== (_a = (0,
config_1.invokeAnimateSpec)(timeSlice.duration, element, parameters)) && void 0 !== _a ? _a : totalTime / elementCount, effects = (0,
vutils_1.array)(timeSlice.effects).map((effect => Object.assign({}, effect, {
customParameters: (0, config_1.invokeAnimateSpec)(effect.customParameters, element, parameters)
})));
timeSlices.push({
effects: effects,
duration: duration,
delay: delay,
delayAfter: delayAfter
}), loopTime += delay + duration + delayAfter;
}));
const oneByOneDelay = (0, vutils_1.isNumber)(oneByOne) ? oneByOne : !0 === oneByOne ? loopTime : 0;
return {
initialDelay: startTime,
loopCount: (0, vutils_1.isNumber)(loop) ? loop : !0 === loop ? 1 / 0 : 1,
loopDelay: oneByOneDelay * index,
loopDelayAfter: oneByOneDelay * (elementCount - index - 1),
loopAnimateDuration: loopTime,
loopDuration: loopTime + oneByOneDelay * (elementCount - 1),
totalTime: totalTime,
timeSlices: timeSlices
};
}
clearElementAnimation(element, clearElement = !0) {
this.animators.forEach((animators => {
animators.forEach((animator => {
animator.element === element && (animator.animationOptions.state === enums_1.DiffState.exit ? animator.stop("start", !1) : animator.stop("end", !1),
this.handleAnimatorEnd(animator, clearElement));
}));
})), this.elementRecorder.delete(element);
}
clearAllElements() {
const elements = this.mark.getAllElements();
elements && elements.forEach(((element, i) => {
this.clearElement(element, i === elements.length - 1);
}));
}
clearElement(element, updateMark = !0) {
this.clearElementAnimation(element), element.getGraphicItem() && (element.clearGraphicAttributes(),
element.diffState === enums_1.DiffState.exit && (element.isReserved = !1), updateMark && this.mark.cleanExitElements());
}
handleAnimatorEnd(animator, clearElement = !0) {
const element = animator.element, animationOptions = animator.animationOptions, animationState = animationOptions.state, isImmediateAnimation = animationState === constants_1.ImmediateAnimationState, stateAnimationCounts = this.elementRecorder.get(element).count;
stateAnimationCounts[animationState] -= 1, this.animators.set(animationState, this.animators.get(animationState).filter((ani => ani !== animator))),
0 === this.animators.get(animationState).length && this.animators.delete(animationState),
this.timelineCount[animationOptions.id] -= 1;
const isLastAnimator = 0 === this.timelineCount[animationOptions.id], originAnimationConfig = isImmediateAnimation ? this.immediateConfigs.find((config => config.id === animationOptions.id)).originConfig : this.configs.find((config => config.id === animationOptions.id)).originConfig;
isLastAnimator && (delete this.timelineCount[animationOptions.id], isImmediateAnimation && (this.immediateConfigs = this.immediateConfigs.filter((config => config.id !== animationOptions.id)))),
clearElement && (0 === Object.keys(this.timelineCount).length ? this.clearAllElements() : animationState === enums_1.DiffState.exit && 0 === stateAnimationCounts[enums_1.DiffState.exit] && this.clearElement(element));
const animationEvent = {
mark: this.mark,
animationState: animationState,
animationConfig: originAnimationConfig
};
isLastAnimator && this.mark.emit(enums_1.HOOK_EVENT.ANIMATION_END, animationEvent),
this.mark.emit(enums_1.HOOK_EVENT.ELEMENT_ANIMATION_END, animationEvent, element);
}
}
exports.Animate = Animate;
//# sourceMappingURL=animate.js.map