首页 HTML5 Dive Into HTML5:绘图(续)

Dive Into HTML5:绘图(续)

0 2

坐标系统

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");

现在,我们可以画线了!

路径

IEFirefoxSafariChromeOperaiPhoneAndroid
7.0+*3.0+3.0+3.0+10.0+1.0+1.0+
IE 7.0 和 8.0 需要使用第三方 JS 库explorercanvas;IE9 原生支持。

想象一下使用钢笔绘画。你并不想直接拿笔在纸上就画,因为你可能犯错误。相反的,你会先用铅笔在纸上打上格子,或者轻轻划出痕迹,然后就可以用钢笔直接把这些铅笔痕迹描画出来。

每个画布都有一个路径。定义路径就像使用铅笔绘图。你可以画任何东西,但是这些东西不会出现在最终的作品中,直到你用钢笔重新描一遍。

为了使用“铅笔”画一条直线,我们需要以下两个步骤:

  1. moveTo(x, y)将铅笔移动到一个指定的起始点;
  2. 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仍然是白色的。(fillStylestrokeStyle在你开始新路径的时候不会被重置。)没关系,因为我们仅仅是运行了“铅笔”方法。但是当我们实际绘制的时候,也就是调用“钢笔”方法的时候,我们就应当将strokeStyle设置为黑色。否则,这两个箭头就是白色的,我们就很难看清它们了。下面的几行就是我们改变颜色,并实际绘制的代码:

context.strokeStyle = "#000";
context.stroke();

最后,这就是我们的最终结果:


发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2