自选图为什么不能自选(选不出来可以不选图)
771
2022-05-30
第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所示。
图3.1 Chrome浏览器中的requestAnimationFrame输出时间差
渲染 架构设计 TypeScript
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。