1 /**
  2  * Hilo
  3  * Copyright 2015 alibaba.com
  4  * Licensed under the MIT License
  5  */
  6 
  7 /**
  8  * @class Ticker是一个定时器类。它可以按指定帧率重复运行,从而按计划执行代码。
  9  * @param {Number} fps 指定定时器的运行帧率。
 10  * @module hilo/util/Ticker
 11  * @requires hilo/core/Class
 12  * @requires hilo/core/Hilo
 13  */
 14 var Ticker = Class.create(/** @lends Ticker.prototype */{
 15     constructor: function(fps){
 16         this._targetFPS = fps || 30;
 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      * 启动定时器。
 33      * @param {Boolean} userRAF 是否使用requestAnimationFrame,默认为false。
 34      */
 35     start: function(useRAF){
 36         if(this._intervalId) return;
 37         this._lastTime = +new Date();
 38 
 39         var self = this, interval = this._interval,
 40             raf = window.requestAnimationFrame ||
 41                   window[Hilo.browser.jsVendor + 'RequestAnimationFrame'];
 42 
 43         if(useRAF && raf){
 44             var tick = function(){
 45                 self._tick();
 46             }
 47             var runLoop = function(){
 48                 self._intervalId = setTimeout(runLoop, interval);
 49                 raf(tick);
 50             };
 51         }else{
 52             runLoop = function(){
 53                 self._intervalId = setTimeout(runLoop, interval);
 54                 self._tick();
 55             };
 56         }
 57 
 58         runLoop();
 59     },
 60 
 61     /**
 62      * 停止定时器。
 63      */
 64     stop: function(){
 65         clearTimeout(this._intervalId);
 66         this._intervalId = null;
 67         this._lastTime = 0;
 68     },
 69 
 70     /**
 71      * 暂停定时器。
 72      */
 73     pause: function(){
 74         this._paused = true;
 75     },
 76 
 77     /**
 78      * 恢复定时器。
 79      */
 80     resume: function(){
 81         this._paused = false;
 82     },
 83 
 84     /**
 85      * @private
 86      */
 87     _tick: function(){
 88         if(this._paused) return;
 89         var startTime = +new Date(),
 90             deltaTime = startTime - this._lastTime,
 91             tickers = this._tickers;
 92 
 93         //calculates the real fps
 94         if(++this._tickCount >= this._targetFPS){
 95             this._measuredFPS = 1000 / (this._tickTime / this._tickCount) + 0.5 >> 0;
 96             this._tickCount = 0;
 97             this._tickTime = 0;
 98         }else{
 99             this._tickTime += startTime - this._lastTime;
100         }
101         this._lastTime = startTime;
102 
103         for(var i = 0, len = tickers.length; i < len; i++){
104             tickers[i].tick(deltaTime);
105         }
106     },
107 
108     /**
109      * 获得测定的运行时帧率。
110      */
111     getMeasuredFPS: function(){
112         return this._measuredFPS;
113     },
114 
115     /**
116      * 添加定时器对象。定时器对象必须实现 tick 方法。
117      * @param {Object} tickObject 要添加的定时器对象。此对象必须包含 tick 方法。
118      */
119     addTick: function(tickObject){
120         if(!tickObject || typeof(tickObject.tick) != 'function'){
121             throw new Error('Ticker: The tick object must implement the tick method.');
122         }
123         this._tickers.push(tickObject);
124     },
125 
126     /**
127      * 删除定时器对象。
128      * @param {Object} tickObject 要删除的定时器对象。
129      */
130     removeTick: function(tickObject){
131         var tickers = this._tickers,
132             index = tickers.indexOf(tickObject);
133         if(index >= 0){
134             tickers.splice(index, 1);
135         }
136     }
137 
138 });