【canvas学习笔记二】绘制图形
上一篇我们已经讲述了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)画的。
路径
绘制路径的一般步骤如下:
- 创造路径
- 绘制路径
- 闭合路径
- 填充路径或给路径描边
绘制的代码如下:
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学习笔记二】绘制图形的更多相关文章
- WebGL学习笔记二——绘制基本图元
webGL的基本图元点.线.三角形 gl.drawArrays(mode, first,count) first,代表从第几个点开始绘制即顶点的起始位置 count,代表绘制的点的数量. mode,代 ...
- canvas学习(二):渐变与曲线的绘制
canvas学习(二):渐变与曲线的绘制 一:createLinearGradient()线性渐变: 二:createLinearGradient() 放射状/圆形渐变: 三:createPatter ...
- qml学习笔记(二):可视化元素基类Item详解(上半场anchors等等)
原博主博客地址:http://blog.csdn.net/qq21497936本文章博客地址:http://blog.csdn.net/qq21497936/article/details/78516 ...
- WPF的Binding学习笔记(二)
原文: http://www.cnblogs.com/pasoraku/archive/2012/10/25/2738428.htmlWPF的Binding学习笔记(二) 上次学了点点Binding的 ...
- AJax 学习笔记二(onreadystatechange的作用)
AJax 学习笔记二(onreadystatechange的作用) 当发送一个请求后,客户端无法确定什么时候会完成这个请求,所以需要用事件机制来捕获请求的状态XMLHttpRequest对象提供了on ...
- [Firefly引擎][学习笔记二][已完结]卡牌游戏开发模型的设计
源地址:http://bbs.9miao.com/thread-44603-1-1.html 在此补充一下Socket的验证机制:socket登陆验证.会采用session会话超时的机制做心跳接口验证 ...
- JMX学习笔记(二)-Notification
Notification通知,也可理解为消息,有通知,必然有发送通知的广播,JMX这里采用了一种订阅的方式,类似于观察者模式,注册一个观察者到广播里,当有通知时,广播通过调用观察者,逐一通知. 这里写 ...
- java之jvm学习笔记二(类装载器的体系结构)
java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...
- Java IO学习笔记二
Java IO学习笔记二 流的概念 在程序中所有的数据都是以流的方式进行传输或保存的,程序需要数据的时候要使用输入流读取数据,而当程序需要将一些数据保存起来的时候,就要使用输出流完成. 程序中的输入输 ...
随机推荐
- Oracle查询数据出来乱码问题?
为什么Oracle 查询出来的数据会产生乱码? 安装的数据库和客户端编码编码不一致就会产生乱码,要想解决此问题改一下客户端的编码即可 1. select * from table; 如果是这种问题 ...
- Bash中的数学计算
一.整数计算 1.整数 $delare -i num$num=5+5$echo $num10 $num="5 + 8"$echo $num13注意:算式中如果有空格,需要用引号引起 ...
- centos文件权限详解
假设回显信息为 ①-②rws③r-x④r-x ⑤1 ⑥root ⑦root ⑧430540 ⑨Dec 20 18:27 ⑩/usr/sbin/passwd ,现在逐一分析其内容. ①. 首字符-,表 ...
- C#中==运算符
在这篇博客中,我们将介绍如下内容: ==运算符与基元类型 ==运算符与引用类型 ==运算符与String类型 ==运算符与值类型 ==运算符与泛型 ==运算符与基元类型 我们分别用两种方式比较两个整数 ...
- Codeforces Round #419 (Div. 2)
1.题目A:Karen and Morning 题意: 给出hh:mm格式的时间,问至少经过多少分钟后,该时刻为回文字符串? 思路: 简单模拟,从当前时刻开始,如果hh的回文rh等于mm则停止累计.否 ...
- php根据经纬度获取城市名
/*php根据经纬度获取城市名*/ function get_my_addr_infos(){ $ch = curl_init(); $timeout = 5; $lat = $list['info' ...
- Ionic2+ 环境搭建
ionic2+官方guide 基础环境安装 nodejs安装 ionic,cordova安装 npm install -g ionic cordova 项目创建 ionic start MyIonic ...
- 个人开源项目testall 持续更新中···
项目在GitHub上:https://github.com/x113773/testall ,喜欢的给个星星呀,亲~ 打算把用到过的和学习过的,所有前后端技术都集成到这个项目里,并在issues里配以 ...
- NPOI 表头、页眉页脚重复设置
NPOI 是 POI 项目的 .NET 版本.POI是一个开源的Java读写Excel.WORD等微软OLE2组件文档的项目. 使用 NPOI 你就可以在没有安装 Office 或者相应环境的机器上对 ...
- 剖析Elasticsearch集群系列第一篇 Elasticsearch的存储模型和读写操作
剖析Elasticsearch集群系列涵盖了当今最流行的分布式搜索引擎Elasticsearch的底层架构和原型实例. 本文是这个系列的第一篇,在本文中,我们将讨论的Elasticsearch的底层存 ...