大家好,解读矩形我是绘制前端西瓜哥,今天带大家看一下 PixiJS 的底层都源码实现。
PixiJS 是源码一个非常流行的 Canvas 库,start 数将近 4w。解读矩形
使用 PixiJS 简单易用的绘制 API,我们可以在浏览器页面的底层都 Canvas 元素上高性能地绘制图形,实现流畅的源码动画。它的解读矩形底层是 WebGL。
用 PixiJS 绘制一个矩形,绘制代码实现为:
const app = new PIXI.Application({ width: 500, height: 300,});document.body.appendChild(app.view);const graph = new PIXI.Graphics();graph.beginFill(0xff0044); // 填充色graph.drawRect(10, 10, 100, 80);graph.endFill();app.stage.addChild(graph);
渲染结果:
这些代码的底层究竟做了什么呢?这次西瓜哥就带大家来一探究竟。
使用的 PixiJS 版本为 7.2.4。
首先是调用 Application 类的构造函数,创建 app 对象。
下面是 Application 构造函数的代码。
export class Application { // 创建 stage public stage: Container = new Container(); // ... constructor(options) { options = Object.assign( { // 是否强制使用 Canvas 2D,否则如果支持 WebGL,用 WebGL // 默认为 false,且已经废弃 Canvas 2D,仅 pixi.js-legacy 可用 forceCanvas: false, }, options ); // 选择渲染器 this.renderer = autoDetectRenderer(options); // 插件初始化 Application._plugins.forEach((plugin) => { plugin.init.call(this, options); }); }}
主要做了以下几件事。
Application 默认内置两个插件:
const graph = new PIXI.Graphics();
创建一个 Graphics 对象。这个 Graphics 对象下可以绘制任何图形,这里我只绘制一个矩形。
graph.beginFill(0xff0044); // 填充色
该方法会给 Graphics 对象的 _fillStyle 设置为指定的颜色值。传入的颜色值会进行标准化(normalize)。
Pixijs 实现有自己的风格:喜欢用类似 _varX 的方法保存 “私有” 变量,然后提供对应的 setter 和 getter 去读写这个内部变量。
getter 可能不提供,这样一个属性就会变成只读属性。有些 getter 里会做懒加载,在第一次读取的时候再初始化,比如 Texture.WHITE。
如果我们不指定颜色,这个 _fillStyle 会使用默认值,且其 visible 属性为 false,表示图形没有填充色,也会在之后的渲染阶段跳过填充的逻辑。
然后是创建一个矩形。
graph.drawRect(10, 10, 100, 80);
上面代码其实调用的是:
return this.drawShape(new PIXI.Rectangle(x, y, width, height));
首先创建一个 Rectangle 对象。
然后基于该 Rectangle 对象、之前设置的 fillStyle、lineStyle、matrix 创建一个 GraphicsData 对象,最后添加到给 rect._geometry.graphicsData 数组上。
总之就是将这个矩形的数据记录下来,之后 PixiJS 会基于这些值构造出绘制 WebGL 可以直接使用的数据。
然后是重置填充色。
rect.endFill();
将 rect 的 _fillStyle 设置为默认值:
public reset() { this.color = 0xFFFFFF; this.alpha = 1; this.texture = Texture.WHITE; this.matrix = null; this.visible = false;}
最后是把 rect 添加到容器 app.stage 下。
app.stage.addChild(rect);
对应的源码是:
export class Container extends DisplayObject { // ... addChild(...children) { if (children.length > 1) { // 有多个图形要添加,会遍历调用当前 addChild 方法 for (let i = 0; i < children.length; i++) { this.addChild(children[i]); } } else { const child = children[0]; if (child.parent) { child.parent.removeChild(child); } child.parent = this; this.sortDirty = true; // 表示没有排序 child.transform._parentID = -1; this.children.push(child); this._boundsID++; // 触发子节点改变的相关事件 this.onChildrenChange(this.children.length - 1); this.emit("childAdded", child, this, this.children.length - 1); child.emit("added", this); } return children[0]; }}
至此,我们的矩形设置好属性并添加到图形树上。
下面是渲染环节。
还记得我们初始化 Application 时,初始化的两个插件吗?
其中一个就是 TickerPlugin,它是 raf(requestAnimationFrame)的封装,会在页面绘制下一帧要之前执行回调函数。
Application 初始化时,调用了TickerPlugin.init() 方法,将 renderer 的 render 方法绑定到 Ticker 上。这样,render 就会不断地被异步调用。
class TickerPlugin { static init(options) { Object.defineProperty(this, "ticker", { set(ticker) { // 将 app.render 函数传入 ticker 的回调列表 ticker.add(this.render, this, UPDATE_PRIORITY.LOW); }, // ... }); // 触发 ticker setter this.ticker = options.sharedTicker ? Ticker.shared : new Ticker(); } // ...}
render 方法:
class Application { // ... public render() { this.renderer.render(this.stage); }}
因为渲染的过程非常长,代码逻辑太多,各种细枝末节,这里只讲大致流程,之后会写一篇文章具体讲解。
PixiJS 高性能的一个原因是减少 draw call,尽可能一次性批量(batch)提供大量顶点和片元给到 WebGL 去处理,充分利用 GPU 的并发计算能力。
责任编辑:姜华 来源: 前端西瓜哥 PixiJSCanvas 库(责任编辑:娱乐)
港铁公司(0066.HK)去年大幅亏损48.09亿港元 全年普通股息合共每股1.23港元
iPhone7新功能曝光 苹果7集成3DTouch功能Home键或升级
神火股份:3月23日融资净偿还37.10万元 当前融资余额为6.29亿元
消息称微软并没有在为 Windows 11 开发可移动任务栏功能