Skip to content

Instantly share code, notes, and snippets.

@terion-name
Created May 4, 2015 11:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save terion-name/5e8afdfd1dcb45b742dc to your computer and use it in GitHub Desktop.
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
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
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;
})();
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