fillRect(10, 10, 30, 30) 的意思是:先找到 (10, 10),再向右延伸 30、向下延伸 30。因此左上角是 (10, 10),右下角是 (40, 40),不是 (30, 30)。这里假定没有坐标变换,且宽、高为正。
参考分析与课后阅读(点击展开)
移动位置与改变大小,是两件事
把第一个 10 改成 60,矩形向右移动,但仍是 30 × 30;把第三个参数 30 改成 60,矩形左上角不动,但宽度变成 60。
这就是“位置+大小”的便利之处:移动时只改 x、y,保持宽高不变;改变大小时只改宽、高。注意,连续调用两次 fillRect 会画出两个矩形,不会自动把旧矩形搬走。
拖拽框选为什么常记录两个点?
按下鼠标时记录一个点 (x1, y1),拖动时记录当前位置 (x2, y2),松开时确定终点。这两个点是框选矩形的一对对角,不一定依次是左上角和右下角:也可以从右下方向左上方拖动。
因此,交互时记录两个点很方便;绘制时,再换算成“左上角+宽高”。下面假定两点都已换算为画布内部坐标,并把宽高统一为正数或零。
x = Math.min(x1, x2):取较小的横坐标,找到左边。
y = Math.min(y1, y2):取较小的纵坐标,找到上边。
width = Math.abs(x2 - x1):横向距离是宽度。
height = Math.abs(y2 - y1):纵向距离是高度。
Math.min 取较小值,Math.abs 取绝对值,让距离不因拖动方向而变成负数。之后用 ctx.strokeRect(x, y, width, height) 画出框选边框;strokeRect 的四个参数与 fillRect 含义相同,但只画边框。
例如:从 (120, 80) 向左上方拖到 (20, 20),得到左上角 (20, 20)、宽 100、高 60。反过来拖动,得到的矩形也一样。
两种表示方式并不矛盾:两个点便于记录拖拽,位置与宽高便于控制绘制。
内部尺寸与屏幕大小不要混为一谈
本页画布内部宽 640、高 360;在默认坐标系中,右边界是 x=640,下边界是 y=360。如果 CSS 把整张画布缩小显示,内部坐标仍然不变,只是整个画面在屏幕上缩小了。
ctx 是对象的名字,还是固定语法?
canvas 指向画布元素,ctx 指向从这块画布取得的二维绘图上下文对象。fillRect 是这个对象的方法;ctx.fillRect(...) 表示调用该方法,不是创建一个新的 HTML 元素。
ctx 是我们起的变量名,不是 JavaScript 关键字。也可以命名为 context,但后面的调用也要相应改成 context.fillRect(...)。本例使用 const 声明,因此不能给 ctx 重新赋值,仍然可以通过它调用绘图方法。