canvas 中的自定义图形
文章目录
以下属于 canvas 开发中 JavaScript 面向对象继承方面的内容
前言
一直以来,我都不认为 janvas 是一个框架,我更倾向于它是一个封装库,它提供了两个方面的内容:
- 基础内容,一些常用的东西,用于解决重复代码问题,如 Utils/Canvas。
- 图形封装,janvas 的本质,用于解决 canvas 中无对象的问题
以下通过对比原始绘图方式与继承的区别,来说明为何要使用 janvas。
准备工作
通常,要开发 canvas,页面中应该有这样一个东西:
<canvas id="canvas"></canvas>
可以通过以下语句获取这个 canvas 对象的引用:
var canvas = document.getElementById("canvas");
因为 canvas 仅仅是个“画布”,要绘制内容,还需要“画笔”,也就是:
var ctx = canvas.getContext("2d");
这样子,我们的准备工作就完成了。
原始绘图方式
通常,我们拿到一个画笔,想在坐标 10, 30 的位置填充一个宽高分别为 80, 40 的矩形,我们会这么做:
ctx.beginPath();
ctx.rect(10, 30, 80, 40);
ctx.fill();
结果如下:
进一步抽象一下,我们想在坐标 x, y 的位置填充一个宽高分别为 w, h 的矩形,我们会这么做:
function fillRect(x, y, w, h) {
ctx.beginPath();
ctx.rect(x, y, w, h);
ctx.fill();
}
接下来,只需要调用 fillRect(10, 30, 80, 40);
函数,即可复用这一段绘制矩形的代码。
以上就是面向过程的思维方式,封装一个函数,做到某一件事情。
可是接下来呢?点击了某个区域,如何判断坐标是否在这个矩形内部?我们想在鼠标移动到矩形的时候,改变它的颜色又如何做到?我们如何控制这个矩形的变形呢?
毫无疑问,以上这些内容都需要编写大量额外的代码,但接下来的操作,可能会让你对 janvas 刮目相看。
继承于 janvas.Shape
现在,我们抽象一个矩形类,代码如下:
function MyRect(ctx, sx, sy, w, h) {
janvas.Shape.call(this, ctx, sx, sy);
this.w = w;
this.h = h;
}
MyRect
构造函数需要传入五个对应参数,通过调用 janvas.Shape.call
将前三个参数,ctx
画笔,sx, sy
起始绘制点 x, y,传给父类,将后两个参数保存给自身。
接下来,只需要写一行继承代码:
janvas.Utils.inheritPrototype(MyRect, janvas.Shape);
见证奇迹的时刻马上发生,我们重写原型链上的 process()
方法:
MyRect.prototype.process = function () {
this.ctx.beginPath();
this.ctx.rect(this.sx, this.sy, this.w, this.h);
};
可以看到,绘制代码是在 process()
方法中实现的,相比原始绘图方式,多了抽象矩形类以及继承的一行语句,接着我们实例化一个矩形对象,看看它有什么作用:
var rect = new MyRect(ctx, 10, 30, 80, 40);
rect.fill();
这两行代码,同样的完成了矩形的绘制!
同样的,还可以试试调用以下两行代码,得到坐标点是否在矩形内:
console.log(rect.isPointInPath(5, 5)); // output: false
console.log(rect.isPointInPath(15, 40)); // output: true
继承的作用发挥出来了,它具备了探测坐标点是否在图形内的方法!
可以改变矩形的位置,并改变矩形的填充颜色,再次绘制:
rect.setStart(20, 40).getStyle().setFillStyle("pink");
rect.fill();
效果图如下:
实现过继承的它,同样具有了能改变自身位置以及颜色的能力。
也就是说,面向过程封装的函数方法中的绘制部分,如果以继承 janvas.Shape
的方式移植,会使得毫无生气的绘制过程变得对象化,使得:
- 这个对象不仅具有了自己的属性,还继承了判断坐标点是否在此对象内的方法
- 能单独给此对象设置相关的样式
- 能给对象设置以上代码没展示出来的各种变形参数,如错切、缩放、旋转以及平移能力。
其实,这以上就是 janvas.Rect
类的简化版实现源码。
推荐阅读