《TypeScript图形渲染实战:2D架构设计与实现》 —3.4.3 触发多个定时任务的操作

举报
华章计算机 发表于 2019/12/12 19:43:00 2019/12/12
【摘要】 本节书摘来自华章计算机《TypeScript图形渲染实战:2D架构设计与实现》 一书中第3章,第3.4.3节,作者是步磊峰。

3.4.3  触发多个定时任务的操作

  定时器处理的关键源码封装在Application类的私有方法_handleTimers中,_handleTimers实现细节已经注释得很详细了。具体代码如下:

 

// _handleTimers私有方法被Application的update函数调用

// update函数第二个参数是以秒表示的前后帧时间差

// 正符合_handleTimers参数要求

// 计时器依赖于requestAnimationFrame回调

// 如果当前Application没有调用start的话

// 则计时器不会生效

private _handleTimers ( intervalSec : number ) :  void {

    // 遍历整个timers列表

    for ( let i = 0 ; i < this . timers . length ; i ++ ) {

        let timer : Timer = this . timers [ i ] ;

        // 如果当前timer enabled为false,那么继续循环

        // 这句也是重用Timer对象的一个关键实现

        if ( timer . enabled === false ) {

            continue ;

        }

        // countdown初始化时 = timeout

        // 每次调用本函数,会减少上下帧的时间间隔,也就是update第二个参数传来的值

        // 从而形成倒计时的效果

        timer . countdown -= intervalSec ;

        // 如果countdown 小于 0.0,那么说明时间到了

        // 要触发回调了

        // 从这里看到,实际上timer并不是很精确的

        // 举个例子,假设update每次0.16秒

        // timer设置0.3秒回调一次

        // 那么实际上是 ( 0.3 - 0.32 ) < 0,触发回调

        if ( timer . countdown < 0.0 ) {

            // 调用回调函数

            timer . callback ( timer . id , timer . callbackData ) ;

            // 下面的代码两个分支分别处理触发一次和重复触发的操作

            // 如果该计时器需要重复触发

            if ( timer . onlyOnce === false ) {

                // 重新将countdown设置为timeout

                // 由此可见,timeout不会更改,它规定了触发的时间间隔

                // 每次更新的是countdown倒计时器

                timer . countdown = timer . timeout ;         //很精妙的一个技巧

            } else {  // 如果该计时器只需要触发一次,那么就删除该计时器

                this . removeTimer ( timer . id ) ;

            }

        }

    }

}

 

  这段代码实现了设定的3个目标:

* Application类能够同时触发多个计时器。

* 每个计时器可以以不同帧率来重复执行任务。

* 每个计时器可以以倒计时方式执行一次任务。

  _handleTimer私有方法可以在Application类的step方法中被调用,也可以在update方法中调用。这里就将其放在step方法中,并且顺便在step方法中添加计算FPS(Frame Per Second)的源码。具体代码如下:

 

// Application中声明私有成员变量

private _fps : number = 0 ;

// 提供一个只读函数,用于获得当前帧率

public get fps ( ) {

     return this . _fps ;

}

// 周而复始地运动

protected step ( timeStamp : number ) : void {

    // 第一次调用本函数时,设置start和lastTime为timestamp

   if ( this . _startTime === -1 ) this . _startTime = timeStamp ;

   if(  this . _lastTime === -1 ) this . _lastTime = timeStamp ;

   //计算当前时间点与第一次调用step时间点的差

   let elapsedMsec = timeStamp - this . _startTime ;

   // 下面的代码和前几节的代码有更改:

   // 1.增加FPS计算

   // 2.增加调用_updateTimer私方法

   //计算当前时间点与上一次调用step时间点的差(可以理解为两帧之间的时间差)

   // 此时intervalSec实际是毫秒表示

   let intervalSec = ( timeStamp - this . _lastTime ) ;

   // 第一帧的时候,intervalSec为0,防止0作为分母

   if ( intervalSec !== 0 ) {

       // 计算fps

       this . _fps = 1000.0 / intervalSec ;

   }

   // update使用的是以秒为单位,因此转换为秒表示

   intervalSec /= 1000.0 ;

   //记录上一次的时间戳

   this ._lastTime = timeStamp ;

     

    this . _handleTimers ( intervalSec ) ;

    // console . log (" elapsedTime = " + elapsedMsec + " diffTime = " +

    intervalSec);

    // 先更新

    this . update ( elapsedMsec , intervalSec ) ;

    // 后渲染

    this . render ( ) ;

    // 递归调用,形成周而复始地前进

    requestAnimationFrame ( ( elapsedMsec : number ) : void => {

       this . step ( elapsedMsec ) ;

    } ) ;

       

   // requestAnimationFrame ( this . step . bind ( this ) ) ;

}

 

  完整地了解了整个Timer的实现过程,知道Timer定时触发任务操作都是依赖request AnimationFrame方法的,这意味着如果Application类不调用start方法进入动画循环,是无法触发自定义的Timer定时任务回调的,这一点与Window对象的setTimerout和setInterval有所区别,其他的功能都类似。


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。