define(function(require, exports, module) { "use strict"; var Util = require('./util'); var Base = require('./base'); var Easing = require('./easing'); var RAF = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { window.setTimeout(callback, 1000 / 60); }; var vendors = ['webkit', 'moz', 'ms', 'o']; var cancelRAF = window.cancelAnimationFrame; if (!cancelRAF) { for (var i = 0; i < vendors.length; i++) { if (window[vendors[i] + 'CancelAnimationFrame'] || window[vendors[i] + 'CancelRequestAnimationFrame']) { cancelRAF = window[vendors[i] + 'CancelAnimationFrame'] || window[vendors[i] + 'CancelRequestAnimationFrame']; } } } cancelRAF = cancelRAF || window.clearTimeout; function Bezier(x1, y1, x2, y2, epsilon) { var curveX = function(t) { var v = 1 - t; return 3 * v * v * t * x1 + 3 * v * t * t * x2 + t * t * t; }; var curveY = function(t) { var v = 1 - t; return 3 * v * v * t * y1 + 3 * v * t * t * y2 + t * t * t; }; var derivativeCurveX = function(t) { var v = 1 - t; return 3 * (2 * (t - 1) * t + v * v) * x1 + 3 * (-t * t * t + 2 * v * t) * x2; }; return function(t) { var x = t, t0, t1, t2, x2, d2, i; // First try a few iterations of Newton's method -- normally very fast. for (t2 = x, i = 0; i < 8; i++) { x2 = curveX(t2) - x; if (Math.abs(x2) < epsilon) return curveY(t2); d2 = derivativeCurveX(t2); if (Math.abs(d2) < 1e-6) break; t2 = t2 - x2 / d2; } t0 = 0, t1 = 1, t2 = x; if (t2 < t0) return curveY(t0); if (t2 > t1) return curveY(t1); // Fallback to the bisection method for reliability. while (t0 < t1) { x2 = curveX(t2); if (Math.abs(x2 - x) < epsilon) return curveY(t2); if (x > x2) t0 = t2; else t1 = t2; t2 = (t1 - t0) * .5 + t0; } // Failure return curveY(t2); }; }; function Timer(cfg) { var self = this; self.cfg = Util.mix({ easing: "linear" }, cfg) } Timer.MIN_DURATION = 1; Util.extend(Timer, Base, { reset: function(cfg) { var self = this; Util.mix(self.cfg, cfg); self.isfinished = false; self.percent = 0; self._stop = null; }, run: function() { var self = this; var duration = self.cfg.duration; if (duration <= Timer.MIN_DURATION) { self.isfinished = true; self.trigger("run", { percent: 1 }); self.trigger("end", { percent: 1 }); } if (self.isfinished) return; self._hasFinishedPercent = self._stop && self._stop.percent || 0; self._stop = null; self.start = Date.now(); self.percent = 0; // epsilon determines the precision of the solved values var epsilon = (1000 / 60 / duration) / 4; var b = Easing[self.cfg.easing]; self.easingFn = Bezier(b[0], b[1], b[2], b[3], epsilon); self._run(); }, _run: function() { var self = this; cancelRAF(self._raf); self._raf = RAF(function() { self.now = Date.now(); self.duration = self.now - self.start >= self.cfg.duration ? self.cfg.duration : self.now - self.start; self.progress = self.easingFn(self.duration / self.cfg.duration); self.percent = self.duration / self.cfg.duration + self._hasFinishedPercent; if (self.percent >= 1 || self._stop) { self.percent = self._stop && self._stop.percent ? self._stop.percent : 1; self.duration = self._stop && self._stop.duration ? self._stop.duration : self.duration; var param = { percent: self.percent }; self.trigger("stop", param); if (self.percent >= 1) { self.isfinished = true; self.trigger("end", { percent: 1 }); } return; } self.trigger("run", { percent: self.progress, originPercent:self.percent }); self._run(); }) }, stop: function() { var self = this; self._stop = { percent: self.percent, now: self.now }; cancelRAF(self._raf) } }); if (typeof module == 'object' && module.exports) { module.exports = Timer; } /** ignored by jsdoc **/ else { return Timer; } });