var Ease = use('ease') var time = use('time') var rate = 1/240 var TweenEngine = { tweens: [], add(tween) { this.tweens.push(tween) }, remove(tween) { this.tweens = this.tweens.filter(t => t != tween) }, update(dt) { var now = time.number() for (var tween of this.tweens.slice()) { tween._update(now) } $_.delay(_ => TweenEngine.update(), rate) } } function Tween(obj) { this.obj = obj this.startVals = {} this.endVals = {} this.duration = 0 this.easing = Ease.linear this.startTime = 0 this.onCompleteCallback = function() {} } Tween.prototype.to = function(props, duration) { for (var key in props) { this.startVals[key] = this.obj[key] this.endVals[key] = props[key] } this.duration = duration this.startTime = time.number() TweenEngine.add(this) return this } Tween.prototype.ease = function(easingFn) { this.easing = easingFn return this } Tween.prototype.onComplete = function(callback) { this.onCompleteCallback = callback return this } Tween.prototype.onUpdate = function(cb) { this.onUpdateCallback = cb return this } Tween.prototype._update = function(now) { var elapsed = now - this.startTime var t = Math.min(elapsed / this.duration, 1) var eased = this.easing(t) for (var key in this.endVals) { var start = this.startVals[key] var end = this.endVals[key] this.obj[key] = start + (end - start) * eased this.onUpdateCallback?.() } if (t == 1) { this.onCompleteCallback() TweenEngine.remove(this) } } function tween(obj) { return new Tween(obj) } $_.delay(_ => TweenEngine.update(), rate) return tween