• Laya1.x Timer小记


    Timer是时钟管理类,在Laya初始化的时候会创建一个实例,通过Laya.timer访问。

    TimerHandler
    • TimerHandler是对每一个定时任务的封装,每次调用frameOnce、frameLoop、once、loop或者callLayer都会产生一个TimerHandler实例。
    • TimerHandler会缓存传入的参数(args),当定时器触发时,会将对应的args传递给对应回调。
    public var key:int;   			//唯一key,用于快速找到对应handler
    public var repeat:Boolean;		//是否是重复调用   分别对应 once/loop
    public var delay:int;			//延时
    public var userFrame:Boolean;	// true表示使用帧计时  false表示使用时间计时
    public var exeTime:int;			//下次执行的时间
    public var caller:*				//调用者
    public var method:Function;		//调用方法
    public var args:Array;			//触发时的参数
    public var jumpFrame:Boolean;	//时钟是否跳帧。基于时间的循环回调,单位时间间隔内,如能执行多次回调,出于性能考虑,引擎默认只执行一次,设置jumpFrame=true后,则回调会连续执行多次
    
    public function clear():void {
    	caller = null;
    	method = null;
    	args = null;
    }
    
    public function run(withClear:Boolean):void {
    	var caller:* = this.caller;
    	/*[IF-FLASH]*/
    	if ((caller is Node) && caller.destroyed)
    		/*[IF-FLASH]*/
    		return clear();
    	//[IF-SCRIPT] if (caller && caller.destroyed) return clear();
    	var method:Function = this.method;
    	var args:Array = this.args;
    	withClear && clear();
    	if (method == null) return;
    	args ? method.apply(caller, args) : method.call(caller);
    }
    
    Timer数据类型定义
    /**@private */
    private static var _pool:Array = [];   //TimerHandler池
    
    /*[DISABLE-ADD-VARIABLE-DEFAULT-VALUE]*/
    /** 两帧之间的时间间隔,单位毫秒。*/
    private var _delta:int = 0;		
    /** 时针缩放。*/
    public var scale:Number = 1;
    /** 当前帧开始的时间。*/
    public var currTimer:Number = _now();
    /** 当前的帧数。*/
    public var currFrame:int = 0;
    /**@private */
    private var _lastTimer:Number = _now();	//上一帧的时间
    /**@private */
    private var _mid:int = 1;				//用于生成唯一key
    /**@private */
    /*[IF-FLASH]*/							//TimerHandler的map  用于查找Handler(包括Later的handler)
    private var _map:flash.utils.Dictionary = new flash.utils.Dictionary(true);
    //[IF-JS] private var _map:Array = [];
    /**@private */
    private var _laters:Array = [];			//存放callLater创建的handler
    /**@private */
    private var _handlers:Array = [];		//存放非callLater创建的handler,包括frameOnce、frameLoop、once、loop
    /**@private */
    private var _temp:Array = [];			//临时数组,用于清理
    /**@private */
    private var _count:int = 0;				//记录handler.method为空的数量,检测是否清理
    
    初始化
    /**@private */
    protected function _init():void {
    	Laya.timer && Laya.timer.frameLoop(1, this, _update);
    }
    

    除了Laya.timer之外,其他的timer的实例都是注册到Laya.timer的frameLoop中。

    _create方法
    • 创建对应的TimerHandler。
    • 如果coverBefore并且已经注册过相同Handler,则覆盖之前的handler
    • 设置对应参数,并计算下一次执行的时间。
    • 为Handler创建唯一key,添加到_map中,用于快速查找。
    • 将handler放到_handler数组中,用于_update时遍历。

    frameOnce、frameLoop、once、loop实现上都是调用_create方法,传入对应的参数,将handler注册到timer中,等待_update执行相关计算。

    /** @private */
    public function _create(useFrame:Boolean, repeat:Boolean, delay:int, caller:*, method:Function, args:Array, coverBefore:Boolean):TimerHandler {
    	//如果延迟为0,则立即执行
    	if (!delay) {
    		method.apply(caller, args);
    		return null;
    	}
    	
    	//先覆盖相同函数的计时
    	if (coverBefore) {
    		var handler:TimerHandler = _getHandler(caller, method);
    		if (handler) {
    			handler.repeat = repeat;
    			handler.userFrame = useFrame;
    			handler.delay = delay;
    			handler.caller = caller;
    			handler.method = method;
    			handler.args = args;
    			handler.exeTime = delay + (useFrame ? this.currFrame : this.currTimer + _now() - _lastTimer);
    			return handler;
    		}
    	}
    	
    	//找到一个空闲的timerHandler
    	handler = _pool.length > 0 ? _pool.pop() : new TimerHandler();
    	handler.repeat = repeat;
    	handler.userFrame = useFrame;
    	handler.delay = delay;
    	handler.caller = caller;
    	handler.method = method;
    	handler.args = args;
    	handler.exeTime = delay + (useFrame ? this.currFrame : this.currTimer + _now() - _lastTimer) + 1;
    	
    	//索引handler
    	_indexHandler(handler);
    	
    	//插入数组
    	_handlers.push(handler);
    	
    	return handler;
    }
    
    callLater
    • callLater使用延时执行,再Timer.update执行末尾调用。
    • 如果对应的caller和method已经注册过,则不会重复注册。
    • 创建的TimerHandler会放到_laters数组中。
    _update
    • 计算当前帧和当前时间。
    • 计算delta。
    • 遍历handler数组,检查时间/帧是否超过了handler的exeTime。
    • 超过时间handler执行回调,如果是once则将handler的caller和method设置为空;如果是loop则执行回调,并计算下次执行的时间。如果下次调用的时间间隔内,可以触发多次回调,默认情况下执行执行一次回调。如果设置为jumpFrame则会调用多次回调(如delay为1毫秒,delta为30毫秒,则下个update时,会调用30次回调)。
    • 遍历数组时,会记录method为空的handler的数量,当数量为30或者200帧时,会清理一次没用的handler,将没用的handler回收到池中。
    • 执行完所有handler之后,遍历所有laters数组,执行callLater所注册的回调。执行完成后,清理所有laters数组。
    /**
     * @private
     * 帧循环处理函数。
     */
    public function _update():void {
    	if (scale <= 0) {
    		_lastTimer =_now();
    		return;
    	}
    	var frame:int = this.currFrame = this.currFrame + scale;
    	var now:Number = _now();
    	_delta = (now - _lastTimer) * scale;
    	var timer:Number = this.currTimer = this.currTimer + _delta;
    	_lastTimer = now;
    	
    	//处理handler
    	var handlers:Array = this._handlers;
    	_count = 0;
    	for (i = 0, n = handlers.length; i < n; i++) {
    		handler = handlers[i];
    		if (handler.method !== null) {
    			var t:int = handler.userFrame ? frame : timer;
    			if (t >= handler.exeTime) {
    				if (handler.repeat) {
    					if (!handler.jumpFrame) {
    						handler.exeTime += handler.delay;
    						handler.run(false);
    						if (t > handler.exeTime) {
    							//如果执行一次后还能再执行,做跳出处理,如果想用多次执行,需要设置jumpFrame=true
    							handler.exeTime += Math.ceil((t - handler.exeTime) / handler.delay) * handler.delay;
    						}
    					} else {
    						while (t >= handler.exeTime) {
    							handler.exeTime += handler.delay;
    							handler.run(false);
    						}
    					}
    				} else {
    					handler.run(true);
    				}
    			}
    		} else {
    			_count++;
    		}
    	}
    	
    	if (_count > 30 || frame % 200 === 0) _clearHandlers();
    	
    	//处理callLater
    	var laters:Array = this._laters;
    	for (var i:int = 0, n:int = laters.length - 1; i <= n; i++) {
    		var handler:TimerHandler = laters[i];
    		if (handler.method !== null) {
    			/*[IF-FLASH]*/
    			_map[handler.method] = null;
    			//[IF-SCRIPT]_map[handler.key] = null;
    			handler.run(false);
    		}
    		_recoverHandler(handler);
    		i === n && (n = laters.length - 1);
    	}
    	laters.length = 0;
    }
    
    其他
    • Laya.timer._update是在Laya.stage.loop中调用,Laya.stage.loop是在Render中调用。

    • 在Frash中,监听enterFrame事件。

    • 在js中则是注册requestAnimationFrame,来进入渲染帧。

    • 当stage不可见时(如切到后台),则timer._update每一秒触发一次。一秒钟一帧,当切回前台后,帧率恢复正常。

    • update是在渲染之前执行。

    • Laya.stage控制实际帧率,根据Laya.stage.frameRate。

  • 相关阅读:
    文件
    drf序列化组件
    drf入门规范
    单例模式
    初识drf
    restful规范
    虚拟环境使用
    vue基础(三)
    vue基础(二)
    作业
  • 原文地址:https://www.cnblogs.com/chiguozi/p/9755808.html
Copyright © 2020-2023  润新知