UNPKG

act

Version:

An animation engine for the canvas and dom

743 lines (680 loc) 21 kB
// Generated by CoffeeScript 1.3.3 (function() { var Ease, EaseIn, EaseIn2, EaseInOut, EaseInOut2, EaseLinear, EaseOut, EaseOut2, EaseOutBounce, Implicit, Scheduler, Task, Transaction, TransactionBuilder, act, actGenerator, linearAnimator, mapAnimatorFromName, setAtEndAnimator, _, _actInstanceID, _default, _extendEventFunctions, _final, _getTime, _interp, _maxEndTime, _objectFromKeysAndValues, _requestAnimFrame, _setTimeout, _smoothTicks, __slice = [].slice; _ = require('underscore'); _actInstanceID = 1; actGenerator = function() { var act, k, v, _defaultInterpolationForValue; act = function(obj, dest, options) { var k, scheduler, task, _results; if (options == null) { options = {}; } scheduler = act._scheduler; if (obj instanceof Implicit) { obj = obj._obj; } if (options.animator) { task = new Task(obj, dest, act, options); return scheduler.addTask(task); } else { _results = []; for (k in dest) { task = new Task(obj, _.pick(dest, k), act, options); _results.push(scheduler.addTask(task)); } return _results; } }; act.instanceID = _actInstanceID; _actInstanceID++; for (k in Ease) { v = Ease[k]; act[k] = v; } act.animator = function(name) { return mapAnimatorFromName[name]; }; _extendEventFunctions(act); act.clone = actGenerator; act.rate = 1; act.tickInterval = 1 / 60; act._transactionBuilders = []; act.play = function() { var state, ticker; act._forceRenderNextTick = true; if (!act._playState) { act._playState = state = { lastTick: _getTime() }; ticker = function() { var changed, timeCurrent, timeElapsed; if (act._playState === state) { _setTimeout(act.tickInterval, ticker); timeCurrent = _getTime(); timeElapsed = timeCurrent - state.lastTick; changed = act._tick(timeElapsed * act.rate); state.lastTick = timeCurrent; if (changed || act._forceRenderNextTick) { act._forceRenderNextTick = false; return act.trigger('render'); } } }; return _setTimeout(act.tickInterval, ticker); } }; act._tick = function(dt) { return act._rootScheduler.tick(dt); }; act.fastForward = act._tick; act.stop = function() { return act._playState = null; }; act.begin = function(options) { var builder; builder = new TransactionBuilder(options); act._transactionBuilders.push(builder); return builder; }; Object.defineProperty(act, '_scheduler', { get: function() { var len; len = act._transactionBuilders.length; if (len) { return act._transactionBuilders[len - 1]; } else { return act._rootScheduler; } }, enumerable: false }); act.commit = function() { var builderTop, scheduler; builderTop = act._transactionBuilders.pop(); scheduler = this._scheduler; return scheduler.addTask(builderTop.transaction()); }; act.renderNextTick = function() { return act._forceRenderNextTick = true; }; act.property = function(obj, key, val, options) { var setter, store; if ((_.isUndefined(options)) && obj.actOptions) { options = _.isFunction(obj.actOptions) ? obj.actOptions()[key] : obj.actOptions[key]; } store = { value: val }; setter = function(v) { return act(store, { value: v }, options); }; setter._actStore = store; return Object.defineProperty(obj, key, { get: function() { return store.value; }, set: setter }); }; act._implicitPropertyStore = function(obj, key) { var d, _ref; d = Object.getOwnPropertyDescriptor(obj, key); return d != null ? (_ref = d.set) != null ? _ref._actStore : void 0 : void 0; }; act.properties = function(obj, properties, options) { var _results; _results = []; for (k in properties) { v = properties[k]; _results.push(act.property(obj, k, v, options != null ? options[k] : void 0)); } return _results; }; act.implicit = function(obj, options) { if (options == null) { options = {}; } options = _.clone(options); if (obj.actOptions) { _.defaults(options, (_.isFunction(obj.actOptions) ? obj.actOptions() : obj.actOptions)); } for (k in obj) { v = obj[k]; if (options[k] === null) { options[k] = { animator: null }; } else if (options[k] === void 0) { options[k] = { animator: _defaultInterpolationForValue(v) }; } else if (_.isString(options[k] || _.isFunction(options[k]))) { options[k] = { animator: options[k] }; } else { throw new Error("act: animator is not string or function"); } } return new Implicit(act, obj, options); }; _defaultInterpolationForValue = function(v) { if (_.isNumber(v)) { return linearAnimator; } else { return setAtEndAnimator; } }; act._rootScheduler = new Scheduler(); act.play(); return act; }; linearAnimator = function(obj, destinations) { var finals, initials, k, ops, v; initials = {}; finals = {}; for (k in destinations) { v = destinations[k]; if (_.isObject(v)) { ops = _.keys(v); if (ops.length !== 1) { throw new Error("act: expected only one operator (" + ops + ") for key " + k); } destinations[k] = { op: ops[0], value: v[ops[0]] }; } else { destinations[k] = { op: '=', value: v }; } initials[k] = obj[k]; finals[k] = _final(initials[k], destinations[k]['op'], destinations[k]['value']); } return function(t) { var changed; changed = {}; for (k in finals) { v = finals[k]; changed[k] = _interp(initials[k], finals[k], t); } return changed; }; }; _interp = function(a, b, t) { return a * (1.0 - t) + b * t; }; _final = function(initial, op, value) { if (op === '==' || op === '=') { return value; } else if (op === '/=') { return initial / value; } else if (op === '*=') { return initial * value; } else if (op === '-=') { return initial - value; } else if (op === '+=') { return initial + value; } }; setAtEndAnimator = function(obj, destinations) { var finals, initials; initials = _.pick.apply(_, [obj].concat(__slice.call(_.keys(destinations)))); finals = _.clone(destinations); return function(t) { return _.clone((t < 1 ? initials : finals)); }; }; mapAnimatorFromName = { 'setAtEnd': setAtEndAnimator, 'linear': linearAnimator }; Task = (function() { function Task(obj, destination, act, options) { var k, v, _ref; this.obj = obj; this.destination = destination; this.act = act; if (options == null) { options = {}; } _ref = this.destination; for (k in _ref) { v = _ref[k]; if (_.isFunction(v)) { throw new Error('act: functions cannot be animated'); } } if ((options.duration != null) && options.duration <= 0) { throw new Error('act: duration must be positive'); } this.duration = options.duration || 1; this.started = options.started || function() {}; this.completed = options.completed || function() {}; this._easing = options.easing || EaseLinear; this._animator = _.isString(options.animator) ? mapAnimatorFromName[options.animator] : options.animator || linearAnimator; this.startTime = 0; this._elapsed = 0; } Task.prototype.update = function(time) { var eased, elapsed, extender, k, store, v; elapsed = time - this.startTime; if ((this._elapsed <= 0) && (elapsed <= 0)) { return; } if ((this._elapsed <= 0) && (elapsed > 0)) { this._start(); } else if ((this._elapsed >= this.duration) && (elapsed >= this.duration)) { return; } if (elapsed < 0) { elapsed = 0; } if (elapsed > this.duration) { elapsed = this.duration; } eased = this._easing(elapsed / this.duration); extender = this._interpolator(eased); for (k in extender) { v = extender[k]; if (store = act._implicitPropertyStore(this.obj, k)) { store.value = v; } else { this.obj[k] = v; } } if (elapsed >= this.duration) { this._complete(); } return this._elapsed = elapsed; }; Task.prototype._start = function() { var finalValue, initialValue, _base; this._interpolator = this._animator(this.obj, this.destination, this.act); finalValue = this._interpolator(1); initialValue = this._interpolator(0); if (typeof (_base = this.obj).actBefore === "function") { _base.actBefore(initialValue, finalValue); } return typeof this.started === "function" ? this.started() : void 0; }; Task.prototype._complete = function() { var finalValue, initialValue, _base; initialValue = this._interpolator(0); finalValue = this._interpolator(1); if (typeof (_base = this.obj).actAfter === "function") { _base.actAfter(initialValue, finalValue); } return typeof this.completed === "function" ? this.completed() : void 0; }; return Task; })(); _maxEndTime = function(tasks) { return _.reduce(tasks, (function(acc, task) { return Math.max(acc, task.startTime + task.duration); }), 0); }; Ease = {}; Ease.EaseLinear = EaseLinear = function(v) { return v; }; Ease.EaseIn = EaseIn = function(v) { return v * v * v; }; Ease.EaseOut = EaseOut = function(v) { return 1.0 - EaseIn(1.0 - v); }; Ease.EaseIn2 = EaseIn2 = function(v) { return v * v; }; Ease.EaseOut2 = EaseOut2 = function(v) { return 1.0 - EaseIn(1.0 - v); }; Ease.EaseInOut = EaseInOut = function(v) { if (v < 0.5) { return EaseIn(v * 2) / 2; } else { return EaseOut(v * 2 - 1.0) / 2 + 0.5; } }; Ease.EaseInOut2 = EaseInOut2 = function(v) { return (3 * v * v) - 2 * v * v * v; }; Ease.EaseOutBounce = EaseOutBounce = function(v) { if (v < 1 / 2.75) { return 7.5625 * v * v; } else if (v < 2 / 2.75) { v -= 1.5 / 2.75; return 7.5625 * v * v + 0.75; } else if (v < 2.5 / 2.75) { v -= 2.25 / 2.75; return 7.5625 * v * v + 0.9375; } else { v -= 2.625 / 2.75; return 7.5625 * v * v + 0.984375; } }; Transaction = (function() { function Transaction(fields) { this._serial = fields.serial; this._rate = fields.rate; if (!(this._rate > 0)) { throw new Error('act: rate must be positive'); } this._easing = fields.easing; this._tasks = fields.tasks; this._reverseTasks = (this._tasks.slice(0)).reverse(); this._started = fields.started; this._completed = fields.completed; this._elapsed = 0; this.duration = (_maxEndTime(this._tasks)) / this._rate; this.startTime = 0; } Transaction.prototype.update = function(time) { var eased, elapsed, task, tasks, _i, _len, _ref; elapsed = time - this.startTime; if ((this._elapsed <= 0) && (elapsed <= 0)) { return; } if ((this._elapsed <= 0) && (elapsed > 0)) { this._start(); } else if ((this._elapsed >= this.duration) && (elapsed >= this.duration)) { return; } if (elapsed < 0) { elapsed = 0; } if (elapsed > this.duration) { elapsed = this.duration; } tasks = elapsed >= this._elapsed ? this._tasks : this._reverseTasks; eased = (this._easing(elapsed / this.duration)) * this.duration * this._rate; _ref = this._tasks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { task = _ref[_i]; task.update(eased); } if (elapsed >= this.duration) { this._complete(); } return this._elapsed = elapsed; }; Transaction.prototype._start = function() { return typeof this._started === "function" ? this._started() : void 0; }; Transaction.prototype._complete = function() { return typeof this._completed === "function" ? this._completed() : void 0; }; return Transaction; })(); _default = function(a, b) { if (a == null) { a = b; } return a; }; TransactionBuilder = (function() { function TransactionBuilder(fields) { if (fields == null) { fields = {}; } this.rate = _default(fields.rate, 1); this.started = fields.started || function() {}; this.completed = fields.completed || function() {}; this.serial = fields.serial || false; this.easing = fields.easing || EaseLinear; this._tasks = []; } TransactionBuilder.prototype.addTask = function(task) { return this._tasks.push(task); }; TransactionBuilder.prototype.transaction = function() { var t, task, _i, _len, _ref; if (this.serial) { t = 0; _ref = this._tasks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { task = _ref[_i]; task.startTime = t; t += task.duration; } } return new Transaction({ rate: this.rate, started: this.started, completed: this.completed, serial: this.serial, easing: this.easing, tasks: this._tasks }); }; return TransactionBuilder; })(); Scheduler = (function() { function Scheduler(options) { if (options == null) { options = {}; } this._tasks = []; this.rate = options.rate || 1; this._elapsed = 0; } Scheduler.prototype.tick = function(dt) { var changed, incompleteTasks, task, _i, _len, _ref; this._elapsed += dt * this.rate; incompleteTasks = []; changed = this._tasks.length; _ref = this._tasks; for (_i = 0, _len = _ref.length; _i < _len; _i++) { task = _ref[_i]; task.update(this._elapsed); if (task.startTime + task.duration >= this._elapsed) { incompleteTasks.push(task); } } this._tasks = incompleteTasks; return changed; }; Scheduler.prototype.addTask = function(task) { task.startTime = this._elapsed; return this._tasks.push(task); }; Scheduler.rate = 1.0; return Scheduler; })(); Implicit = (function() { function Implicit(_act, _obj, options) { var k, opts, v, _defineAnimatedProperty, _definePassthroughProperty, _fn, _fn1, _proxyFunction, _ref, _this = this; this._act = _act; this._obj = _obj; _defineAnimatedProperty = function(k, actOptions) { return Object.defineProperty(_this, k, { get: function() { return _this._obj[k]; }, set: function(v) { return _this._act(_this._obj, _objectFromKeysAndValues(k, v), actOptions); } }); }; _definePassthroughProperty = function(k) { return Object.defineProperty(_this, k, { get: function() { return _this._obj[k]; }, set: function(v) { return _this._obj[k] = v; } }); }; _proxyFunction = function(k) { return _this[k] = function() { return this._obj[k].apply(this, arguments); }; }; _fn = function(k, opts) { if (opts === null || opts.animator === null) { return _definePassthroughProperty(k); } else { return _defineAnimatedProperty(k, opts); } }; for (k in options) { opts = options[k]; _fn(k, opts); } _ref = this._obj; _fn1 = function(k, v) { if (_.isFunction(v)) { return _proxyFunction(k); } else if (!(_this[k] != null)) { return _definePassthroughProperty(k); } }; for (k in _ref) { v = _ref[k]; _fn1(k, v); } } return Implicit; })(); _objectFromKeysAndValues = function() { var i, k, obj, v; obj = Object.create(null); i = 0; while (i + 1 < arguments.length) { k = arguments[i]; v = arguments[i + 1]; obj[k] = v; i += 2; } return obj; }; _requestAnimFrame = function() { if (typeof window !== "undefined" && window !== null) { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame; } }; _setTimeout = function(timeSeconds, fn) { var requestAnimFrame; if (requestAnimFrame = _requestAnimFrame()) { return requestAnimFrame(fn); } else { return setTimeout(fn, timeSeconds * 1000); } }; _getTime = function() { return (new Date).getTime() * 0.001; }; _smoothTicks = function(simulate, tick, fn) { return fn; /* # TODO: Think about this later return fn unless simulate throw (new Error 'tick must be positive') unless tick > 0 timeElapsed = 0 (dt) -> timeElapsed += dt while timeElapsed > tick fn tick timeElapsed -= tick */ }; Array.prototype.remove = function(from, to) { var rest; rest = this.slice((to || from) + 1 || this.length); this.length = from < 0 ? this.length + from : from; return this.push.apply(this, rest); }; _extendEventFunctions = function(obj) { var _eventHandlers; if ((obj.on != null) || (obj.off != null) || (obj.trigger != null)) { throw new Error('act: object already has event functions'); } _eventHandlers = {}; obj.on = function(eventName, cb, context) { var eventHandler, eventNames, name, _i, _len; if (!_.isString(eventName)) { throw new Error("act.on: eventName is not a string (" + eventName + ")"); } if (!_.isFunction(cb)) { throw new Error("act.on: eventHandler is not a function (eventName: " + eventName + ", eventHandler: " + cb + ")"); } eventNames = eventName.split(' '); eventHandler = { cb: cb, context: context }; for (_i = 0, _len = eventNames.length; _i < _len; _i++) { name = eventNames[_i]; if (name.length) { if (!_eventHandlers[name]) { _eventHandlers[name] = []; } _eventHandlers[name].push(eventHandler); } } return function() { return obj.off(eventName, cb, context); }; }; obj.off = function(eventName, cb, context) { var eventHandlersToChange, eventNames, handlers, name, _i, _len; if (eventName) { eventHandlersToChange = {}; eventNames = eventName.split(' '); for (_i = 0, _len = eventNames.length; _i < _len; _i++) { name = eventNames[_i]; if (name.length && _eventHandlers[name]) { eventHandlersToChange[name] = _eventHandlers[name]; } } } else { eventHandlersToChange = _eventHandlers; } for (name in eventHandlersToChange) { handlers = eventHandlersToChange[name]; _eventHandlers[name] = _.reject(handlers, function(handler) { if (cb) { if (handler.cb !== cb) { return false; } } if (context) { if (handler.context !== context) { return false; } } return true; }); } return null; }; return obj.trigger = function(eventName) { var args, eventNames, handler, name, _i, _j, _len, _len1, _ref; eventNames = eventName.split(' '); for (_i = 0, _len = eventNames.length; _i < _len; _i++) { name = eventNames[_i]; if (name.length && _eventHandlers[name]) { _ref = _eventHandlers[name]; for (_j = 0, _len1 = _ref.length; _j < _len1; _j++) { handler = _ref[_j]; args = Array.prototype.slice.call(arguments, 1); handler.cb.apply(handler.context || obj, args); } } } return null; }; }; act = actGenerator(); act.version = '0.0.6'; module.exports = act; }).call(this);