《TypeScript图形渲染实战:2D架构设计与实现》 —3 动画与Application类

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

第2篇

Canvas2D篇

  

    (  第3章  动画与Application类

    (  第4章  使用Canvas2D绘图

 

第3章  动画与Application类

  本章将从程序实现的角度来了解一下动画的原理,以及HTML 5提供的一些基础且必要的方法。

  可以将动画的相关功能都封装到一个名为Application的类中,该类主要是作为应用程序的入口类。它能启动或关闭动画循环,抽象更新与重绘流程,提供事件分发和处理功能,并且具有一个允许以不同帧率运行的计时器。

  在开始本章内容前先声明一下,本书所有的Demo以Chrome浏览器为主要测试环境,为简单起见,本书并不关注各浏览器之间的兼容性。

3.1  requestAnimationFrame方法与动画

  从程序的角度来描述,笔者认为,动画就是不间断地、基于时间的更新与重绘。可以说这句话贯穿了本节要讲的所有内容。

3.1.1  HTML中不间断的循环

  所谓不间断的,是指动画需要一个不停地重复循环机制,从程序实现的角度来说,一般有两种选择:

  一种是类似于while ( true ) { }之类的死循环,除非满足退出死循环的条件,否则就一直不停地重复相同的行为。在Windows下的D3D / OpenGL开发中,经常使用这种模式来驱动动画不断运行。作为知识的延伸点,下面来看一段经典的Windows下基于C / C++语言的动画循环演示代码。若不感兴趣可直接跳过。具体代码如下:

 

 MSG msg ;

 ZeroMemory ( & msg , sizeof ( msg ) ) ;

 // 只有明确地收到WM_QUIT消息,才跳出while循环,退出应用程序

 // 否则一直循环重复相同的行为

 // Windows下经典的runLoop操作

 while ( msg . message != WM_QUIT )

 {

    // 如果当前线程消息队列中有消息,则取出该消息

    if ( PeekMessage ( & msg , NULL , 0U , 0U , PM_REMOVE ) ) {

        //将键盘的虚拟键消息转换为WM_CHAR消息,并将WM_CHAR消息再次放入当前线程

        消息队列中,下次还是可以被PeekMessage读取并处理

        TranslateMessage ( & msg ) ;

        //将当前的WM_开头的消息分发到Window 窗口过程处理回调函数中进行处理

        DispatchMessage ( & msg ) ;

        //上面的代码实际就是处理鼠标、键盘、WM_PAINT,或者计时器等队列消息

    } else {

        //如果当前线程消息队列中(上面的代码处理消息队列)没有消息可处理,就一直更新并重绘

        Update ( ) ;                                                //更新

        Render ( ) ;                                                //重绘

    }

 }

 

  另外一种是类似于定时器的回调,例如使用HTML DOM(Document Object Model,文档对象模型)中Window对象的setTimeout、setInterval及requestAnimationFrame方法。关于setTimeout和setInterval的用法,请各位读者自行查阅相关资料(在3.4节中将实现类似setTimeout和setInterval的功能)。下面主要来看一下requestAnimationFrame方法的用法。具体代码如下:

 

// start记录的是第一次调用step函数的时间点,用于计算与第一次调用step函数的时间差,

以毫秒为单位

let start : number = 0 ;

//lastTime记录的是上一次调用step函数的时间点,用于计算两帧之间的时间差,以毫秒为单位

let lastTime : number = 0 ;

// count用于记录step函数运行的次数

let count : number = 0 ;

// step函数用于计算:

// 1.获取当前时间点与HTML程序启动时的时间差 : timestamp

// 2.获取当前时间点与第一次调用step时的时间差 : elapsedMsec

// 3.获取当前时间点与上一次调用step时的时间差 : intervalMsec

// step函数是作为requestAnimationFrame方法的回调函数使用的

// 因此step函数的签名必须是 ( timestamp : number ) => void

function step ( timestamp : number ) : void  {

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

    if ( ! start ) start = timestamp ;

    if ( ! lastTime ) lastTime = timestamp ;

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

    let elapsedMsec : number = timestamp - start ;

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

    let intervalMsec : number = timestamp - lastTime ;

    // 记录上一次的时间戳

    lastTime = timestamp ;

    // 计数器,用于记录step函数被调用的次数

    count ++ ;

    console . log ( " " + count + " timestamp = " + timestamp ) ;

    console . log ( " " + count + " elapsedMsec = " + elapsedMsec ) ;

    console . log ( " " + count + " intervalMsec = " + intervalMsec) ;

    // 使用requestAnimationFrame调用step函数

    window . requestAnimationFrame ( step ) ;

}

// 使用requestAnimationFrame启动step

// 而step函数中又会调用requestAnimationFrame来回调step函数

// 从而形成不间断地递归调用,驱动动画不停地运行

window . requestAnimationFrame ( step ) ;

 

  上述代码每次调用step函数会在浏览器的console控制台窗口中输出当前函数的调用次数,以及3个时间差的数值。Chrome浏览器中console控制台窗口输出的结果,如图3.1所示。

 image.png

图3.1  Chrome浏览器中的requestAnimationFrame输出时间差


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

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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