上一篇我们已经讲述了canvas的基本用法,学会了构建canvas环境。现在我们就来学习绘制一些基本图形。

坐标

canvas的坐标原点在左上角,从左到右X轴坐标增加,从上到下Y轴坐标增加。坐标的一个单元是1像素。示意如下:

矩形

canvas可以绘制的多边形只有矩形,其他都要通过线段拼接而成。

绘制矩形有三个API:

fillRect(x, y, width, height)
绘制一个填充的矩形。 strokeRect(x, y, width, height)
绘制一个只有描边的矩形。 clearRect(x, y, width, height)
清除特定的矩形区域,使之变成透明。

参数说明:

x,y是矩形左上角的坐标,width和height是矩形的宽和高。

代码示例:

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d'); ctx.fillRect(25, 25, 100, 100);
ctx.clearRect(45, 45, 60, 60);
ctx.strokeRect(50, 50, 50, 50);
}
}

效果如下:

最外面那个黑色的矩形就是第一行代码ctx.fillRect(25, 25, 100, 100)画的填充矩形。中间的透明矩形就是第二行代码ctx.clearRect(45, 45, 60, 60)清除的矩形,再中间的描边矩形就是第三行代码ctx.strokeRect(50, 50, 50, 50)画的。

路径

绘制路径的一般步骤如下:

  1. 创造路径
  2. 绘制路径
  3. 闭合路径
  4. 填充路径或给路径描边

绘制的代码如下:

ctx.beginPath();    // 创造路径
// 路径绘制,先省略,后面再路径绘制的方法
ctx.closePath(); // 闭合路径
ctx.stroke(); // 描边
// ctx.fill(); // 换成这行命令就是填充

在这个一般步骤中,只有闭合路径步骤可以省略,其他步骤都是不可省略的。如果不需要画闭合的图形,可以不闭合路径。

接下来讲述绘制路径的方法。

移动画笔

moveTo(x, y)这个方法可以移动画笔的坐标,将画笔移动到坐标(x, y)的位置。

用beginPath()创造路径时,要先用moveTo(x, y)确定起始位置,除非是圆弧之类不需要初始点的路径绘制方法。

线段

lineTo(x, y)方法绘制一条线段,x, y指定了线段末尾的坐标。配合moveTo(x, y)方法就可以画出线段。

示例:

      var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(0,0)
ctx.lineTo(50,50);
ctx.lineTo(100,50);
ctx.stroke(); ctx.beginPath();
ctx.moveTo(75,0)
ctx.lineTo(70,50);
ctx.lineTo(150,50);
ctx.closePath();
ctx.stroke();
}

效果如下:

再来画个三角形:

var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d'); ctx.beginPath();
ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.fill();
}

效果:

矩形******

同样的,路径方法里也有绘制矩形的方法。

rect(x, y, width, height)

x,y是矩形左上角坐标,width是矩形的宽,height是矩形的高。

这个方法和前面说的矩形不同在于,这个方法是路径绘制方法,要配合beginPath()和stroke()之类的方法来使用的。要先创建路径,然后用这个方法绘制矩形,再stroke()或fill(),否则画不出来。

圆弧

绘制圆弧有两个方法:

arc(x, y, radius, startAngle, endAngle, anticlockwise)

arcTo(x1, y1, x2, y2, radius)

第一个方法中的x, y参数指定了圆弧的圆心坐标。

radius参数是圆弧的半径长度。

startAngle参数是圆弧起始的弧度,endAngle是圆弧结束的弧度,单位是弧度,不是度。

anticlockwise是个布尔值,设置圆弧是顺时针画还是逆时针画,当anticlockwise为true是逆时针,为false是顺时针,这个值缺省的时候默认是顺时针。

Tips:

角度的单位是弧度,可以用这个表达式把度数转换成弧度:radians = (Math.PI/180)*degrees

下面来看一个有趣的例子:

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d'); for (var i = 0; i < 4; i++) {
for (var j = 0; j < 3; j++) {
ctx.beginPath();
var x = 25 + j * 50; // x coordinate
var y = 25 + i * 50; // y coordinate
var radius = 20; // Arc radius
var startAngle = 0; // Starting point on circle
var endAngle = Math.PI + (Math.PI * j) / 2; // End point on circle
var anticlockwise = i % 2 !== 0; // clockwise or anticlockwise ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise); if (i > 1) {
ctx.fill();
} else {
ctx.stroke();
}
}
}
}
}

