@visactor/vrender-animate
Version:
This module provides a graph-based animation system for VRender.
225 lines (223 loc) • 16.7 kB
JavaScript
import { isArray, isFunction } from "@visactor/vutils";
export class AnimateExecutor {
static registerBuiltInAnimate(name, animate) {
AnimateExecutor.builtInAnimateMap[name] = animate;
}
constructor(target) {
this._animates = [], this._startCallbacks = [], this._endCallbacks = [], this._started = !1,
this._activeCount = 0, this._target = target;
}
onStart(cb) {
cb ? (this._startCallbacks.push(cb), this._started && this._activeCount > 0 && cb()) : this._startCallbacks.forEach((cb => {
cb();
}));
}
onEnd(cb) {
cb ? this._endCallbacks.push(cb) : this._endCallbacks.forEach((cb => {
cb();
}));
}
_trackAnimation(animate) {
this._animates.push(animate), this._activeCount++, 1 !== this._activeCount || this._started || (this._started = !0,
this.onStart()), animate.onEnd((() => {
this._activeCount--;
const index = this._animates.indexOf(animate);
index >= 0 && this._animates.splice(index, 1), 0 === this._activeCount && this._started && (this._started = !1,
this.onEnd());
}));
}
parseParams(params, isTimeline, child) {
var _a, _b;
const totalTime = this.resolveValue(params.totalTime, void 0, void 0), startTime = this.resolveValue(params.startTime, void 0, 0), parsedParams = Object.assign({}, params);
parsedParams.oneByOneDelay = 0, parsedParams.startTime = startTime, parsedParams.totalTime = totalTime;
const oneByOne = this.resolveValue(params.oneByOne, child, !1);
if (isTimeline) {
const timeSlices = parsedParams.timeSlices;
isArray(timeSlices) || (parsedParams.timeSlices = [ timeSlices ]);
let sliceTime = 0;
parsedParams.timeSlices = parsedParams.timeSlices.map((slice => {
const delay = this.resolveValue(slice.delay, child, 0), delayAfter = this.resolveValue(slice.delayAfter, child, 0), duration = this.resolveValue(slice.duration, child, 300);
return sliceTime += delay + duration + delayAfter, Object.assign(Object.assign({}, slice), {
delay: delay,
delayAfter: delayAfter,
duration: duration
});
}));
let oneByOneDelay = 0;
oneByOne && (oneByOneDelay = "number" == typeof oneByOne ? oneByOne : oneByOne ? sliceTime : 0),
parsedParams.oneByOneDelay = oneByOneDelay;
let scale = 1;
if (totalTime) {
const _totalTime = sliceTime + oneByOneDelay * (this._target.count - 2);
scale = totalTime ? totalTime / _totalTime : 1;
}
parsedParams.timeSlices = parsedParams.timeSlices.map((slice => {
let effects = slice.effects;
return Array.isArray(effects) || (effects = [ effects ]), Object.assign(Object.assign({}, slice), {
delay: slice.delay * scale,
delayAfter: slice.delayAfter * scale,
duration: slice.duration * scale,
effects: effects.map((effect => {
var _a, _b;
const custom = null !== (_a = effect.custom) && void 0 !== _a ? _a : AnimateExecutor.builtInAnimateMap[null !== (_b = effect.type) && void 0 !== _b ? _b : "fromTo"], customType = custom && isFunction(custom) ? /^class\s/.test(Function.prototype.toString.call(custom)) ? 1 : 2 : 0;
return Object.assign(Object.assign({}, effect), {
custom: custom,
customType: customType
});
}))
});
})), parsedParams.oneByOneDelay = oneByOneDelay * scale, parsedParams.startTime = startTime * scale;
} else {
const delay = this.resolveValue(params.delay, child, 0), delayAfter = this.resolveValue(params.delayAfter, child, 0), duration = this.resolveValue(params.duration, child, 300);
let oneByOneDelay = 0;
oneByOne && (oneByOneDelay = "number" == typeof oneByOne ? oneByOne : oneByOne ? delay + delayAfter + duration : 0),
parsedParams.oneByOneDelay = oneByOneDelay, parsedParams.custom = null !== (_a = params.custom) && void 0 !== _a ? _a : AnimateExecutor.builtInAnimateMap[null !== (_b = params.type) && void 0 !== _b ? _b : "fromTo"];
const customType = parsedParams.custom && isFunction(parsedParams.custom) ? /^class\s/.test(Function.prototype.toString.call(parsedParams.custom)) ? 1 : 2 : 0;
if (parsedParams.customType = customType, totalTime) {
const _totalTime = delay + delayAfter + duration + oneByOneDelay * (this._target.count - 2), scale = totalTime ? totalTime / _totalTime : 1;
parsedParams.delay = delay * scale, parsedParams.delayAfter = delayAfter * scale,
parsedParams.duration = duration * scale, parsedParams.oneByOneDelay = oneByOneDelay * scale,
parsedParams.startTime = startTime;
}
}
return parsedParams;
}
execute(params) {
Array.isArray(params) ? params.forEach((param => this._execute(param))) : this._execute(params);
}
_execute(params) {
if (params.selfOnly) return this._executeItem(params, this._target, 0, 1);
const isTimeline = "timeSlices" in params;
let filteredChildren;
isTimeline && params.partitioner && (filteredChildren = (null != filteredChildren ? filteredChildren : this._target.getChildren()).filter((child => {
var _a, _b;
return params.partitioner(null === (_b = null === (_a = child.context) || void 0 === _a ? void 0 : _a.data) || void 0 === _b ? void 0 : _b[0], child, {});
}))), isTimeline && params.sort && (filteredChildren = null != filteredChildren ? filteredChildren : this._target.getChildren(),
filteredChildren.sort(((a, b) => {
var _a, _b, _c, _d;
return params.sort(null === (_b = null === (_a = a.context) || void 0 === _a ? void 0 : _a.data) || void 0 === _b ? void 0 : _b[0], null === (_d = null === (_c = b.context) || void 0 === _c ? void 0 : _c.data) || void 0 === _d ? void 0 : _d[0], a, b, {});
})));
const cb = isTimeline ? (child, index, count) => {
const parsedParams = this.parseParams(params, isTimeline, child), animate = this.executeTimelineItem(parsedParams, child, index, count);
animate && this._trackAnimation(animate);
} : (child, index, count) => {
const parsedParams = this.parseParams(params, isTimeline, child), animate = this.executeTypeConfigItem(parsedParams, child, index, count);
animate && this._trackAnimation(animate);
};
filteredChildren ? filteredChildren.forEach(((child, index) => cb(child, index, filteredChildren.length))) : this._target.count <= 1 ? cb(this._target, 0, 1) : this._target.forEachChildren(((child, index) => cb(child, index, this._target.count - 1)));
}
executeTypeConfigItem(params, graphic, index, count) {
var _a, _b, _c, _d, _e, _f, _g, _h;
const {type: type = "fromTo", channel: channel, customParameters: customParameters, easing: easing = "linear", delay: delay = 0, delayAfter: delayAfter = 0, duration: duration = 300, startTime: startTime = 0, oneByOneDelay: oneByOneDelay = 0, loop: loop, bounce: bounce, priority: priority = 0, options: options, custom: custom, customType: customType, controlOptions: controlOptions} = params, animate = graphic.animate();
animate.priority = priority;
const delayValue = isFunction(delay) ? delay(null === (_b = null === (_a = graphic.context) || void 0 === _a ? void 0 : _a.data) || void 0 === _b ? void 0 : _b[0], graphic, {}) : delay, datum = null === (_d = null === (_c = graphic.context) || void 0 === _c ? void 0 : _c.data) || void 0 === _d ? void 0 : _d[0], indexKey = null === (_e = graphic.context) || void 0 === _e ? void 0 : _e.indexKey;
datum && indexKey && (index = null !== (_f = datum[indexKey]) && void 0 !== _f ? _f : index),
animate.startAt(startTime);
const wait = index * oneByOneDelay + delayValue;
wait > 0 && animate.wait(wait);
let parsedFromProps = null, props = params.to, from = params.from;
props || (parsedFromProps || (parsedFromProps = this.createPropsFromChannel(channel, graphic)),
props = parsedFromProps.props), from || (parsedFromProps || (parsedFromProps = this.createPropsFromChannel(channel, graphic)),
from = parsedFromProps.from), this._handleRunAnimate(animate, custom, customType, from, props, duration, easing, customParameters, controlOptions, options, type, graphic);
let totalDelay = 0;
oneByOneDelay && (totalDelay = oneByOneDelay * (count - index - 1));
const delayAfterValue = isFunction(delayAfter) ? delayAfter(null === (_h = null === (_g = graphic.context) || void 0 === _g ? void 0 : _g.data) || void 0 === _h ? void 0 : _h[0], graphic, {}) : delayAfter;
return delayAfterValue > 0 && (totalDelay += delayAfterValue), totalDelay > 0 && animate.wait(totalDelay),
loop && loop > 0 && animate.loop(loop), bounce && animate.bounce(!0), animate;
}
_handleRunAnimate(animate, custom, customType, from, props, duration, easing, customParameters, controlOptions, options, type, graphic) {
var _a, _b, _c, _d;
if (custom && customType) {
const customParams = Object.assign({
width: graphic.stage.width,
height: graphic.stage.height,
group: this._target.parent
}, this.resolveValue(customParameters, graphic)), objOptions = isFunction(options) ? options.call(null, null !== (_b = customParams && (null === (_a = customParams.data) || void 0 === _a ? void 0 : _a[0])) && void 0 !== _b ? _b : null === (_d = null === (_c = graphic.context) || void 0 === _c ? void 0 : _c.data) || void 0 === _d ? void 0 : _d[0], graphic, customParams) : options;
customParams.options = objOptions, customParams.controlOptions = controlOptions,
1 === customType ? this.createCustomAnimation(animate, custom, from, props, duration, easing, customParams) : 2 === customType && this.createCustomInterpolatorAnimation(animate, custom, props, duration, easing, customParams);
} else "to" === type ? animate.to(props, duration, easing) : "from" === type && animate.from(props, duration, easing);
}
executeTimelineItem(params, graphic, index, count) {
var _a, _b, _c, _d;
const {timeSlices: timeSlices, startTime: startTime = 0, loop: loop, bounce: bounce, oneByOneDelay: oneByOneDelay, priority: priority, controlOptions: controlOptions} = params, datum = null === (_b = null === (_a = graphic.context) || void 0 === _a ? void 0 : _a.data) || void 0 === _b ? void 0 : _b[0], indexKey = null === (_c = graphic.context) || void 0 === _c ? void 0 : _c.indexKey;
datum && indexKey && (index = null !== (_d = datum[indexKey]) && void 0 !== _d ? _d : index);
const animate = graphic.animate();
animate.priority = priority, animate.startAt(startTime), animate.wait(index * oneByOneDelay),
loop && loop > 0 && animate.loop(loop), bounce && animate.bounce(!0);
return (Array.isArray(timeSlices) ? timeSlices : [ timeSlices ]).forEach((slice => {
this.applyTimeSliceToAnimate(slice, animate, graphic, controlOptions);
})), oneByOneDelay && animate.wait(oneByOneDelay * (count - index - 1)), animate;
}
applyTimeSliceToAnimate(slice, animate, graphic, controlOptions) {
var _a, _b, _c, _d;
const {effects: effects, duration: duration = 300, delay: delay = 0, delayAfter: delayAfter = 0} = slice, delayValue = isFunction(delay) ? delay(null === (_b = null === (_a = graphic.context) || void 0 === _a ? void 0 : _a.data) || void 0 === _b ? void 0 : _b[0], graphic, {}) : delay, delayAfterValue = isFunction(delayAfter) ? delayAfter(null === (_d = null === (_c = graphic.context) || void 0 === _c ? void 0 : _c.data) || void 0 === _d ? void 0 : _d[0], graphic, {}) : delayAfter;
delayValue > 0 && animate.wait(delayValue);
(Array.isArray(effects) ? effects : [ effects ]).forEach((effect => {
var _a;
const {type: type = "fromTo", channel: channel, customParameters: customParameters, easing: easing = "linear", options: options} = effect;
let parsedFromProps = null, props = effect.to, from = effect.from;
props || (parsedFromProps || (parsedFromProps = this.createPropsFromChannel(channel, graphic)),
props = parsedFromProps.props), from || (parsedFromProps || (parsedFromProps = this.createPropsFromChannel(channel, graphic)),
from = parsedFromProps.from);
const custom = null !== (_a = effect.custom) && void 0 !== _a ? _a : AnimateExecutor.builtInAnimateMap[type], customType = effect.customType;
this._handleRunAnimate(animate, custom, customType, from, props, duration, easing, customParameters, controlOptions, options, type, graphic);
})), delayAfterValue > 0 && animate.wait(delayAfterValue);
}
createCustomInterpolatorAnimation(animate, interpolator, props, duration, easing, customParams) {
const from = {}, to = props;
Object.keys(to).forEach((key => {
from[key] = animate.target.getComputedAttribute(key);
})), animate.interpolateUpdateFunction = (from, to, ratio, step, target) => {
interpolator(ratio, from, to, step, target, animate.target, customParams);
}, animate.to(props, duration, easing);
}
createCustomAnimation(animate, CustomAnimateConstructor, from, props, duration, easing, customParams) {
const customAnimate = new CustomAnimateConstructor(from, props, duration, easing, customParams);
animate.play(customAnimate);
}
createPropsFromChannel(channel, graphic) {
const props = {};
let from = null;
return channel ? (Array.isArray(channel) ? channel.forEach((key => {
var _a, _b;
const value = null === (_b = null === (_a = graphic.context) || void 0 === _a ? void 0 : _a.diffAttrs) || void 0 === _b ? void 0 : _b[key];
void 0 !== value && (props[key] = value);
})) : Object.keys(channel).forEach((key => {
var _a, _b, _c, _d;
const config = channel[key];
void 0 !== config.to && ("function" == typeof config.to ? props[key] = config.to(null === (_b = null === (_a = graphic.context) || void 0 === _a ? void 0 : _a.data) || void 0 === _b ? void 0 : _b[0], graphic, {}) : props[key] = config.to),
void 0 !== config.from && (from || (from = {}), "function" == typeof config.from ? from[key] = config.from(null === (_d = null === (_c = graphic.context) || void 0 === _c ? void 0 : _c.data) || void 0 === _d ? void 0 : _d[0], graphic, {}) : from[key] = config.from);
})), {
from: from,
props: props
}) : {
from: from,
props: props
};
}
resolveValue(value, graphic, defaultValue) {
var _a, _b;
return void 0 === value ? defaultValue : "function" == typeof value && graphic ? value(null === (_b = null === (_a = graphic.context) || void 0 === _a ? void 0 : _a.data) || void 0 === _b ? void 0 : _b[0], graphic, {}) : value;
}
executeItem(params, graphic, index = 0, count = 1) {
return Array.isArray(params) ? params.map((param => this._executeItem(param, graphic, index, count))).filter(Boolean) : [ this._executeItem(params, graphic, index, count) ].filter(Boolean);
}
_executeItem(params, graphic, index = 0, count = 1) {
if (!graphic) return null;
const isTimeline = "timeSlices" in params;
let animate = null;
const parsedParams = this.parseParams(params, isTimeline);
return animate = isTimeline ? this.executeTimelineItem(parsedParams, graphic, index, count) : this.executeTypeConfigItem(parsedParams, graphic, index, count),
animate && this._trackAnimation(animate), animate;
}
stop(type) {
for (;this._animates.length > 0; ) {
const animate = this._animates.pop();
null == animate || animate.stop(type);
}
this._animates = [], this._activeCount = 0, this._started && (this._started = !1,
this.onEnd());
}
}
AnimateExecutor.builtInAnimateMap = {};
//# sourceMappingURL=animate-executor.js.map