1 /** 2 * Hilo 3 * Copyright 2015 alibaba.com 4 * Licensed under the MIT License 5 */ 6 7 /** 8 * @class Ticker is a Timer. It can run the code at specified framerate. 9 * @param {Number} fps The fps of ticker.Default is 60. 10 * @module hilo/util/Ticker 11 * @requires hilo/core/Class 12 * @requires hilo/util/browser 13 */ 14 var Ticker = Class.create(/** @lends Ticker.prototype */{ 15 constructor: function(fps){ 16 this._targetFPS = fps || 60; 17 this._interval = 1000 / this._targetFPS; 18 this._tickers = []; 19 }, 20 21 _paused: false, 22 _targetFPS: 0, 23 _interval: 0, 24 _intervalId: null, 25 _tickers: null, 26 _lastTime: 0, 27 _tickCount: 0, 28 _tickTime: 0, 29 _measuredFPS: 0, 30 31 /** 32 * Start the ticker. 33 * @param {Boolean} userRAF Whether or not use requestAnimationFrame, default is true. 34 */ 35 start: function(useRAF){ 36 if(useRAF === undefined){ 37 useRAF = true; 38 } 39 40 if(this._intervalId) return; 41 this._lastTime = +new Date(); 42 43 var self = this, interval = this._interval, 44 raf = window.requestAnimationFrame || 45 window[browser.jsVendor + 'RequestAnimationFrame']; 46 47 var runLoop; 48 if(useRAF && raf && interval < 17){ 49 this._useRAF = true; 50 runLoop = function(){ 51 self._intervalId = raf(runLoop); 52 self._tick(); 53 }; 54 }else{ 55 runLoop = function(){ 56 self._intervalId = setTimeout(runLoop, interval); 57 self._tick(); 58 }; 59 } 60 61 this._paused = false; 62 runLoop(); 63 }, 64 65 /** 66 * Stop the ticker. 67 */ 68 stop: function(){ 69 if(this._useRAF){ 70 var cancelRAF = window.cancelAnimationFrame || 71 window[browser.jsVendor + 'CancelAnimationFrame']; 72 cancelRAF(this._intervalId); 73 } 74 else{ 75 clearTimeout(this._intervalId); 76 } 77 this._intervalId = null; 78 this._lastTime = 0; 79 this._paused = true; 80 }, 81 82 /** 83 * Pause the ticker. 84 */ 85 pause: function(){ 86 this._paused = true; 87 }, 88 89 /** 90 * Resume the ticker. 91 */ 92 resume: function(){ 93 this._paused = false; 94 }, 95 96 /** 97 * @private 98 */ 99 _tick: function(){ 100 if(this._paused) return; 101 var startTime = +new Date(), 102 deltaTime = startTime - this._lastTime, 103 tickers = this._tickers; 104 105 //calculates the real fps 106 if(++this._tickCount >= this._targetFPS){ 107 this._measuredFPS = 1000 / (this._tickTime / this._tickCount) + 0.5 >> 0; 108 this._tickCount = 0; 109 this._tickTime = 0; 110 }else{ 111 this._tickTime += startTime - this._lastTime; 112 } 113 this._lastTime = startTime; 114 115 var tickersCopy = tickers.slice(0); 116 for(var i = 0, len = tickersCopy.length; i < len; i++){ 117 tickersCopy[i].tick(deltaTime); 118 } 119 }, 120 121 /** 122 * Get the fps. 123 */ 124 getMeasuredFPS: function(){ 125 return Math.min(this._measuredFPS, this._targetFPS); 126 }, 127 128 /** 129 * Add tickObject. The tickObject must implement the tick method. 130 * @param {Object} tickObject The tickObject to add.It must implement the tick method. 131 */ 132 addTick: function(tickObject){ 133 if(!tickObject || typeof(tickObject.tick) != 'function'){ 134 throw new Error('Ticker: The tick object must implement the tick method.'); 135 } 136 this._tickers.push(tickObject); 137 }, 138 139 /** 140 * Remove the tickObject 141 * @param {Object} tickObject The tickObject to remove. 142 */ 143 removeTick: function(tickObject){ 144 var tickers = this._tickers, 145 index = tickers.indexOf(tickObject); 146 if(index >= 0){ 147 tickers.splice(index, 1); 148 } 149 }, 150 /** 151 * 下次tick时回调 152 * @param {Function} callback 153 * @return {tickObj} 154 */ 155 nextTick:function(callback){ 156 var that = this; 157 var tickObj = { 158 tick:function(dt){ 159 that.removeTick(tickObj); 160 callback(); 161 } 162 }; 163 164 that.addTick(tickObj); 165 return tickObj; 166 }, 167 /** 168 * 延迟指定的时间后调用回调, 类似setTimeout 169 * @param {Function} callback 170 * @param {Number} duration 延迟的毫秒数 171 * @return {tickObj} 172 */ 173 timeout:function(callback, duration){ 174 var that = this; 175 var targetTime = new Date().getTime() + duration; 176 var tickObj = { 177 tick:function(){ 178 var nowTime = new Date().getTime(); 179 var dt = nowTime - targetTime; 180 if(dt >= 0){ 181 that.removeTick(tickObj); 182 callback(); 183 } 184 } 185 }; 186 that.addTick(tickObj); 187 return tickObj; 188 }, 189 /** 190 * 指定的时间周期来调用函数, 类似setInterval 191 * @param {Function} callback 192 * @param {Number} duration 时间周期,单位毫秒 193 * @return {tickObj} 194 */ 195 interval:function(callback, duration){ 196 var that = this; 197 var targetTime = new Date().getTime() + duration; 198 var tickObj = { 199 tick:function(){ 200 var nowTime = new Date().getTime(); 201 var dt = nowTime - targetTime; 202 if(dt >= 0){ 203 if(dt < duration){ 204 nowTime -= dt; 205 } 206 targetTime = nowTime + duration; 207 callback(); 208 } 209 } 210 }; 211 that.addTick(tickObj); 212 return tickObj; 213 } 214 });