这段代码总共画了四行三列的圆弧。相邻的圆弧圆心X轴和Y轴都相距50像素,圆弧的半径都是20像素,每列圆弧的弧度相差90度。第一行和第三行都是顺时针,第二行和第四行都是逆时针。第一行和第二行描边,第三行和第四行填充。

效果是这样的:

第二个圆弧方法比较复杂,效果难以控制,它其实是画一段带直线的圆弧。我尽量把它说明白。

arcTo(x1, y1, x2, y2, radius)

参数x1, y1, x2, y2分别是两个控制点的坐标,radius是圆弧的半径长度。

下面用一个例子来说明控制点的意义:

ctx.beginPath();
ctx.moveTo(150, 20);
ctx.arcTo(150, 100, 50, 20, 30);
ctx.stroke(); ctx.fillStyle = 'blue';
// base point
ctx.fillRect(150, 20, 10, 10); ctx.fillStyle = 'red';
// control point one
ctx.fillRect(150, 100, 10, 10);
// control point two
ctx.fillRect(50, 20, 10, 10);

效果如图:



蓝色方块的左上角坐标是moveTo的坐标,右下方的红色方块的左上角坐标是第一个控制点的坐标,左上方的红色方块的左上角坐标是第二个控制点的坐标。

注意,蓝色方块和圆弧之间是有一小段直线的。

注意,这个方法的开始是必须用moveTo方法来指定起始点的,也就是蓝色方块的位置,否则画不出来。

这个方法难以控制,所以通常不去用它。

贝塞尔和二次曲线

现在我要说两个更难以控制的绘图API了,二次贝塞尔曲线和三次贝塞尔曲线。

quadraticCurveTo(cp1x, cp1y, x, y)

bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)

上面的是二次曲线,下面的是三次曲线。下面用一张图来说明这两个曲线方法。



这两个方法的区别就是一个只有一个控制点(图中红点),一个有两个控制点。

参数cp1x, cp1y, cp2x, cp2y是控制点的坐标,x, y是曲线的末端的坐标值,也就是图中的蓝点中的其中一个。

因为不是在Illustrator等图形编辑软件里,不能可视化地控制点,所以用这个两个方法绘制图形相当需要耐心。

举个二次曲线画图的例子吧:

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d'); // Quadratric curves example
ctx.beginPath();
ctx.moveTo(75, 25);
ctx.quadraticCurveTo(25, 25, 25, 62.5);
ctx.quadraticCurveTo(25, 100, 50, 100);
ctx.quadraticCurveTo(50, 120, 30, 125);
ctx.quadraticCurveTo(60, 120, 65, 100);
ctx.quadraticCurveTo(125, 100, 125, 62.5);
ctx.quadraticCurveTo(125, 25, 75, 25);
ctx.stroke();
}
}

效果:



三次曲线的例子我就不说了。反正大家通常肯定不会用这两个方法去绘制图形的。

有个贝塞尔曲线可视化的网页,对画贝塞尔曲线有帮助。戳这里

2D路径对象

实际绘图过程中,常常需要绘制很多路径,其中很多可能是重复的。2D路径对象的作用就是存储重复使用的路径,提高性能,简化代码,新版浏览器都支持。

我先说一下初始化2D路径对象的方法,有三种:

new Path2D(); // empty path object

new Path2D(path); // copy from another Path2D object

new Path2D(d); // path from SVG path data

构造函数可以不含参数,这样创建的就是一个空的路径对象,同样也可以传一个2D路径对象作参数,这样就拷贝了一个2D路径对象,还可以传一段SVG路径数据,比如这样:var p = new Path2D('M10 10 h 80 v 80 h -80 Z');

还是用例子来说明Path2D这个对象比较清楚:

function draw() {
var canvas = document.getElementById('canvas');
if (canvas.getContext) {
var ctx = canvas.getContext('2d'); var rectangle = new Path2D();
rectangle.rect(10, 10, 50, 50); var circle = new Path2D();
circle.moveTo(125, 35);
circle.arc(100, 35, 25, 0, 2 * Math.PI); ctx.stroke(rectangle);
ctx.fill(circle);
}
}

