文章目录
理念与特点
按照 canvas 提供的 api 的开发逻辑来说,如果想绘制一个粉红色的矩形,使用的是如下方式(设 ctx 为绘图上下文):
ctx.beginPath();
ctx.fillStyle = "pink";
ctx.rect(0, 0, 50, 50);
ctx.fill();
在这个绘制过程中,我们持有的是对 ctx 的引用,直接操作的是 ctx,以过程化的思路完成了需求:开启新路径->设置粉红色->绘制矩形形状->填充矩形。
问题在于,当需求变复杂时,过程化的抽象就有些力不从心了,比如判断一个坐标点 20, 30 是否处于这个矩形内部,比如将这个矩形向右移动 50 个像素,再比如将这个矩形以中心点旋转 30 度。
根源在于,我们想操作的其实是矩形,而不是 ctx,在面向过程方式开发中,根本就不存在矩形对象,所以,janvas 将图形对象抽象了出来。
所以,以上的三个比如,以如下的代码可轻易完成需求:
new janvas.Canvas({
container: "#app",
methods: {
init: function () {
var ctx = this.$ctx; // 获取自动初始化的 ctx 对象
var rect = new janvas.Rect(ctx, 0, 0, 50, 50, 25, 25); // 初始化矩形对象
console.log(rect.isPointInPath(20, 30)); // 判断坐标点是否在矩形内部
rect.getMatrix().setOffset(25, 25).setAngle(Math.PI / 6); // 给矩形添加偏移量和旋转角度
rect.fill(); // 使矩形进行自我绘制
}
}
});
可以看到,使用 new 初始化这个 rect 的时候,第一个实参是 ctx,这个行为应该属于控制权反转,将本该由我们操作的绘图上下文对象 ctx 赋值给了矩形对象,而永远不再需要直接操作它了,接下来我们只需要操作 rect 对象,完成它的继承行为,如判断坐标点是否在其内部或变形操作,或它的特定行为,如设置宽高,处处均可得心应手。
Shape 简介
此类是 janvas 中所有图形的基类,它可以理解为抽象类,构造函数如下:
Shape(ctx, sx, sy, ox, oy)
接受绘图上下文 ctx 供控制反转,接着是起始绘制点 sx, sy,而 ox, oy 不是必须的,不传值会被默认设置为 0,代表了 originX 和 originY,可以理解为 css 变形中的 transform-origin,代表了变形所依据的基点位置。
虽然实例化它没有太多意义,但是所有图形都继承到了来源于它的原型链上的方法,比较重要的方法如下:
Shape.fill(fillRule)
表示图形进行填充绘制。
Shape.stroke()
表示图形进行描边绘制。
Shape.clip(fillRule)
表示图形的剪切操作,和 ctx.clip() 类似。
Shape.restore()
表示图形恢复,与剪切搭配使用。
Shape.isPointInPath(x, y)
判断坐标点是否在图形内部。
Shape.isPointInStroke(x, y)
判断坐标点是否在图形的描边上。
Shape.setStart(sx, sy)
设置图形的起始绘制点。
Shape.setOrigin(ox, oy)
设置图形变形依据的基点。
剩余的其他函数可以在 Github janvas /doc/overview.md 中看到。
Shape 不仅提供了这些基础的方法,还主要解决了变形和样式的问题,首先是变形。
变形
shape.getMatrix()
每个 janvas 中的图形继承于 Shape,都会持有一个 Matrix 供自身变形,使用 getMatrix()
可以获得此对象,此对象提供了如下四个变形可用:
-
setSkew(skewX, skewY)
错切(不常用) -
setScale(scaleX, scaleY)
缩放 -
setAngle(angle)
旋转 -
setOffset(offsetX, offsetY)
平移
需要注意的是,除了平移,其他三个变形操作都会依据 origin 基点变形,所以如果需要达到预期变形,不要忘了指定正确的 originX, originY。
接着简介一下样式。
样式
shape.getStyle()
同持有了 Matrix 就能轻松操作图形的变形类似,所有继承于 Shape 的对象同样持有了一个 Style 对象,也就能轻松控制图形的样式了。使用 getStyle()
可以获得此对象,此对象包含如下四个方面:
- 全局,即透明度、组合操作和 CSS Filter;
- 阴影,即阴影颜色、模糊度和偏移量;
- 绘制,即填充和描边样式;
- 线型,即虚线、虚线偏移量、线宽和线条两端样式、线条连接处样式。
和 getMatrix().setAngle()
操作类似,比如改变上面示例中的那个矩形 rect 的线宽,可以使用 rect.getStyle().setLineWidth(4)
,要看到效果,只需要把 rect.fill()
改成 rect.stroke()
即可。
其实说 janvas 是浅层封装的缘由也在于此,ctx 的样式设置,全都赋能给了具体的图形,我们改变的不再是过程化的 ctx.lineWidth = 4;
,而是具体图形对象的 rect.getStyle().setLineWidth(4)
。
DotShape & FixedShape
并不是所有图形都能用固定的参数来描述,如 Rect 矩形可以用起始绘制点和宽高来描述,但是折线段就不同,它的数据点的数量并不是固定的,所以 janvas 抽象了 DotShape 类出来。
DotShape 简介
DotShape 同样也继承于 Shape,但它主要以点来驱动,并且它保留了所有数据点的位置,可用于对具体数据点的坐标值有需求的地方,比如 双摆运动,一根线段围绕某点进行旋转,需要求得另外一点的坐标值,用继承于 DotShape 的 Polyline 就很合适。
FixedShape 简介
<canvas>
原生提供了一个实验性的功能类,叫 Path2D
,它可以接收 svg
的 path
字符串来生成图形,FixedShape 意思是不变的图形,内部通过 Path2D
来实现绘制,本质上来源于 ctx.fill(path2d)
。
它同样继承于 Shape,所以可以使得毫无生气的 path
字符串,拥有全部 Shape 的功能,瞬间焕发出了活力。比如解析 SVG 字符串绘制老虎图的这个示例:Tiger。
结尾
目前 janvas 的图形部分结构如下,冒号之后代表所继承的父类:
Shape : Object 所有图形的基类
Arc : Shape 圆形
Sector : Arc 扇形
Ellipse : Arc 椭圆形
RegularPolygon : Arc 正多边形
RegularStar : RegularPolygon 正多角星
Rect : Shape 矩形
Roundrect : Rect 圆角矩形
Image : Rect 图片
Text : Shape 文字
Line : Shape 线段
BezierLine : Line 贝塞尔曲线(二、三阶)
Edge : Line 特殊连线
Triangle : Shape 三角形
Pin : Triangle 标记形
Dotshape : Shape 由数据点构成的图形
Polyline : DotShape 折线段
Bezier : Polyline 贝塞尔曲线(计算所有数据点)
Polygon : Polyline 多边形
PolyRect : Polygon 矩形(数据点驱动)
PolyArc : Polygon 正多边形(数据点驱动)
SuperEllipse : Polygon 超椭圆
SmoothLine : DotShape 经过指定点的平滑曲线
Dots : DotShape 多个圆形(数据点驱动)
FixedShape : Shape 不变的形状,可解析 svg path 字符串
FixedRect : FixedShape 不变的矩形
FixedArc : FixedShape 不变的圆形