@visactor/vrender-core
Version:
## Description
398 lines (388 loc) • 17.4 kB
JavaScript
import { AnimateMode, AnimateStatus, AnimateStepType, AttributeUpdateType } from "../common/enums";
import { Easing } from "./easing";
import { Logger, max } from "@visactor/vutils";
import { defaultTimeline } from "./timeline";
import { Generator } from "../common/generator";
export class ACustomAnimate {
constructor(from, to, duration, easing, params) {
this.from = from, this.to = to, this.duration = duration, this.easing = easing,
this.params = params, this.updateCount = 0;
}
bind(target, subAni) {
this.target = target, this.subAnimate = subAni, this.onBind();
}
onBind() {}
onFirstRun() {}
onStart() {}
onEnd() {}
getEndProps() {
return this.to;
}
getFromProps() {
return this.from;
}
getMergedEndProps() {
var _a;
const thisEndProps = this.getEndProps();
return thisEndProps ? this._endProps === thisEndProps ? this._mergedEndProps : (this._endProps = thisEndProps,
void (this._mergedEndProps = Object.assign({}, null !== (_a = this.step.prev.getLastProps()) && void 0 !== _a ? _a : {}, thisEndProps))) : this.step.prev ? this.step.prev.getLastProps() : thisEndProps;
}
update(end, ratio, out) {
if (0 === this.updateCount) {
this.onFirstRun();
const props = this.step.getLastProps();
Object.keys(props).forEach((k => {
this.subAnimate.animate.validAttr(k) && (out[k] = props[k]);
}));
}
this.updateCount += 1, this.onUpdate(end, ratio, out), end && this.onEnd();
}
}
export class CbAnimate extends ACustomAnimate {
constructor(cb) {
super(null, null, 0, "linear"), this.cb = cb;
}
onUpdate(end, ratio, out) {}
onStart() {
this.cb();
}
}
export class Animate {
constructor(id = Generator.GenAutoIncrementId(), timeline = defaultTimeline, slience) {
this.id = id, this.timeline = timeline || defaultTimeline, this.status = AnimateStatus.INITIAL,
this.tailAnimate = new SubAnimate(this), this.subAnimates = [ this.tailAnimate ],
this.timeScale = 1, this.rawPosition = -1, this._startTime = 0, this._duringTime = 0,
this.timeline.addAnimate(this), this.slience = slience;
}
setTimeline(timeline) {
timeline !== this.timeline && (this.timeline.removeAnimate(this, !1), timeline.addAnimate(this));
}
getStartTime() {
return this._startTime;
}
getDuration() {
return this.subAnimates.reduce(((t, subAnimate) => t + subAnimate.totalDuration), 0);
}
after(animate) {
const t = animate.getDuration();
return this._startTime = t, this;
}
afterAll(list) {
let maxT = -1 / 0;
return list.forEach((a => {
maxT = max(a.getDuration(), maxT);
})), this._startTime = maxT, this;
}
parallel(animate) {
return this._startTime = animate.getStartTime(), this;
}
static AddInterpolate(name, cb) {
Animate.interpolateMap.set(name, cb);
}
play(customAnimate) {
if (this.tailAnimate.play(customAnimate), this.target) {
const stage = this.target.stage;
stage && stage.renderNextFrame();
}
return 1 === this.subAnimates.length && this.tailAnimate.totalDuration === customAnimate.duration && this.trySetAttribute(customAnimate.getFromProps(), customAnimate.mode),
this;
}
trySetAttribute(attr, mode = Animate.mode) {
attr && mode & AnimateMode.SET_ATTR_IMMEDIATELY && this.target.setAttributes && this.target.setAttributes(attr, !1, {
type: AttributeUpdateType.ANIMATE_PLAY
});
}
runCb(cb) {
const customAnimate = new CbAnimate((() => {
cb(this, customAnimate.step.prev);
}));
return this.tailAnimate.play(customAnimate), this;
}
customInterpolate(key, ratio, from, to, target, ret) {
const func = Animate.interpolateMap.get(key) || Animate.interpolateMap.get("");
return !!func && func(key, ratio, from, to, target, ret);
}
pause() {
this.status === AnimateStatus.RUNNING && (this.status = AnimateStatus.PAUSED);
}
resume() {
this.status === AnimateStatus.PAUSED && (this.status = AnimateStatus.RUNNING);
}
to(props, duration, easing, params) {
if (this.tailAnimate.to(props, duration, easing, params), this.target) {
const stage = this.target.stage;
stage && stage.renderNextFrame();
}
return this;
}
from(props, duration, easing, params) {
if (this.tailAnimate.from(props, duration, easing, params), this.target) {
const stage = this.target.stage;
stage && stage.renderNextFrame();
}
return this;
}
wait(duration) {
if (this.tailAnimate.wait(duration), this.target) {
const stage = this.target.stage;
stage && stage.renderNextFrame();
}
return this;
}
startAt(t) {
if (this.tailAnimate.startAt(t), this.target) {
const stage = this.target.stage;
stage && stage.renderNextFrame();
}
return this;
}
loop(l) {
if (this.tailAnimate.loop = l, this.target) {
const stage = this.target.stage;
stage && stage.renderNextFrame();
}
return this;
}
reversed(r) {
if (this.tailAnimate.reversed = r, this.target) {
const stage = this.target.stage;
stage && stage.renderNextFrame();
}
return this;
}
bounce(b) {
if (this.tailAnimate.bounce = b, this.target) {
const stage = this.target.stage;
stage && stage.renderNextFrame();
}
return this;
}
subAnimate() {
const sa = new SubAnimate(this, this.tailAnimate);
return this.tailAnimate = sa, this.subAnimates.push(sa), sa.bind(this.target), this;
}
getStartProps() {
return this.subAnimates[0].getStartProps();
}
getEndProps() {
return this.tailAnimate.getEndProps();
}
depreventAttr(key) {
this._preventAttrs && this._preventAttrs.delete(key);
}
preventAttr(key) {
this._preventAttrs || (this._preventAttrs = new Set), this._preventAttrs.add(key);
}
preventAttrs(keys) {
keys.forEach((key => this.preventAttr(key)));
}
validAttr(key) {
return !this._preventAttrs || !this._preventAttrs.has(key);
}
bind(target) {
return this.target = target, this.target.onAnimateBind && !this.slience && this.target.onAnimateBind(this),
this.subAnimates.forEach((sa => {
sa.bind(target);
})), this;
}
advance(delta) {
if (this._duringTime < this._startTime) {
if (this._duringTime + delta * this.timeScale < this._startTime) return void (this._duringTime += delta * this.timeScale);
delta = this._duringTime + delta * this.timeScale - this._startTime, this._duringTime = this._startTime;
}
this.status === AnimateStatus.INITIAL && (this.status = AnimateStatus.RUNNING, this._onStart && this._onStart.forEach((cb => cb())));
this.setPosition(Math.max(this.rawPosition, 0) + delta * this.timeScale) && this.status === AnimateStatus.RUNNING && (this.status = AnimateStatus.END,
this._onEnd && this._onEnd.forEach((cb => cb())));
}
setPosition(rawPosition) {
let sa, d = 0;
const prevRawPos = this.rawPosition, maxRawPos = this.subAnimates.reduce(((a, b) => a + b.totalDuration), 0);
rawPosition < 0 && (rawPosition = 0);
const end = rawPosition >= maxRawPos;
if (end && (rawPosition = maxRawPos), rawPosition === prevRawPos) return end;
for (let i = 0; i < this.subAnimates.length && (sa = this.subAnimates[i], !(d + sa.totalDuration >= rawPosition)); i++) d += sa.totalDuration,
sa = void 0;
return this.rawPosition = rawPosition, sa.setPosition(rawPosition - d), end;
}
onStart(cb) {
this._onStart || (this._onStart = []), this._onStart.push(cb);
}
onEnd(cb) {
this._onEnd || (this._onEnd = []), this._onEnd.push(cb);
}
onRemove(cb) {
this._onRemove || (this._onRemove = []), this._onRemove.push(cb);
}
onFrame(cb) {
this._onFrame || (this._onFrame = []), this._onFrame.push(cb);
}
release() {
this.status = AnimateStatus.END;
}
stop(nextVal) {
nextVal || this.target.onStop(), "start" === nextVal ? this.target.onStop(this.getStartProps()) : "end" === nextVal ? this.target.onStop(this.getEndProps()) : this.target.onStop(nextVal),
this.release();
}
}
Animate.mode = AnimateMode.NORMAL, Animate.interpolateMap = new Map;
export class SubAnimate {
get totalDuration() {
return this.calcAttr(), this._totalDuration + this._startAt;
}
constructor(animate, lastSubAnimate) {
this.rawPosition = -1, this.position = 0, this.loop = 0, this.duration = 0, this.animate = animate,
this.stepHead = new Step(0, 0, lastSubAnimate ? Object.assign({}, lastSubAnimate.stepTail.props) : {}),
this.stepTail = this.stepHead, this.dirty = !0, this._startAt = 0;
}
calcAttr() {
this.dirty && (this._totalDuration = this.duration * (this.loop + 1));
}
bind(target) {
return this.target = target, this;
}
play(customAnimate) {
let duration = customAnimate.duration;
(null == duration || duration < 0) && (duration = 0);
const easing = customAnimate.easing, easingFunc = "string" == typeof easing ? Easing[easing] : easing, step = this._addStep(duration, null, easingFunc);
return step.type = AnimateStepType.customAnimate, this._appendProps(customAnimate.getEndProps(), step, !1),
this._appendCustomAnimate(customAnimate, step), this;
}
to(props, duration, easing, params) {
(null == duration || duration < 0) && (duration = 0);
const easingFunc = "string" == typeof easing ? Easing[easing] : easing, step = this._addStep(duration, null, easingFunc);
return step.type = AnimateStepType.to, this._appendProps(props, step, !!params && params.tempProps),
step.propKeys || (step.propKeys = Object.keys(step.props)), params && params.noPreventAttrs || this.target.animates && this.target.animates.forEach((a => {
a.id !== this.animate.id && a.preventAttrs(step.propKeys);
})), this;
}
from(props, duration, easing, params) {
this.to(props, 0, easing, params);
const toProps = {};
this.stepTail.propKeys || (this.stepTail.propKeys = Object.keys(this.stepTail.props)),
this.stepTail.propKeys.forEach((k => {
toProps[k] = this.getLastPropByName(k, this.stepTail);
})), this.to(toProps, duration, easing, params), this.stepTail.type = AnimateStepType.from;
}
startAt(t) {
return t < 0 && (t = 0), this._startAt = t, this;
}
getStartProps() {
var _a;
return null === (_a = this.stepHead) || void 0 === _a ? void 0 : _a.props;
}
getEndProps() {
return this.stepTail.props;
}
getLastStep() {
return this._lastStep;
}
wait(duration) {
if (duration > 0) {
const step = this._addStep(+duration, null);
step.type = AnimateStepType.wait, step.prev.customAnimate ? step.props = step.prev.customAnimate.getEndProps() : step.props = step.prev.props,
this.target.onAddStep && this.target.onAddStep(step);
}
return this;
}
_addStep(duration, props, easingFunc) {
const step = new Step(this.duration, duration, props, easingFunc);
return this.duration += duration, this.stepTail.append(step), this.stepTail = step,
step;
}
_appendProps(props, step, tempProps) {
step.props = tempProps ? props : Object.assign({}, props);
let lastStep = step.prev;
const _props = step.props;
for (step.propKeys || (step.propKeys = Object.keys(step.props)), step.propKeys.forEach((k => {
void 0 === step.props[k] && (step.props[k] = this.target.getDefaultAttribute(k));
})); lastStep.prev; ) lastStep.props && (lastStep.propKeys || (lastStep.propKeys = Object.keys(lastStep.props)),
lastStep.propKeys.forEach((key => {
void 0 === _props[key] && (_props[key] = lastStep.props[key]);
}))), step.propKeys = Object.keys(step.props), lastStep = lastStep.prev;
const initProps = this.stepHead.props;
step.propKeys || (step.propKeys = Object.keys(_props)), step.propKeys.forEach((key => {
if (void 0 === initProps[key]) {
const parentAnimateInitProps = this.animate.getStartProps();
initProps[key] = parentAnimateInitProps[key] = this.target.getComputedAttribute(key);
}
})), this.target.onAddStep && this.target.onAddStep(step);
}
_appendCustomAnimate(customAnimate, step) {
step.customAnimate = customAnimate, customAnimate.step = step, customAnimate.bind(this.target, this);
}
setPosition(rawPosition) {
var _a;
const d = this.duration, loopCount = this.loop, prevRawPos = this.rawPosition;
let loop, position, end = !1;
const startAt = null !== (_a = this._startAt) && void 0 !== _a ? _a : 0;
if (rawPosition < 0 && (rawPosition = 0), rawPosition < startAt) return this.rawPosition = rawPosition,
!1;
if (rawPosition -= startAt, d <= 0 && (end = !0, d < 0)) return end;
if (loop = Math.floor(rawPosition / d), position = rawPosition - loop * d, end = rawPosition >= loopCount * d + d,
end && (position = d, loop = loopCount, rawPosition = position * loop + d), rawPosition === prevRawPos) return end;
const rev = !this.reversed != !(this.bounce && loop % 2);
return rev && (position = d - position), this._deltaPosition = position - this.position,
this.position = position, this.rawPosition = rawPosition + startAt, this.updatePosition(end, rev),
end;
}
updatePosition(end, rev) {
if (!this.stepHead) return;
let step = this.stepHead.next;
const position = this.position, duration = this.duration;
if (this.target && step) {
let stepNext = step.next;
for (;stepNext && stepNext.position <= position; ) step = stepNext, stepNext = step.next;
let ratio = end ? 0 === duration ? 1 : position / duration : (position - step.position) / step.duration;
step.easing && (ratio = step.easing(ratio)), this.tryCallCustomAnimateLifeCycle(step, this._lastStep || (rev ? this.stepTail : this.stepHead), rev),
this.updateTarget(step, ratio, end), this._lastStep = step, this.animate._onFrame && this.animate._onFrame.forEach((cb => cb(step, ratio)));
}
}
tryCallCustomAnimateLifeCycle(step, lastStep, rev) {
if (step !== lastStep) if (rev) {
let _step = lastStep.prev;
for (;_step && _step !== step; ) _step.customAnimate && (_step.customAnimate.onStart && _step.customAnimate.onStart(),
_step.customAnimate.onEnd && _step.customAnimate.onEnd()), _step = step.prev;
lastStep && lastStep.customAnimate && lastStep.customAnimate.onEnd && lastStep.customAnimate.onEnd(),
step && step.customAnimate && step.customAnimate.onStart && step.customAnimate.onStart();
} else {
let _step = lastStep.next;
for (;_step && _step !== step; ) _step.customAnimate && (_step.customAnimate.onStart && _step.customAnimate.onStart(),
_step.customAnimate.onEnd && _step.customAnimate.onEnd()), _step = _step.next;
lastStep && lastStep.customAnimate && lastStep.customAnimate.onEnd && lastStep.customAnimate.onEnd(),
step && step.customAnimate && step.customAnimate.onStart && step.customAnimate.onStart();
}
}
getLastPropByName(name, step) {
let lastStep = step.prev;
for (;lastStep; ) {
if (lastStep.props && void 0 !== lastStep.props[name]) return lastStep.props[name];
if (lastStep.customAnimate) {
const val = lastStep.customAnimate.getEndProps()[name];
if (void 0 !== val) return val;
}
lastStep = lastStep.prev;
}
return Logger.getInstance().warn("未知错误,step中找不到属性"), step.props[name];
}
updateTarget(step, ratio, end) {
null == step.props && null == step.customAnimate || this.target.onStep(this, this.animate, step, ratio, end);
}
}
class Step {
constructor(position, duration, props, easing) {
this.duration = duration, this.position = position, this.props = props, this.easing = easing;
}
append(step) {
step.prev = this, step.next = this.next, this.next = step;
}
getLastProps() {
let step = this.prev;
for (;step; ) {
if (step.props) return step.props;
if (step.customAnimate) return step.customAnimate.getMergedEndProps();
step = step.prev;
}
return null;
}
}
//# sourceMappingURL=animate.js.map