Created
May 4, 2015 11:26
-
-
Save terion-name/5e8afdfd1dcb45b742dc to your computer and use it in GitHub Desktop.
Simple object properties (including nested) animator for timed frame animations with easings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class Animator | |
constructor: (obj)-> | |
@obj = obj | |
@stack = [] | |
console.log this | |
add: (property, to, duration, callback, easing)-> | |
@stack.push { | |
property: property | |
from: parseFloat @getProperty @obj, property | |
to: parseFloat to | |
duration: duration or 250 | |
callback: callback | |
easing: easing | |
} | |
getProperty: (o, s) -> | |
s = s.replace(/\[(\w+)\]/g, '.$1') # convert indexes to properties | |
s = s.replace(/^\./, '') # strip a leading dot | |
a = s.split('.') | |
i = 0 | |
n = a.length | |
while i < n | |
k = a[i] | |
if o[k]? | |
o = o[k] | |
else | |
return | |
++i | |
return o | |
setProperty: (o, s, v) -> | |
s = s.replace(/\[(\w+)\]/g, '.$1') | |
# convert indexes to properties | |
s = s.replace(/^\./, '') | |
# strip a leading dot | |
a = s.split('.') | |
i = 0 | |
n = a.length | |
while i < n-1 | |
k = a[i] | |
if o[k]? | |
o = o[k] | |
else | |
return | |
++i | |
o[a[i]] = v | |
run: (frame)-> | |
# frame.count - the number of times the frame event was fired | |
# frame.time - The total amount of time passed since the first frame event in seconds | |
# frame.delta - The time passed in seconds since the last frame event | |
currentTime = new Date() | |
index = 0 | |
for task in @stack | |
if task | |
task.startTime = currentTime unless task.startTime? | |
delta = currentTime - task.startTime | |
#console.log task.property | |
#console.log @getProperty(@obj, task.property) | |
if delta > task.duration | |
@setProperty(@obj, task.property, task.to) | |
task.callback() if task.callback? | |
@stack.splice(index, 1) | |
else | |
if task.easing and Animator.easings[task.easing]? | |
value = Animator.easings[task.easing](null, delta, task.from, task.to - task.from, task.duration) | |
else | |
value = task.from + ((task.to - task.from) / task.duration * delta) | |
@setProperty(@obj, task.property, value) | |
++index | |
@easings: | |
# jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/ | |
# t: current time, b: begInnIng value, c: change In value, d: duration | |
easeInQuad: (x, t, b, c, d) -> | |
c * (t /= d) * t + b | |
easeOutQuad: (x, t, b, c, d) -> | |
-c * (t /= d) * (t - 2) + b | |
easeInOutQuad: (x, t, b, c, d) -> | |
if (t /= d / 2) < 1 | |
return c / 2 * t * t + b | |
-c / 2 * (--t * (t - 2) - 1) + b | |
easeInCubic: (x, t, b, c, d) -> | |
c * (t /= d) * t * t + b | |
easeOutCubic: (x, t, b, c, d) -> | |
c * ((t = t / d - 1) * t * t + 1) + b | |
easeInOutCubic: (x, t, b, c, d) -> | |
if (t /= d / 2) < 1 | |
return c / 2 * t * t * t + b | |
c / 2 * ((t -= 2) * t * t + 2) + b | |
easeInQuart: (x, t, b, c, d) -> | |
c * (t /= d) * t * t * t + b | |
easeOutQuart: (x, t, b, c, d) -> | |
-c * ((t = t / d - 1) * t * t * t - 1) + b | |
easeInOutQuart: (x, t, b, c, d) -> | |
if (t /= d / 2) < 1 | |
return c / 2 * t * t * t * t + b | |
-c / 2 * ((t -= 2) * t * t * t - 2) + b | |
easeInQuint: (x, t, b, c, d) -> | |
c * (t /= d) * t * t * t * t + b | |
easeOutQuint: (x, t, b, c, d) -> | |
c * ((t = t / d - 1) * t * t * t * t + 1) + b | |
easeInOutQuint: (x, t, b, c, d) -> | |
if (t /= d / 2) < 1 | |
return c / 2 * t * t * t * t * t + b | |
c / 2 * ((t -= 2) * t * t * t * t + 2) + b | |
easeInSine: (x, t, b, c, d) -> | |
-c * Math.cos(t / d * Math.PI / 2) + c + b | |
easeOutSine: (x, t, b, c, d) -> | |
c * Math.sin(t / d * Math.PI / 2) + b | |
easeInOutSine: (x, t, b, c, d) -> | |
-c / 2 * (Math.cos(Math.PI * t / d) - 1) + b | |
easeInExpo: (x, t, b, c, d) -> | |
if t == 0 then b else c * 2 ** (10 * (t / d - 1)) + b | |
easeOutExpo: (x, t, b, c, d) -> | |
if t == d then b + c else c * (-2 ** (-10 * t / d) + 1) + b | |
easeInOutExpo: (x, t, b, c, d) -> | |
if t == 0 | |
return b | |
if t == d | |
return b + c | |
if (t /= d / 2) < 1 | |
return c / 2 * 2 ** (10 * (t - 1)) + b | |
c / 2 * (-2 ** (-10 * --t) + 2) + b | |
easeInCirc: (x, t, b, c, d) -> | |
-c * (Math.sqrt(1 - (t /= d) * t) - 1) + b | |
easeOutCirc: (x, t, b, c, d) -> | |
c * Math.sqrt(1 - (t = t / d - 1) * t) + b | |
easeInOutCirc: (x, t, b, c, d) -> | |
if (t /= d / 2) < 1 | |
return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b | |
c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b | |
easeInElastic: (x, t, b, c, d) -> | |
s = 1.70158 | |
p = 0 | |
a = c | |
if t == 0 | |
return b | |
if (t /= d) == 1 | |
return b + c | |
if !p | |
p = d * .3 | |
if a < Math.abs(c) | |
a = c | |
s = p / 4 | |
else | |
s = p / (2 * Math.PI) * Math.asin(c / a) | |
-(a * 2 ** (10 * (t -= 1)) * Math.sin((t * d - s) * 2 * Math.PI / p)) + b | |
easeOutElastic: (x, t, b, c, d) -> | |
s = 1.70158 | |
p = 0 | |
a = c | |
if t == 0 | |
return b | |
if (t /= d) == 1 | |
return b + c | |
if !p | |
p = d * .3 | |
if a < Math.abs(c) | |
a = c | |
s = p / 4 | |
else | |
s = p / (2 * Math.PI) * Math.asin(c / a) | |
a * 2 ** (-10 * t) * Math.sin((t * d - s) * 2 * Math.PI / p) + c + b | |
easeInOutElastic: (x, t, b, c, d) -> | |
s = 1.70158 | |
p = 0 | |
a = c | |
if t == 0 | |
return b | |
if (t /= d / 2) == 2 | |
return b + c | |
if !p | |
p = d * .3 * 1.5 | |
if a < Math.abs(c) | |
a = c | |
s = p / 4 | |
else | |
s = p / (2 * Math.PI) * Math.asin(c / a) | |
if t < 1 | |
return -.5 * a * 2 ** (10 * (t -= 1)) * Math.sin((t * d - s) * 2 * Math.PI / p) + b | |
a * 2 ** (-10 * (t -= 1)) * Math.sin((t * d - s) * 2 * Math.PI / p) * .5 + c + b | |
easeInBack: (x, t, b, c, d, s) -> | |
if s == undefined | |
s = 1.70158 | |
c * (t /= d) * t * ((s + 1) * t - s) + b | |
easeOutBack: (x, t, b, c, d, s) -> | |
if s == undefined | |
s = 1.70158 | |
c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b | |
easeInOutBack: (x, t, b, c, d, s) -> | |
if s == undefined | |
s = 1.70158 | |
if (t /= d / 2) < 1 | |
return c / 2 * t * t * (((s *= 1.525) + 1) * t - s) + b | |
c / 2 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b | |
easeInBounce: (x, t, b, c, d) => | |
c - @easings.easeOutBounce(x, d - t, 0, c, d) + b | |
easeOutBounce: (x, t, b, c, d) -> | |
if (t /= d) < 1 / 2.75 | |
c * 7.5625 * t * t + b | |
else if t < 2 / 2.75 | |
c * (7.5625 * (t -= 1.5 / 2.75) * t + .75) + b | |
else if t < 2.5 / 2.75 | |
c * (7.5625 * (t -= 2.25 / 2.75) * t + .9375) + b | |
else | |
c * (7.5625 * (t -= 2.625 / 2.75) * t + .984375) + b | |
easeInOutBounce: (x, t, b, c, d) => | |
if t < d / 2 | |
return @easings.easeInBounce(x, t * 2, 0, c, d) * .5 + b | |
@easings.easeOutBounce(x, t * 2 - d, 0, c, d) * .5 + c * .5 + b |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Animator; | |
Animator = (function() { | |
function Animator(obj) { | |
this.obj = obj; | |
this.stack = []; | |
console.log(this); | |
} | |
Animator.prototype.add = function(property, to, duration, callback, easing) { | |
return this.stack.push({ | |
property: property, | |
from: parseFloat(this.getProperty(this.obj, property)), | |
to: parseFloat(to), | |
duration: duration || 250, | |
callback: callback, | |
easing: easing | |
}); | |
}; | |
Animator.prototype.getProperty = function(o, s) { | |
var a, i, k, n; | |
s = s.replace(/\[(\w+)\]/g, '.$1'); | |
s = s.replace(/^\./, ''); | |
a = s.split('.'); | |
i = 0; | |
n = a.length; | |
while (i < n) { | |
k = a[i]; | |
if (o[k] != null) { | |
o = o[k]; | |
} else { | |
return; | |
} | |
++i; | |
} | |
return o; | |
}; | |
Animator.prototype.setProperty = function(o, s, v) { | |
var a, i, k, n; | |
s = s.replace(/\[(\w+)\]/g, '.$1'); | |
s = s.replace(/^\./, ''); | |
a = s.split('.'); | |
i = 0; | |
n = a.length; | |
while (i < n - 1) { | |
k = a[i]; | |
if (o[k] != null) { | |
o = o[k]; | |
} else { | |
return; | |
} | |
++i; | |
} | |
return o[a[i]] = v; | |
}; | |
Animator.prototype.run = function(frame) { | |
var currentTime, delta, index, j, len, ref, results, task, value; | |
currentTime = new Date(); | |
index = 0; | |
ref = this.stack; | |
results = []; | |
for (j = 0, len = ref.length; j < len; j++) { | |
task = ref[j]; | |
if (task) { | |
if (task.startTime == null) { | |
task.startTime = currentTime; | |
} | |
delta = currentTime - task.startTime; | |
if (delta > task.duration) { | |
this.setProperty(this.obj, task.property, task.to); | |
if (task.callback != null) { | |
task.callback(); | |
} | |
this.stack.splice(index, 1); | |
} else { | |
if (task.easing && (Animator.easings[task.easing] != null)) { | |
value = Animator.easings[task.easing](null, delta, task.from, task.to - task.from, task.duration); | |
} else { | |
value = task.from + ((task.to - task.from) / task.duration * delta); | |
} | |
this.setProperty(this.obj, task.property, value); | |
} | |
results.push(++index); | |
} else { | |
results.push(void 0); | |
} | |
} | |
return results; | |
}; | |
Animator.easings = { | |
easeInQuad: function(x, t, b, c, d) { | |
return c * (t /= d) * t + b; | |
}, | |
easeOutQuad: function(x, t, b, c, d) { | |
return -c * (t /= d) * (t - 2) + b; | |
}, | |
easeInOutQuad: function(x, t, b, c, d) { | |
if ((t /= d / 2) < 1) { | |
return c / 2 * t * t + b; | |
} | |
return -c / 2 * (--t * (t - 2) - 1) + b; | |
}, | |
easeInCubic: function(x, t, b, c, d) { | |
return c * (t /= d) * t * t + b; | |
}, | |
easeOutCubic: function(x, t, b, c, d) { | |
return c * ((t = t / d - 1) * t * t + 1) + b; | |
}, | |
easeInOutCubic: function(x, t, b, c, d) { | |
if ((t /= d / 2) < 1) { | |
return c / 2 * t * t * t + b; | |
} | |
return c / 2 * ((t -= 2) * t * t + 2) + b; | |
}, | |
easeInQuart: function(x, t, b, c, d) { | |
return c * (t /= d) * t * t * t + b; | |
}, | |
easeOutQuart: function(x, t, b, c, d) { | |
return -c * ((t = t / d - 1) * t * t * t - 1) + b; | |
}, | |
easeInOutQuart: function(x, t, b, c, d) { | |
if ((t /= d / 2) < 1) { | |
return c / 2 * t * t * t * t + b; | |
} | |
return -c / 2 * ((t -= 2) * t * t * t - 2) + b; | |
}, | |
easeInQuint: function(x, t, b, c, d) { | |
return c * (t /= d) * t * t * t * t + b; | |
}, | |
easeOutQuint: function(x, t, b, c, d) { | |
return c * ((t = t / d - 1) * t * t * t * t + 1) + b; | |
}, | |
easeInOutQuint: function(x, t, b, c, d) { | |
if ((t /= d / 2) < 1) { | |
return c / 2 * t * t * t * t * t + b; | |
} | |
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b; | |
}, | |
easeInSine: function(x, t, b, c, d) { | |
return -c * Math.cos(t / d * Math.PI / 2) + c + b; | |
}, | |
easeOutSine: function(x, t, b, c, d) { | |
return c * Math.sin(t / d * Math.PI / 2) + b; | |
}, | |
easeInOutSine: function(x, t, b, c, d) { | |
return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b; | |
}, | |
easeInExpo: function(x, t, b, c, d) { | |
if (t === 0) { | |
return b; | |
} else { | |
return c * Math.pow(2, 10 * (t / d - 1)) + b; | |
} | |
}, | |
easeOutExpo: function(x, t, b, c, d) { | |
if (t === d) { | |
return b + c; | |
} else { | |
return c * (-(Math.pow(2, -10 * t / d)) + 1) + b; | |
} | |
}, | |
easeInOutExpo: function(x, t, b, c, d) { | |
if (t === 0) { | |
return b; | |
} | |
if (t === d) { | |
return b + c; | |
} | |
if ((t /= d / 2) < 1) { | |
return c / 2 * Math.pow(2, 10 * (t - 1)) + b; | |
} | |
return c / 2 * (-(Math.pow(2, -10 * --t)) + 2) + b; | |
}, | |
easeInCirc: function(x, t, b, c, d) { | |
return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b; | |
}, | |
easeOutCirc: function(x, t, b, c, d) { | |
return c * Math.sqrt(1 - (t = t / d - 1) * t) + b; | |
}, | |
easeInOutCirc: function(x, t, b, c, d) { | |
if ((t /= d / 2) < 1) { | |
return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b; | |
} | |
return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b; | |
}, | |
easeInElastic: function(x, t, b, c, d) { | |
var a, p, s; | |
s = 1.70158; | |
p = 0; | |
a = c; | |
if (t === 0) { | |
return b; | |
} | |
if ((t /= d) === 1) { | |
return b + c; | |
} | |
if (!p) { | |
p = d * .3; | |
} | |
if (a < Math.abs(c)) { | |
a = c; | |
s = p / 4; | |
} else { | |
s = p / (2 * Math.PI) * Math.asin(c / a); | |
} | |
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * 2 * Math.PI / p)) + b; | |
}, | |
easeOutElastic: function(x, t, b, c, d) { | |
var a, p, s; | |
s = 1.70158; | |
p = 0; | |
a = c; | |
if (t === 0) { | |
return b; | |
} | |
if ((t /= d) === 1) { | |
return b + c; | |
} | |
if (!p) { | |
p = d * .3; | |
} | |
if (a < Math.abs(c)) { | |
a = c; | |
s = p / 4; | |
} else { | |
s = p / (2 * Math.PI) * Math.asin(c / a); | |
} | |
return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * 2 * Math.PI / p) + c + b; | |
}, | |
easeInOutElastic: function(x, t, b, c, d) { | |
var a, p, s; | |
s = 1.70158; | |
p = 0; | |
a = c; | |
if (t === 0) { | |
return b; | |
} | |
if ((t /= d / 2) === 2) { | |
return b + c; | |
} | |
if (!p) { | |
p = d * .3 * 1.5; | |
} | |
if (a < Math.abs(c)) { | |
a = c; | |
s = p / 4; | |
} else { | |
s = p / (2 * Math.PI) * Math.asin(c / a); | |
} | |
if (t < 1) { | |
return -.5 * a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * 2 * Math.PI / p) + b; | |
} | |
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * 2 * Math.PI / p) * .5 + c + b; | |
}, | |
easeInBack: function(x, t, b, c, d, s) { | |
if (s === void 0) { | |
s = 1.70158; | |
} | |
return c * (t /= d) * t * ((s + 1) * t - s) + b; | |
}, | |
easeOutBack: function(x, t, b, c, d, s) { | |
if (s === void 0) { | |
s = 1.70158; | |
} | |
return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b; | |
}, | |
easeInOutBack: function(x, t, b, c, d, s) { | |
if (s === void 0) { | |
s = 1.70158; | |
} | |
if ((t /= d / 2) < 1) { | |
return c / 2 * t * t * (((s *= 1.525) + 1) * t - s) + b; | |
} | |
return c / 2 * ((t -= 2) * t * (((s *= 1.525) + 1) * t + s) + 2) + b; | |
}, | |
easeInBounce: function(x, t, b, c, d) { | |
return c - Animator.easings.easeOutBounce(x, d - t, 0, c, d) + b; | |
}, | |
easeOutBounce: function(x, t, b, c, d) { | |
if ((t /= d) < 1 / 2.75) { | |
return c * 7.5625 * t * t + b; | |
} else if (t < 2 / 2.75) { | |
return c * (7.5625 * (t -= 1.5 / 2.75) * t + .75) + b; | |
} else if (t < 2.5 / 2.75) { | |
return c * (7.5625 * (t -= 2.25 / 2.75) * t + .9375) + b; | |
} else { | |
return c * (7.5625 * (t -= 2.625 / 2.75) * t + .984375) + b; | |
} | |
}, | |
easeInOutBounce: function(x, t, b, c, d) { | |
if (t < d / 2) { | |
return Animator.easings.easeInBounce(x, t * 2, 0, c, d) * .5 + b; | |
} | |
return Animator.easings.easeOutBounce(x, t * 2 - d, 0, c, d) * .5 + c * .5 + b; | |
} | |
}; | |
return Animator; | |
})(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SomeClass | |
constructor: (@paperjs_project)-> | |
@ring = new @paperjs_project.Path.Circle | |
center: [200, 200] | |
radius: 100 | |
@ringAnimator = new Animator @ring | |
@paperjs_project.view.on 'frame', (e)=> @ringAnimator.run(e) | |
@animator.add 'center._x', 400, 250, (-> console.log('center._x ready')), 'easeOutCubic' | |
@animator.add 'center._y', 400, 250, (-> console.log('center._y ready')), 'easeOutBounce' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment