kute.js/src/tween/tweenBase.js
2020-06-23 16:38:58 +00:00

175 lines
5 KiB
JavaScript

import KUTE from '../objects/kute.js'
import connect from '../objects/connect.js'
import onStart from '../objects/onStart.js'
import onComplete from '../objects/onComplete.js'
import defaultOptions from '../objects/defaultOptions.js'
import {Tick,Ticker,stop} from '../core/render.js'
import add from '../core/add.js'
import remove from '../core/remove.js'
import linkInterpolation from '../core/linkInterpolation.js'
// single Tween object construct
// TweenBase is meant to be use for pre-processed values
export default class TweenBase {
constructor(targetElement, startObject, endObject, options){
// element animation is applied to
this.element = targetElement;
this.playing = false;
this._startTime = null;
this._startFired = false;
this.valuesEnd = endObject; // valuesEnd
this.valuesStart = startObject; // valuesStart
// OPTIONS
options = options || {}
// internal option to process inline/computed style at start instead of init
// used by to() method and expects object : {} / false
this._resetStart = options.resetStart || 0;
// you can only set a core easing function as default
this._easing = typeof (options.easing) === 'function' ? options.easing : connect.processEasing(options.easing);
this._duration = options.duration || defaultOptions.duration; // duration option | default
this._delay = options.delay || defaultOptions.delay; // delay option | default
// set other options
for (const op in options) {
let internalOption = `_${op}`
if( !(internalOption in this ) ) this[internalOption] = options[op]
}
// callbacks should not be set as undefined
// this._onStart = options.onStart
// this._onUpdate = options.onUpdate
// this._onStop = options.onStop
// this._onComplete = options.onComplete
// queue the easing
const easingFnName = this._easing.name;
if (!onStart[easingFnName]) {
onStart[easingFnName] = function(prop){
!KUTE[prop] && prop === this._easing.name && (KUTE[prop] = this._easing)
}
}
return this;
}
// tween prototype
// queue tween object to main frame update
start(time) { // move functions that use the ticker outside the prototype to be in the same scope with it
// now it's a good time to start
add(this);
this.playing = true;
this._startTime = typeof time !== 'undefined' ? time : KUTE.Time();
this._startTime += this._delay;
if (!this._startFired) {
if (this._onStart) {
this._onStart.call(this);
}
// fire onStart actions
for (const obj in onStart) {
if (typeof (onStart[obj]) === 'function') {
onStart[obj].call(this,obj) // easing functions
} else {
for (const prop in onStart[obj]) {
onStart[obj][prop].call(this,prop);
}
}
}
// add interpolations
linkInterpolation.call(this)
this._startFired = true;
}
!Tick && Ticker();
return this;
}
stop() {
if (this.playing) {
remove(this);
this.playing = false;
if (this._onStop) {
this._onStop.call(this);
}
this.close();
}
return this;
}
close () {
// scroll|transformMatrix need this
for (const component in onComplete){
for (const toClose in onComplete[component]){
onComplete[component][toClose].call(this,toClose)
}
}
// when all animations are finished, stop ticking after ~3 frames
this._startFired = false;
stop.call(this);
}
chain(args) {
this._chain = []
this._chain = args.length ? args : this._chain.concat(args);
return this;
}
stopChainedTweens() {
this._chain && this._chain.length && this._chain.map(tw=>tw.stop())
}
update(time) {
time = time !== undefined ? time : KUTE.Time();
let elapsed, progress;
if ( time < this._startTime && this.playing ) { return true; }
elapsed = (time - this._startTime) / this._duration;
elapsed = (this._duration === 0 || elapsed > 1) ? 1 : elapsed;
// calculate progress
progress = this._easing(elapsed);
// render the update
for (const tweenProp in this.valuesEnd){
KUTE[tweenProp](this.element,this.valuesStart[tweenProp],this.valuesEnd[tweenProp],progress);
}
// fire the updateCallback
if (this._onUpdate) {
this._onUpdate.call(this);
}
if (elapsed === 1) {
// fire the complete callback
if (this._onComplete) {
this._onComplete.call(this);
}
// now we're sure no animation is running
this.playing = false;
//stop ticking when finished
this.close();
// start animating chained tweens
if (this._chain !== undefined && this._chain.length){
this._chain.map(tw=>tw.start())
}
return false;
}
return true;
}
}
// Update Tween Interface
connect.tween = TweenBase