UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

209 lines (183 loc) 6.45 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2004-2011 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Martin Wittemann (wittemann) ************************************************************************ */ /** * This is a cross browser wrapper for requestAnimationFrame. For further * information about the feature, take a look at spec: * http://www.w3.org/TR/animation-timing/ * * This class offers two ways of using this feature. First, the plain * API the spec describes. * * Here is a sample usage: * <pre class='javascript'>var start = Date.now(); * var cb = function(time) { * if (time >= start + duration) { * // ... do some last tasks * } else { * var timePassed = time - start; * // ... calculate the current step and apply it * qx.bom.AnimationFrame.request(cb, this); * } * }; * qx.bom.AnimationFrame.request(cb, this); * </pre> * * Another way of using it is to use it as an instance emitting events. * * Here is a sample usage of that API: * <pre class='javascript'>var frame = new qx.bom.AnimationFrame(); * frame.on("end", function() { * // ... do some last tasks * }, this); * frame.on("frame", function(timePassed) { * // ... calculate the current step and apply it * }, this); * frame.startSequence(duration); * </pre> * * @require(qx.lang.normalize.Date) */ qx.Bootstrap.define("qx.bom.AnimationFrame", { extend : qx.event.Emitter, events : { /** Fired as soon as the animation has ended. */ "end" : undefined, /** * Fired on every frame having the passed time as value * (might be a float for higher precision). */ "frame" : "Number" }, members : { __canceled : false, /** * Method used to start a series of animation frames. The series will end as * soon as the given duration is over. * * @param duration {Number} The duration the sequence should take. * * @ignore(performance.*) */ startSequence : function(duration) { this.__canceled = false; var start = (window.performance && performance.now) ? (performance.now() + qx.bom.AnimationFrame.__start) : Date.now(); var cb = function(time) { if (this.__canceled) { this.id = null; return; } // final call if (time >= start + duration) { this.emit("end"); this.id = null; } else { var timePassed = Math.max(time - start, 0); this.emit("frame", timePassed); this.id = qx.bom.AnimationFrame.request(cb, this); } }; this.id = qx.bom.AnimationFrame.request(cb, this); }, /** * Cancels a started sequence of frames. It will do nothing if no * sequence is running. */ cancelSequence : function() { this.__canceled = true; } }, statics : { /** * The default time in ms the timeout fallback implementation uses. */ TIMEOUT : 30, /** * Calculation of the predefined timing functions. Approximation of the real * bezier curves has been used for easier calculation. This is good and close * enough for the predefined functions like <code>ease</code> or * <code>linear</code>. * * @param func {String} The defined timing function. One of the following values: * <code>"ease-in"</code>, <code>"ease-out"</code>, <code>"linear"</code>, * <code>"ease-in-out"</code>, <code>"ease"</code>. * @param x {Integer} The percent value of the function. * @return {Integer} The calculated value */ calculateTiming : function(func, x) { if (func == "ease-in") { var a = [3.1223e-7, 0.0757, 1.2646, -0.167, -0.4387, 0.2654]; } else if (func == "ease-out") { var a = [-7.0198e-8, 1.652, -0.551, -0.0458, 0.1255, -0.1807]; } else if (func == "linear") { return x; } else if (func == "ease-in-out") { var a = [2.482e-7, -0.2289, 3.3466, -1.0857, -1.7354, 0.7034]; } else { // default is 'ease' var a = [-0.0021, 0.2472, 9.8054, -21.6869, 17.7611, -5.1226]; } // A 6th grade polynomial has been used as approximation of the original // bezier curves described in the transition spec // http://www.w3.org/TR/css3-transitions/#transition-timing-function_tag // (the same is used for animations as well) var y = 0; for (var i=0; i < a.length; i++) { y += a[i] * Math.pow(x, i); }; return y; }, /** * Request for an animation frame. If the native <code>requestAnimationFrame</code> * method is supported, it will be used. Otherwise, we use timeouts with a * 30ms delay. The HighResolutionTime will be used if supported but the time given * to the callback will still be a timestamp starting at 1 January 1970 00:00:00 UTC. * * @param callback {Function} The callback function which will get the current * time as argument (which could be a float for higher precision). * @param context {var} The context of the callback. * @return {Number} The id of the request. */ request : function(callback, context) { var req = qx.core.Environment.get("css.animation.requestframe"); var cb = function(time) { // check for high resolution time if (time < 1e10) { time = qx.bom.AnimationFrame.__start + time; } time = time || Date.now(); callback.call(context, time); }; if (req) { return window[req](cb); } else { // make sure to use an indirection because setTimeout passes a // number as first argument as well return window.setTimeout(function() { cb(); }, qx.bom.AnimationFrame.TIMEOUT); } } }, /** * @ignore(performance.timing.*) */ defer : function(statics) { // check and use the high resolution start time if available statics.__start = window.performance && performance.timing && performance.timing.navigationStart; // if not, simply use the current time if (!statics.__start) { statics.__start = Date.now(); } } });