在这个例子中,创建了一个存储了矩形路径的对象,和一个存储了圆的路径对象,结果如下:



所有上面说过的路径绘制方法Path2D这个对象都能使用。

另外Path2D这个对象还有一个拼接路径的方法:

Path2D.addPath(path [, transform])

Adds a path to the current path with an optional transformation matrix.

把一段路径传进去就可以将这两段路径拼接起来,带有一个可选参数,是一个SVG变换矩阵。

举个栗子:

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d'); // Create a new path with a rect
var p1 = new Path2D();
p1.rect(0, 0, 100, 100); // Create another path with a rect
var p2 = new Path2D();
p2.rect(0, 0, 100, 100); // Create transformation matrix that moves vertically 300 points to the right
var m = document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGMatrix();
m.a = 1; m.b = 0;
m.c = 0; m.d = 1;
m.e = 300; m.f = 0; // add the second path to the first path
p1.addPath(p2, m); // Finally, fill the first path onto the canvas
ctx.fill(p1);

【canvas学习笔记二】绘制图形的更多相关文章

  1. WebGL学习笔记二——绘制基本图元

    webGL的基本图元点.线.三角形 gl.drawArrays(mode, first,count) first,代表从第几个点开始绘制即顶点的起始位置 count,代表绘制的点的数量. mode,代 ...

  2. canvas学习(二):渐变与曲线的绘制

    canvas学习(二):渐变与曲线的绘制 一:createLinearGradient()线性渐变: 二:createLinearGradient() 放射状/圆形渐变: 三:createPatter ...

  3. qml学习笔记(二):可视化元素基类Item详解(上半场anchors等等)

    原博主博客地址:http://blog.csdn.net/qq21497936本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78516 ...

  4. WPF的Binding学习笔记(二)

    原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...

  5. AJax 学习笔记二(onreadystatechange的作用)

    AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...

  6. [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计

    源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...

  7. JMX学习笔记(二)-Notification

    Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...

  8. java之jvm学习笔记二(类装载器的体系结构)

    java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...

  9. Java IO学习笔记二

    Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...

随机推荐

  1. 解决jmeter请求不成功或者报403错误

    有同学遇到这种情况,jmeter请求一个网站,各项参数填写正确,可是响应是403,同样的请求放在浏览器执行就没有问题: 这是因为被请求的网站做了请求来源过滤,来源不明的请求拒绝访问,我们需要在jmet ...

  2. TypeScript技巧集锦(陆续更新)

    在C++项目中编译TypeScript(以下简称ts) 编辑ts文件的属性,项类型选择"自定义生产工具". 命令行输入tsc所在位置与编译参数,我的是"C:\Progra ...

  3. java io 流

    Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  4. EF添加

    1.添加单个模型(CreatRule()是构造模型)(Shop_ActivityRuleProduct是类) var rule = CreatRule(model); var ruled = db.S ...

  5. IBM的人工智能“沃森”首次确诊罕见白血病,只用了10分钟!

    患者为一名60岁的女性,最初根据诊断结果,显示她患了急髓白血病.但在经历各种疗法后,效果并不明显. 根据东大医学院研究人员Arinobu Tojo的说法,他们利用Watson系统来对此病人进行诊断.系 ...

  6. VMware-VCSA-6.5安装过程

    1.新建虚拟机 2.选择从OVF或OVA文件导入 3.给虚拟机命名,并选择OVF文件. 4.选择虚拟机的存储位置.这里没有配置共享存储宿,这里选择的宿主机的存储. 5.许可协议同意就OK了. 6.部署 ...

  7. document.getElementById("searchForm").submit is not a function

    document.getElementById("searchForm").submit is not a function在用userForm.submit() 提交表单的时候, ...

  8. python中的字符串编码

    获取字符串的编码类型: encodingdate = chardet.detect(str) chardet用于实现字符串的编码类型检测 chardet的下载地址:https://pypi.pytho ...

  9. 【Android Developers Training】 4. 启动另一个Activity

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  10. accp8.0转换教材第1章多线程理解与练习

    一.单词部分: ①process进程 ②current当前的③thread线程④runnable可获取的 ⑤interrupt中断⑥join加入⑦yield产生⑧synchronize同时发生 二.预 ...