UNPKG

kityminder

Version:
171 lines (156 loc) 4.81 kB
/** * @fileOverview * * 提供动画帧的基本支持 */ define(function(require, exports) { // 原生动画帧方法 polyfill var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame || function(fn) { return setTimeout(fn, 1000 / 60); }; var cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelAnimationFrame || window.msCancelAnimationFrame || window.clearTimeout; // 上一个请求的原生动画帧 id var frameRequestId; // 等待执行的帧动作的集合,这些帧的方法将在下个原生动画帧同步执行 var pendingFrames = []; /** * 添加一个帧到等待集合中 * * 如果添加的帧是序列的第一个,至少有一个帧需要被执行,则会请求一个原生动画帧来执行 */ function pushFrame(frame) { if (pendingFrames.push(frame) === 1) { frameRequestId = requestAnimationFrame(executePendingFrames); } } /** * 执行所有等待帧 */ function executePendingFrames() { var frames = pendingFrames; pendingFrames = []; while (frames.length) { executeFrame(frames.pop()); } frameRequestId = 0; } /** * @method kity.requestFrame * @catalog animate * @grammar kity.requestFrame(action) => {frame} * @description 请求一个帧,执行指定的动作。动作回调提供一些有用的信息 * * @param {Function} action * * 要执行的动作,该动作回调有一个参数 frame,其中: * * frame.time {Number} * 动作执行时的时间戳(ms) * * frame.index {Number} * 当前执行的帧的编号(首帧为 0) * * frame.dur {Number} * 上一帧至当前帧经过的时间,单位 ms * * frame.elapsed {Number} * 从首帧开始到当前帧经过的时间,单位 ms * * frame.action {Number} * 指向当前的帧处理函数 * * frame.next() * 表示下一帧继续执行。如果不调用该方法,将不会执行下一帧。 * * @example * * ```js * kity.requestFrame(function(frame) { * console.log('平均帧率:' + frame.elapsed / (frame.index + 1)); * * // 更新或渲染动作 * * frame.next(); //继续执行下一帧 * }); * ``` */ function requestFrame(action) { var frame = initFrame(action); pushFrame(frame); return frame; } /** * @method kity.releaseFrame * @catalog animate * @grammar kity.releaseFrame(frame) * @description 释放一个已经请求过的帧,如果该帧在等待集合里,将移除,下个动画帧不会执行释放的帧 * * @param {frame} frame 使用 kity.requestFrame() 返回的帧 * * @example * * ```js * var frame = kity.requestFrame(function() {....}); * kity.releaseFrame(frame); * ``` */ function releaseFrame(frame) { var index = pendingFrames.indexOf(frame); if (~index) { pendingFrames.splice(index, 1); } if (pendingFrames.length === 0) { cancelAnimationFrame(frameRequestId); } } /** * 初始化一个帧,主要用于后续计算 */ function initFrame(action) { var frame = { index: 0, time: +new Date(), elapsed: 0, action: action, next: function() { pushFrame(frame); } }; return frame; } /** * 执行一个帧动作 */ function executeFrame(frame) { // 当前帧时间错 var time = +new Date(); // 当上一帧到当前帧经过的时间 var dur = time - frame.time; // // http://stackoverflow.com/questions/13133434/requestanimationframe-detect-stop // 浏览器最小化或切换标签,requestAnimationFrame 不会执行。 // 检测时间超过 200 ms(频率小于 5Hz ) 判定为计时器暂停,重置为一帧长度 // if (dur > 200) { dur = 1000 / 60; } frame.dur = dur; frame.elapsed += dur; frame.time = time; frame.action.call(null, frame); frame.index++; } // 暴露 exports.requestFrame = requestFrame; exports.releaseFrame = releaseFrame; });