坐标系统
canvas 可以看作一个二维表格。坐标 (0, 0) 位于画布左上角。X轴正方向沿画布向右,Y轴正方向沿画布向下。
上面的坐标图就是使用<canvas>
进行绘制的。它由以下部分组成:
- 一系列白色的竖直线;
- 一系列白色的水平线;
- 两条黑色水平线;
- 组成箭头的两个短黑线;
- 两条黑色竖直线;
- 组成另一个尖头的两个短黑线;
- 字母 x;
- 字母 y;
- 左上角附近的文本 (0, 0);
- 右下角附近的文本 (500, 375);
- 左上角的一个点和右下角的一个点。
下面我们来自己把这个坐标图画出来。首先,我们定义一个<canvas>
元素。这个<canvas>
定义宽和高,并且我们给定一个 id:
<canvas id="c" width="500" height="375"></canvas>
然后,我们需要一段脚本在 DOM 中找到这个<canvas>
,并且获得其 context:
var c_canvas = document.getElementById("c"); var context = c_canvas.getContext("2d");
现在,我们可以画线了!
路径
IE | Firefox | Safari | Chrome | Opera | iPhone | Android |
7.0+* | 3.0+ | 3.0+ | 3.0+ | 10.0+ | 1.0+ | 1.0+ |
IE 7.0 和 8.0 需要使用第三方 JS 库explorercanvas;IE9 原生支持。 |
想象一下使用钢笔绘画。你并不想直接拿笔在纸上就画,因为你可能犯错误。相反的,你会先用铅笔在纸上打上格子,或者轻轻划出痕迹,然后就可以用钢笔直接把这些铅笔痕迹描画出来。
每个画布都有一个路径。定义路径就像使用铅笔绘图。你可以画任何东西,但是这些东西不会出现在最终的作品中,直到你用钢笔重新描一遍。
为了使用“铅笔”画一条直线,我们需要以下两个步骤:
moveTo(x, y)
将铅笔移动到一个指定的起始点;lineTo(x, y)
画一条到指定的终止点的直线。
你调用的moveTo()
和lineTo()
越多,你画的图就越大越复杂。这些不过是“铅笔”方法——你愿意调用多少次就调用多少次,但是,除非你调用了“钢笔”方法,否则你是看不到什么东西的。
知道了原理,我们开始准备画图。
// 绘制竖直线 for (var x = 0.5; x < 500; x += 10) { context.moveTo(x, 0); context.lineTo(x, 375); } // 绘制水平线 for (var y = 0.5; y < 375; y += 10) { context.moveTo(0, y); context.lineTo(500, y); }
这些都是“铅笔”方法。此时,我们的画布上什么都不会有。我们需要调用一个“钢笔”方法,把我们定义的这些路径实际画出来。
context.strokeStyle = "#eee"; context.stroke();
stroke()
就是一个“钢笔”方法。它能够根据你前面创建的复杂的moveTo()
和lineTo()
路径,将其实际画到画布上。strokeStyle
定义线条的颜色。最终结果是:
Q: 为什么 x 和 y 都要从 0.5 开始?为什么不是 0?
A: 我们把每一个像素想象成一个大的方块。整数的坐标值 (0, 1, 2…) 出现在方块的边界。如果你要绘制整数坐标值之间的一个单位宽的线,那么它将覆盖像素块的两侧的边界,其结果是最终的线将有两个像素宽。为了绘制只有一个像素宽的线,你就得偏移 0.5 个像素。
例如,如果你要绘制从 (1, 0) 到 (1, 3) 的直线(注意,坐标值出现在像素的边界处),浏览器将绘制一条在 x=1 两侧多覆盖 0.5 像素的直线。因为屏幕上不能显示半个像素,所以,我们的线就变成2像素宽了。
但是,如果你绘制从 (1.5, 0) 到 (1.5, 3) 的线,浏览器就可以绘制一条在 x=1.5 的两侧多覆盖 0.5 像素的直线,其结果是这条线只有一个像素宽。
感谢 Jason Johnson 提供示例图像。
下面我们来绘制水平箭头。路径上所有的直线和曲线都是用同一颜色(或者同一渐变)绘制的。我们想用另外的颜色绘制箭头——黑色,而不是白色——所以我们得重新开始一个路径。
context.beginPath(); // 开始新的路径 context.moveTo(0, 40); context.lineTo(240, 40); context.moveTo(260, 40); context.lineTo(500, 40); context.moveTo(495, 35); context.lineTo(500, 40); context.lineTo(495, 45);
竖直箭头也是这样的。不过,竖直箭头和水平箭头颜色一样,所以我们就不需要开始新的路径了。这样,虽然两个箭头不在一起,但是它们实际是在同一个路径之中的。
context.moveTo(60, 0); context.lineTo(60, 153); context.moveTo(60, 173); context.lineTo(60, 375); context.moveTo(65, 370); context.lineTo(60, 375); context.lineTo(55, 370);
我曾经说过,这些箭头是黑色的,但是strokeStyle
仍然是白色的。(fillStyle
和strokeStyle
在你开始新路径的时候不会被重置。)没关系,因为我们仅仅是运行了“铅笔”方法。但是当我们实际绘制的时候,也就是调用“钢笔”方法的时候,我们就应当将strokeStyle
设置为黑色。否则,这两个箭头就是白色的,我们就很难看清它们了。下面的几行就是我们改变颜色,并实际绘制的代码:
context.strokeStyle = "#000"; context.stroke();
最后,这就是我们的最终结果: