117 lines
3.0 KiB
JavaScript
117 lines
3.0 KiB
JavaScript
import {compose, EventEmitter} from '@avocado/core';
|
|
|
|
import easingFunctions from './easing';
|
|
|
|
const decorate = compose(
|
|
EventEmitter,
|
|
);
|
|
|
|
export default class TransitionResult extends decorate(class {}) {
|
|
|
|
constructor(subject, props, duration, easing) {
|
|
super();
|
|
// Speed might not get passed. If it doesn't, default to 100
|
|
// milliseconds.
|
|
this.duration = parseFloat(duration || .1);
|
|
this.elapsed = 0;
|
|
this._isEmittingProgress = false;
|
|
this.props = props;
|
|
this.subject = subject;
|
|
|
|
if ('function' === typeof easing) {
|
|
this.easing = easing;
|
|
}
|
|
// If easing isn't passed in as a function, attempt to look it up
|
|
// as a string key into Transition.easing. If that fails, then
|
|
// default to 'easeOutQuad'.
|
|
else {
|
|
this.easing = easingFunctions[easing] || easingFunctions['easeOutQuad'];
|
|
}
|
|
|
|
this.original = {};
|
|
this.change = {};
|
|
for (const i in this.props) {
|
|
const value = this.subject[i];
|
|
this.original[i] = value;
|
|
this.change[i] = this.props[i] - value;
|
|
}
|
|
|
|
// Set up the transition object.
|
|
this.promise = new Promise((resolve, reject) => {
|
|
this.once('stopped', () => resolve());
|
|
});
|
|
}
|
|
|
|
get isEmittingProgress() {
|
|
return this._isEmittingProgress;
|
|
}
|
|
|
|
set isEmittingProgress(isEmittingProgress) {
|
|
this._isEmittingProgress = isEmittingProgress;
|
|
}
|
|
|
|
// Immediately finish the transition. This will leave the object
|
|
// in the fully transitioned state.
|
|
skipTransition() {
|
|
|
|
// Just trick it into thinking the time passed and do one last
|
|
// tick.
|
|
this.elapsed = this.duration;
|
|
this.tick(0);
|
|
}
|
|
|
|
// Immediately stop the transition. This will leave the object in
|
|
// its current state; potentially partially transitioned.
|
|
stopTransition() {
|
|
|
|
// Let any listeners know that the transition is complete.
|
|
if (this._isEmittingProgress) {
|
|
this.emit('progress', [this.elapsed, this.duration]);
|
|
}
|
|
this.emit('stopped');
|
|
}
|
|
|
|
// Tick callback. Called repeatedly while this transition is
|
|
// running.
|
|
tick(elapsed) {
|
|
|
|
// Update the transition's elapsed time.
|
|
this.elapsed += elapsed;
|
|
|
|
// If we've overshot the duration, we'll fix it up here, so
|
|
// things never transition too far (through the end point).
|
|
if (this.elapsed >= this.duration) {
|
|
this.elapsed = this.duration;
|
|
for (const i in this.change) {
|
|
if (this.change[i]) {
|
|
this.subject[i] = this.props[i];
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
|
|
// Do easing for each property that actually changed.
|
|
for (const i in this.change) {
|
|
if (this.change[i]) {
|
|
this.subject[i] = this.easing(
|
|
this.elapsed,
|
|
this.original[i],
|
|
this.change[i],
|
|
this.duration
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Stop if we're done.
|
|
if (this.elapsed === this.duration) {
|
|
this.stopTransition();
|
|
}
|
|
else {
|
|
if (this._isEmittingProgress) {
|
|
this.emit('progress', [this.elapsed, this.duration]);
|
|
}
|
|
}
|
|
}
|
|
}
|