PIXI.JS源码解析:Ticker.js
2020/12/24 5:38:22
本文主要是介绍PIXI.JS源码解析:Ticker.js,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
- 一个渲染引擎如何实现loop方法?
- 一个监听回调队列如何实现添加和删除?
- 监听回调队列先后关系如何实现?
- 如果在监听回调函数中更新队列,本轮回调如何正确地执行?
今天我们就借用PIXI.JS的源码来说说以上功能的实现(本文不打算讲解TS,为了方(tou)便(lan)就是用v4版本的代码)
本文要讲解的是core/ticker目录中的Ticker和TickerListener他们用来处理画面的动态更新,以及执行每次更新时的回调。
从Application进入Ticker
在阅读PIXI.JS的上手文档时,你第一个接触的就是一下这行代码:
var app = new PIXI.Application();
这里我们实例化的app,后续会用来添加精灵图,制作动画。
我们都知道,依赖canvas的动画是会不断循环执行loop函数,把图像绘制在画布上,以此实现动画。那么Application是如何实现的?
在构造函数中,我们可以看到一个内部变量this._ticker变量,这个变量就是实现loop的核心对象,它通过this.ticker对外暴露。
通过set方法,我们知道,对ticker赋值其实是更新了this._ticker(用新值覆盖了前一次的this._ticker)。而start方法也是执行了this._ticker.start。
而通过ticker.add方法,application将render函数添加到了渲染队列中,此后每次执行loop是都会促发一个render函数的执行:
那么这里的add
,start
,remove
三个方法都干了什么?这个UPDATE_PRIORITY.LOW
又是什么?
记住这几个问题本篇我们会一一解答。
那我们就看看Ticker.js都实现了什么逻辑。
ps:这里的shared其实也是Ticker的实例:
Ticker.js
Ticker.js在core/ticker文件夹下:
core文件夹是用来存放核心代码的,比如diaplay中存放处理显示盒的代码,sprites中是处理精灵图的代码,renderer中存放处理渲染的代码。这些我们日后都会说到。上一节我们说的new Application()的逻辑代码就来源于core根目录的Application.js。
core/ticker文件夹下除了Ticker外,还有TickerListener和index。上一节说的shared就是在index中定义的:
而TickerListener是专门用来处理Ticker中的监听回调队列的(后面会讲到)
话不多说让我们看看Ticker(这里我删除一些代码,只留下核型逻辑):
这里最重要的就是内部变量this._head和this._tick;
- this._head
我们之前说过Ticker内部实现了一个渲染回调队列,在每次loop是都需要执行一遍,而这个this._head就是这个这个队列的源头,至于为什么需要单独制作一个源头,等我们说完TickerListener你就能理解其中精妙了。而我们在appliction中传进来的render函数,也会作为TickerListener插入到这个队列之中:
而这个显然目前这个队列中除了head外只有一个Listener。这些回调会在每次loop后执行一边以更新画布上的图像。而这个loop就由this._tick开启。
this._tick会不断被requestAnimationFrame调用执行,以实现loop的逻辑。但是循环开启有三个条件:this.started为真,this._requestId = null以及this._head.next存在,这三个条件是否都成立?
- this.started为真
还记得我们在Appliction.js中有调用this.start方法?
Application.js
this.start其实是对_ticker.start的封装。
这里的start是一个单例,未开始时会执行一次循环,并把this.started设为真,这样即便重复执行start方法也不会在促发更新。这样我们解答了上文的第一个问题,start
函数的逻辑(还剩下add
,remove
逻辑,和UPDATE_PRIORITY.LOW
的意义)
- this._requestId = null;
每次this._tick 执行时都会把他设为null,所以loop的第二个条件也满足,
- this._head.next
这里的next也是一个TickerListener,我们前面说过TickerListener是一个回调函数链,不断通过next找到下一个回调知道执行完回调函数链。这个我们在说到TickerListener会详细说明。当我们在Application.js中调用add方法时,我们就已经往插入了TickerListener,所以this._head.next也存在。那么这个loop就会一直循环下去,直到有逻辑触发关闭。
那么this.update(time);处理了什么逻辑?
这里通过next遍历回调函数链来执行emit方法。这些逻辑都被写在TickerListener中,不过在进入TickerListener之前我们还需要弄懂add和remove函数逻辑以及UPDATE_PRIORITY.LOW的意义。
add函数
add
函数和它的姐妹函数addOnce
底层都调用了内部方法this._addListener。
这里TickerListener可以接受4个参数,第四个参数用于控制回调是执行一次还是能够反复执行。
而此前传入UPDATE_PRIORITY.LOW
其实是可以控制回调函数权重的:
一共有5档,默认情况下回调是按顺序添加,先添加的回调先执行,但也可以通过控制传入的权重大小影响回调函数插入的位置(下面会解释),但如果PRIORITY一致时,就是先添加的先执行。
而TickerListener具体逻辑在TickerListener源码解析一篇中会详细解释。那么我们就来看看内部方法_addListener:
在前面分析update逻辑时,我们可以看到TickerListener回调队列是不断通过找寻下一个next来完成链式执行。而具体TickerListener的位置由previous和next决定,除了_head外TickerListener可以没有next却不能没有previous。(甚至两个TickerListener的previous有可能是同一个)。
而执行完插入逻辑后,会执行this._startIfPossible();
这保证了,如果autoStart为真,当我们往_header后添加了TickerListener后,但即便我们没有执行start函数(this.started为false),逻辑上也会开启loop。
但如果我们已经执行完start后(this.started为true),此时再执行_requestIfNeeded;
因为每次执行完_tick后this._requestId = requestAnimationFrame(this._tick);所以_requestIfNeeded的条件语句不会执行。
remove
最后让我们来看看remove函数都干了什么:
简而言之就是删除fn,而这些逻辑都是由TickerListener完成的。那么下一节我们就来看看TickerListener是如何工作的。
这篇关于PIXI.JS源码解析:Ticker.js的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-27Vite多环境配置资料:新手入门教程
- 2024-11-24Vite多环境配置学习:新手入门教程
- 2024-11-23实现OSS直传,前端怎么实现?-icode9专业技术文章分享
- 2024-11-22在 HTML 中怎么实现当鼠标光标悬停在按钮上时显示提示文案?-icode9专业技术文章分享
- 2024-11-22html 自带属性有哪些?-icode9专业技术文章分享
- 2024-11-21Sass教程:新手入门及初级技巧
- 2024-11-21Sass学习:初学者必备的简单教程
- 2024-11-21Elmentplus入门:新手必看指南
- 2024-11-21Sass入门:初学者的简单教程
- 2024-11-21前端页面设计教程:新手入门指南