Graphics 与 DisplayObject 的关系

在原生 Canvas 中,其实并无 DisplayObject 的概念,它只有绘制图像的概念。javascript

大部分的原生绘制图形或图像的 API 通常是这样的:html

api(x, y, ...)

例如 rect 就是:ctx.rect(x, y, width, height)java

这类绘制 API 大部分被封装在 CreateJS 的 Graphics 类中,它们有一个共同的特色 ---- 名字都是以 draw 开始,例如:drawRect / drawCircle 。segmentfault

为何说是大部分绘制的API被封装在 Grahpics 类中呢?
很简单,Graphics 类处理的是图形绘制,因此也能够说全部的图形绘制 API 被封装在 Graphics 类中。绘制API还有另外一个功能 ------ 绘制图像,这个功能被封装在 Bitmap 类中。api

我曾经这样以为:「graphics.draw*(x, y, ...) 的参数 <x, y> 就是 displayObject 的属性 x, y」。可是其实这个想法很容易 就能够验证获得它的错误性:数组

var rect = new createjs.Shape(); 
rect.graphics.drawRect(50, 50, 100 100);
stage.addChild(rect);
console.log(rect.x, rect.y);

Graphics 的工做原理

来分析 Graphics 的 drawRect API 源码:
https://www.createjs.com/docs...
图片描述app

https://www.createjs.com/docs...
图片描述函数

https://www.createjs.com/docs...
图片描述测试

https://www.createjs.com/docs...
图片描述动画

代码很简单,以下:

  1. 建立一个「Rect 实例」;

  2. 「Rect 构造函数」将 drawRect 的实现代码挂载在 prototype.exec

  3. 将「Rect 实例」append 到数组 _activeInstructions中(第四张截图);

  4. 返回「Graphics」实例

为何须要一个数组(_activeInstructions)来存储原生指令?
对于矩形、圆形和圆角矩形来讲,确实不须要数组来存储指令;可是梯形、三角形等图形没有对应的原生API,此时须要使用绘制线段或孤线(ctx.moveTo/ctx.lineTo/ctx.arc)来组合产生,而数组 _activeInstructions 就是来处理组合图形产生的。

Rect实例有什么做用?
Rect实例是 drawRect 返回的实例,Graphics 里还有其它的 draw* API,它们会返回各自图形的实例,因此能够把 Rect实例和其它实例统称为「基础图形实例」。「基础图形实例」的做用是提供一个修改图形接口。经过第四张截图,能够看到「基础图形实例」最终被挂载在「Graphics 实例」的 command 属性下,也就是说经过「Graphics实例」的 command 能够访问到最新的「基础图形实例」,写一个测试代码以下:

var gcircle = circle.graphics.beginFill("00ff00").drawCircle(0, 0, 10); 
circle.x = circle.y = 100; 
stage.addChild(circle); 
stage.update(); 
setTimeout(function() { 
    gcircle.command.radius = 50;
    stage.update(); 
}, 1000);

这个特性在动态绘制图形上是颇有用的,不过,它有一个短板:经过 command 属性只能访问到最近的「基础图形实例」。也就是说自定义的组合图形,若是须要访问到对应的「基础图形实例」就须要把绘制过程拆散,举个例子:

// 三角形
var threeangle = new createjs.Shape(); 
threeangle.graphics.beginFill("00ff00").moveTo(50, 0).lineTo(0, 50).lineTo(100, 50).closePath(); 
stage.addChild(threeangle); 
stage.update();

若是底部两个端点要作动画:

// 三角形
var threeangle = new createjs.Shape(); 
threeangle.graphics.beginFill("00ff00").moveTo(50, 0); 
var p1 = threeangle.graphics.lineTo(0, 50).command; 
var p2 = threeangle.graphics.lineTo(100, 50).command; 
threeangle.graphics.closePath(); 
stage.addChild(threeangle); 
stage.update(); 
setTimeout(function() {
    p1.y = 100; 
    stage.update(); 
}, 1000); 
setTimeout(function() {
    p2.y = 200; 
    stage.update(); 
}, 2000);

Shape 的工做原理

https://www.createjs.com/docs...
图片描述

Shape 极简单,就是生成一个 DisplayObject 实例,而后在这个实例中挂载一个 Graphics 实例。而它的工做原理(即它的渲染)以下:
https://www.createjs.com/docs...
图片描述

也就是 Shape 实例的 draw 方法实际上是直接调用 Graphics 实例的 draw 方法。

DisplayObject 的工做原理

DisplayObject 的核心功能是处理 DisplayObject 实例的「矩阵转换」(即位移与形变),以下:
https://www.createjs.com/docs...
图片描述

我在 聊聊 Container 的实现 中介绍了使用原生 Canvas 实现 Container 的原理。其实,Shape 实例也是一个Container,只是 CreateJS 限制了 shape 只能放 Graphics 实例,并且 shape 只能存放一个 Graphics 实例(即shape 下的 graphics 属性),Graphics 实例下存放多个「基础图形实例」。

**若是从嵌套的维度来讲 Graphics 与 DisplayObject 的关系是:
Container > Shape > Graphics > Rect/Circle/RoundRect等基础图形**

其实,CreateJS 彻底不须要这么实现。像 PIXI 在实现 Graphics 的时候,Graphics 也一个 Container,它能够像一个正常的 DisplayObject 同样被使用,而不用像 CreateJS 同样麻烦。

相关文章
相关标签/搜索