canvas元素相称于在页面上放置了一块画布,其本身并不能实现图形绘制,也没有任何外不雅观,只是一块无色透明的区域,须要几组API进行操作,最主要的是便是具备基本绘图能力的2D高下文(context)及文本API,利用这些API,可以添加线条、图片、笔墨、绘画、加入高等动画等;现在还可以利用用于绘制硬件加速的WebGL的3D高下文;
基本用法:要利用canvas元素,必须先设置其width和height属性,指定可以绘图的区域大小;该标签中的内容,为后备信息,当浏览器不支持canvas时,就会显示这些信息,如:
<canvas id="drawing" width="400" height="300">你的浏览器不支持canvas</canvas>
canvas 的默认大小为 300px×150px,其对应的DOM元素也具有width和height属性,并且,可以通过CSS设置其样式;
<style>#drawing{background-color: lightblue; width:400px; height:300px;}</style><canvas id="drawing"></canvas><script>var drawing = document.getElementById("drawing");console.log(drawing);console.log(drawing.width + ":" + drawing.height); // 还是默认的300pxX150px</script>
canvas是无色透明的,如果不添加任何样式或不绘制任何图形,在页面上是看不到该元素的;
绘制图形的步骤:1、取得canvas元素:
var canvas = document.getElementsByTagName("canvas")[0];
2、取得高下文(context):进行图形绘制时,须要利用到图形高下文(graphics context),图形高下文是一个封装了很多绘图功能的工具;利用canvas工具的getContext()方法得到图形高下文,在该方法中须要传入“2d”;
var context = canvas.getContext("2d");
3、设定绘图样式:在绘制的时候,首先要设定好绘图的样式,然后再调用有关方法进行图形的绘制,如:fillStyle属性,设定添补的样式,在该属性中填入颜色值;strokeStyle属性,设置边框的样式,值也是颜色值;利用lineWidth属性设置边框的宽度;
context.fillStyle = "yellow";context.strokeStyle = "green";context.lineWidth = 10;
4、添补与绘制边框:利用相应的绘制方法进行绘制,如利用fill()方法添补与stroke()方法描边;添补是指添补图形内部;描边是指不添补图形内部,只绘制图形的边框;对付矩形来说, fillRect()方法绘制矩形内部,strokeRect()方法绘制矩形边框;
context.fillRect(0,0,100,100);context.strokeRect(0,0,100,100);
2D高下文(context):利用canvas工具的getContext()方法得到图形高下文,在该方法中须要传入“2d”,就会获取一个在2D环境中绘制图形的CanvasRenderingContext2D类型的高下文工具,被称为渲染高下文(The rendering context),目前只支持2D,没有3D之类的,但不用除今后会扩展;
// 确定浏览器是否支持canvas元素if(canvas.getContext){var context = canvas.getContext("2d");console.log(context); // CanvasRenderingContext2D// ...}
利用2D绘图高下文供应的方法,可以绘制大略的2D图形,比如矩形、弧线和路径;如果利用它们结合不同的添补和描边样式,就能绘制出非常繁芜的图形;如:
context.fillStyle = "rgba(0, 0, 200, 0.5)";context.fillRect (50, 50, 100, 100);
每个canvas只能有一个高下文工具,多次调用getContext()返回的是同一个工具;
var context = drawing.getContext("2d");var context1 = drawing.getContext("2d");console.log(context === context1); // true
其拥有一个canvas属性,是与当前高下文关联的HTMLCanvasElement工具的只读引用;
canvas的尺寸和坐标:canvas默认的坐标系因此<canvas>元素的左上角(0, 0)为坐标原点;所有图形元素的坐标都基于这个原点打算,x值越大表示越靠右,y值越大表示越靠下,反之亦然;
画布的尺寸和坐标系都因此CSS像素为单位的;
默认情形下,canvas的width和height表示水平和垂直两个方向上可用的像素数目;
画布的尺寸和CSS定义的尺寸是完备不同的观点:画布的尺寸是由<canvas>元素的width和height属性定义的,而CSS中定义的尺寸是画布元素在页面中显示的尺寸;如果两者定义的尺寸不相同,则画布上的像素会自动缩放,以适宜CSS中定义的尺寸;其余,画布中的坐标,也是根据画布的width和height属性定义的;
画布上的点可以利用浮点数来指定坐标,但是它们不会自动转换成整型值,画布会采取反锯齿的办法来仿照部分添补的像素;
画布的尺寸是不能随意变动的,除非完备重置画布;重置画布的width和heigth属性,会清空全体画布,擦除当前的路径并且会重置所有的图形属性(包括当前的变换和裁剪区域)为初始状态;
绘制基本图形:
添补和描边:
2D高下文的基本绘图操作是添补和描边;添补,便是用指定的样式(颜色、渐变或图像)添补图形;描边,便是只在图形的边缘画线;(这个,前面讲过了)
大多数2D高下文操作都会细分为添补和描边两个操作,而操作的结果取决于两个属性:fillStyle和strokeStyle;
这两个属性的值可以是字符串、渐变工具或模式工具,而且它们的默认值都是“#000000”;如果为它们指定表示颜色的字符串值,格式是与CSS同等的,如:
// ...context.fillStyle = "#0000ff";context.strokeStyle = "red";context.fillRect(50,50,200,100);context.strokeRect(50,200,200,100);// ...
绘制矩形:在canvas中,绘图有两种形式:矩形和路径,除了矩形,所有其他类型的图形都是通过一条或者多条路径组合而成的;
矩形是唯一一种可以直接在2D高下文中绘制的形状,其分别利用fillRect(x,y,width,height)、strokeRect(x,y,width,height)方法来绘制添补矩形和绘制矩形边框,两者均吸收4个参数:矩形的x、y坐标、矩形的宽和高,单位为像素;添补和描边的颜色通过fillStyle和strokeStyle两个属性指定,如:
function draw(id){var drawing = document.getElementById(id);if(drawing.getContext){var context = drawing.getContext("2d");// 绘制画布背景context.fillStyle = "#EEEEFF";context.fillRect(0,0,canvas.width, canvas.height);// 绘制蓝色矩形context.fillStyle = "rgba(0,0,255,0.5)";context.strokeStyle = "blue";context.fillRect(50,50,100,100);context.strokeRect(50,50,100,100);// 绘制赤色矩形context.strokeStyle = "#ff0000";context.fillStyle = "rgba(255,0,0,0.2)";context.strokeRect(100,100,100,100);context.fillRect(100,100,100,100);}}draw("canvas");
示例:绘制国际象棋棋盘:
for (i=0; i<8; i++){ // 行for (j=0; j<8; j++){ // 列if ((i+j) % 2 == 0)context.fillStyle = 'black';elsecontext.fillStyle= 'white';context.fillRect(j50, i50, 50, 50);}}
示例:绘制柱状图表
var data = [100, 50, 20, 30, 100];var colors = ["red", "orange", "yellow", "green", "blue"];context.fillStyle = "white";context.fillRect(0, 0, canvas.width, canvas.height);for(var i=0; i<data.length; i++){var v = data[i];context.fillStyle = colors[i];context.fillRect(25+i50, 280-v2, 50, v2);}
关于矩形,还有一个clearRect(x, y, width, height)方法,该方法将指定的矩形区域中的图形进行擦除,使得矩形区域中的颜色全部变为透明,其参数与前两个方法同等;如:
// 在两个矩形重叠的部分打消一个小矩形context.clearRect(110,110,30,30);// 重新绘制context.strokeStyle = "black";context.fillStyle = "black";context.fillRect(250,25,100,100);context.clearRect(270,45,60,60);context.strokeRect(275,50,50,50);
以上绘制的矩形,由于没用到路径,以是一旦绘制,会立即生效,并在canvas中渲染;
清理画布和规复画布状态:
<input type="button" value="清空画布" onClick="clearMap();" /><script type="text/javascript">var c = document.getElementById("canvas");var context = c.getContext("2d");context.fillStyle = "red";context.strokeStyle = "yellow";context.beginPath();context.arc(200,150,100,-Math.PI5/6,true);context.stroke();function clearMap() {context.clearRect(0,0,300,200);}</script>绘制路径:图形的基本元素是路径,其是通过不同颜色和宽度的线段或曲线相连形成的不同形状的点的凑集;一个路径,一样平常都是闭合的;利用路径绘制图形须要一些步骤:创建路径起始点、利用干系命令画出路径、之后把路径封闭;一旦路径天生,就可以通过描边或添补路径区域来渲染图形;
2D高下文供应了多个绘制路径的方法,通过这些方法就可以绘制出繁芜的图形和线条:beginPath()方法,新建一条路径,不须要参数,表示要开始绘制新路径,其后再调用一系列方法来实际地绘制路径;其后绘制的路径都是子路径,都属于同一条路径;再利用moveTo()方法,移动当前点,它并不绘制线条,它的浸染便是确定路径起始点;接着利用绘图方法,例如:lineTo()方法,可以从上一点开始绘制一条线段;或者利用rect()方法,绘制一个矩形路径;再或者利用arc()方法,绘制一条弧线,等等;closePath()方法,闭合路径;此时,就会自动绘制一条连接到路径出发点的线条,路径的创建事情就完成了;如:
context.beginPath();context.moveTo(50,50);context.lineTo(100,50);context.arc(50,50,50,0,Math.PI/2);context.moveTo(200,70);context.arc(200,70,50,0,Math.PI/2,true);context.rect(300,50,100,100);context.closePath();
如果路径已经完成,可以设置绘图样式,并进行绘图,如用fillStyle设置添补样式,并调用fill()方法添补,天生实心的图形;或利用strokeStyle属性设置边框样式,并调用stroke()方法对路径描边,即勾勒图形轮廓;
// ...context.fillStyle = "green";context.fill();context.strokeStyle = "yellow";context.lineWidth = 10;context.stroke();
实质上,路径是由很多子路径构成,这些子路径都是在一个列表中,所有的子路径(线、弧形等)构成一个完全的路径,当调用stroke()或fill()方法后就会绘制图形;
而当每次调用closePath()方法之后,列表就被清空重置,之后就可以重新绘制新的图形,如:
context.beginPath();context.moveTo(50,50);context.lineTo(100,50);context.arc(50,50,50,0,Math.PI/2);context.closePath();context.fillStyle = "red";context.strokeStyle = "blue";context.lineWidth = 12;context.fill();context.stroke();context.beginPath();context.moveTo(200,70);context.arc(200,70,50,0,Math.PI/2,true);context.closePath();context.fillStyle = "blue";context.strokeStyle = "red";context.lineWidth = 8;context.fill();context.stroke();context.beginPath();context.rect(300,50,100,100);context.closePath();context.fillStyle = "green";context.strokeStyle = "yellow";context.lineWidth = 4;context.fill();context.stroke();
闭合路径closePath()方法不是必需的,如果不该用closePath(),则路径不闭合,如:
context.beginPath();context.arc(150, 150,50, 0, Math.PI);// context.closePath();context.stroke();
如果不该用beginPath()开始新路径,并不会关闭之前的路径,并且该路径会永久保留着;就算调用fill()方法或stroke()方法进行绘制,路径也不会消逝;因此,后续的绘制,还会重复绘制该路径;
移动笔触(当前坐标点):moveTo(x,y)方法:将光标(笔触)移动到指定坐标点,绘制图形的时候就以这个坐标点为出发点,也便是确定一个子路径的出发点,当个出发点也称为当前点;其并不绘制任何内容,但也属于路径描述的一部分;
context.moveTo(50, 50);context.lineTo(150, 150);
在画布中第一次调用干系的绘制路径的方法,有时候并不须要利用moveTo()指定出发点位置,出发点位置是由该方法的参数指定的;但后续的路径绘制,必须要明确出发点位置
当调用beginPath()方法开始新的一段路径时,绘制的新路径的出发点由当前的绘制方法自动确定,此时,并不须要明确调用moveTo()方法;当 canvas初始化或者beginPath()调用后,常日会利用moveTo()函数设置出发点;
示例:笑脸
context.beginPath();context.arc(75, 75, 50, 0, Math.PI 2, true);context.moveTo(110, 75);context.arc(75, 75, 35, 0, Math.PI); // 口 (顺时针)context.moveTo(65, 65);context.arc(60, 65, 5, 0, Math.PI 2, true); // 左眼context.moveTo(95, 65);context.arc(90, 65, 5, 0, Math.PI 2, true); // 右眼context.closePath(); // 不是必须的context.stroke();
绘制直线(线段):绘制线段须要lineTo()方法:lineTo(x,y)方法:指定从出发点到终点(x, y)的一条直线;可重复利用lineTo方法,会以上一个lineTo()方法指定的点为出发点,以当前lineTo()为终点再次创建直线;不断重复这个过程,可以绘制繁芜的图形;
context.moveTo(50,50);context.lineTo(100,100);context.lineTo(100,200);实行stroke()绘制直线;context.moveTo(50,50);context.lineTo(200,200);context.lineTo(50,200);context.stroke();
开始点和之前的绘制路径有关,之前路径的结束点便是接下来的开始点,开始点也可以通过moveTo()函数改变;绘制直线也属于路径,因此在必要的情形下,也须要利用beginPath(),否则也属于连续绘制,或调用closePath()方法闭合路径;
// ...context.beginPath();context.moveTo(50,50);context.lineTo(200,200);context.lineTo(50,200);context.lineWidth = 20;context.closePath();// ...
示例:绘制三角形
// 绘制三角形context.beginPath();context.moveTo(75, 50);context.lineTo(100, 75);context.lineTo(100, 25);context.fill();
示例:绘制多边形
CanvasRenderingContext2D.prototype.polygon = function(n ,x, y, r, angle, anticlockwise){this.n = n || 3;this.x = x || 10;this.y = y || 10;this.r = r || 50;this.angle = angle || 0;this.anticlockwise = anticlockwise || false;this.moveTo(this.x + this.r Math.sin(this.angle), this.y - this.r Math.cos(this.angle));var delta = 2 Math.PI / this.n;for(var i=1; i<this.n; i++){this.angle += this.anticlockwise ? -delta : delta;this.lineTo(this.x + this.r Math.sin(this.angle), this.y - this.r Math.cos(this.angle));}this.closePath();}var canvas = document.getElementsByTagName("canvas")[0];if(canvas.getContext){var context = canvas.getContext("2d");context.beginPath();context.polygon(3, 50, 70, 50); // 三角形context.polygon(4, 150, 60, 50, Math.PI / 4); // 正方形context.polygon(5, 255, 55, 50); // 五边形context.polygon(6, 365, 53, 50, Math.PI / 6); // 六边形context.polygon(4, 365, 53, 20, Math.PI / 4, true); // 六边形中的小正方形context.fillStyle = "#ccc";context.strokeStyle = "#008";context.lineWidth = 5;context.fill();context.stroke();}</script>
添补规则:当用到fill()、clip()和isPointinPath()时,可以选择一个添补规则,即传入一个fillRule参数,该添补规则根据某处在路径的表面或者里面来决定该处是否被添补,这对付自己与自己路径相交或者路径被嵌套的情形是有用的;两个可能的值:
"nonzero":non-zero winding rule(非零环抱规则), 默认值;"evenodd":even-odd winding rule;(奇偶环抱规则)非零环抱原则(nonzero):紧张办理绘图中交叉路径的添补问题;
要检测一个点P是否在路径的内部,利用非零环抱原则:想象一条从点P出发沿着任意方向无限延伸(一贯延伸到路径所在的区域外)的射线;首先初始化一个计数器为0,然后对所有穿过这条射线的路径进行列举;每当一条路径顺时针方向穿过射线的时候,计数器就加1;反之,就减1;末了,列举完所有路径之后,如果计数器的值不是0,那么就认为P是在路径内;反之,如果计数器的值是0,则认为P在路径外;总之,由顺、逆时针穿插次数决定是否添补某一区域;
奇偶环抱规则(evenodd):奇数表示在路径内,偶数表示在路径外;从任意位置p作一条射线,若与该射线相交的路径的数目为奇数,则p是路径内部的点,否则是外部的点;
如:
context.beginPath();context.arc(50, 50, 30, 0, Math.PI2);context.arc(50, 50, 15, 0, Math.PI2, true);// context.fill();// context.fill("nonzero");context.fill("evenodd");
如:绘制一个五角星
context.arc(100, 100, 100, 0, Math.PI2);context.fillStyle = '#D43D59';context.fill();context.beginPath();context.moveTo(100, 0);context.lineTo(100+Math.cos(Math.PI3/10)100, 100+Math.sin(Math.PI3/10)100);context.lineTo(100-Math.cos(Math.PI1/10)100, 100-Math.sin(Math.PI1/10)100);context.lineTo(100+Math.cos(Math.PI1/10)100, 100-Math.sin(Math.PI1/10)100);context.lineTo(100-Math.cos(Math.PI3/10)100, 100+Math.sin(Math.PI3/10)100);context.lineTo(100, 0);context.closePath();context.strokeStyle = "rgb(0,0,0)";context.fillStyle = "#246AB2"// context.fill('nonzero');context.fill('evenodd');context.stroke();
如:绘制一个大箭头图标
context.arc(250, 250, 150, 0, Math.PI 2);context.moveTo(250, 150);context.lineTo(350, 250);context.lineTo(150, 250);context.closePath();context.moveTo(200, 250);context.lineTo(300, 250);context.lineTo(300, 350);context.lineTo(200, 350);context.closePath();context.fillStyle = "#0D6EB8";// context.fill("nonzero");context.fill("evenodd");
示例:利用数学方程绘制图案图形
var dx = 150, dy = 150;var s = 100;context.beginPath();context.fillStyle = "rgb(100,255,100)";context.strokeStyle = "rgb(0,0,100)";var dig = Math.PI / 15 11;for(var i=0; i<30; i++){var x = Math.sin(i dig);var y = Math.cos(i dig);console.log(dx + x s, dy + y s);context.lineTo(dx + x s, dy + y s);}context.closePath();// context.fill();context.fill("evenodd");context.stroke();// 以下包装成一个函数function draw(offsetX, n){var dig = Math.PI / 15 n;context.beginPath();for(var i = 0 ; i < 30 ; i++){var x = Math.sin(i dig);var y = Math.cos(i dig);context.lineTo(offsetX + x 80, 150 + y 80);}context.closePath();context.fillStyle = "green";// context.fill();context.fill("evenodd");context.strokeStyle = "#666";context.stroke();}var data = [14,13,19,7,26];data.forEach(function(v, i){draw((i + 1) 160, v);});
示例:利用鼠标实时绘制图形
canvas.onmousedown = function(e) {this.X1 = e.offsetX;this.Y1 = e.offsetY;this.isMouseDown = true;};canvas.onmousemove = function(e) {if (this.isMouseDown) {this.X2 = e.offsetX;this.Y2 = e.offsetY;this.drawing(this.X1, this.Y1, this.X2, this.Y2, e);}};canvas.onmouseup = function(e) {this.isMouseDown = false;};canvas.drawing = function (x1, y1, x2, y2, e) {if (!context) {return;} else {context.fillStyle = "red";context.strokeStyle = "blue";context.lineWidth = 5;context.beginPath();context.moveTo(x1, y1);context.lineTo(x2, y2);context.stroke();this.X1 = this.X2;this.Y1 = this.Y2;context.closePath();}}
例子:迷你图迷你图(sparkline)是指用于显示少量数据的图形,常日会被嵌入文本流中,形如:server:小图;迷你图是由作者Edward Tufte杜撰的,他将该词用于描述“内嵌在笔墨、数字、图片中的小且高分辨率的图形”;迷你图是数据密集、设计大略、单词大小的图形,如:
<style>.sparkline{background-color:#ddd; color:red; display: inline-block;}</style>Load:<span class="sparkline">3 5 7 6 6 9 11 15</span>,source:<span class="sparkline" data-height="36" data-width="100">6 14 8 9 10 13 18</span>complete:<span class="sparkline" data-ymax="18" data-ymin="6">12,3,8,2,88</span><script>window.onload = function(){var elts = document.getElementsByClassName("sparkline");main: for(var e = 0; e<elts.length; e++){var elt = elts[e];var content = elt.textContent || elt.innerText;var content = content.replace(/^\s+|\s+$/g, "");var text = content.replace(/#.$/gm, "");text = text.replace(/[\n\r\t\v\f]/g, " ");var data = text.split(/\s+|\s,\s/);for(var i=0; i<data.length; i++){data[i] = Number(data[i]);if(isNaN(data[i]))continue main;}var style = getComputedStyle(elt, null);var color = style.color;var height = parseInt(elt.getAttribute("data-height")) ||parseInt(style.fontSize) || 20;var width = parseInt(elt.getAttribute("data-width")) ||data.length (parseInt(elt.getAttribute("data-dx")) || 6);var ymin = parseInt(elt.getAttribute("data-ymin")) ||Math.min.apply(Math, data);var ymax = parseInt(elt.getAttribute("data-ymax")) ||Math.max.apply(Math, data);if(ymin >= ymax)ymax = ymin + 1;var canvas = document.createElement("canvas");canvas.width = width;canvas.height = height;canvas.title = content;elt.innerHTML = "";elt.appendChild(canvas);var context = canvas.getContext("2d");for(var i=0; i<data.length; i++){var x = width i / data.length;var y = (ymax - data[i]) height / (ymax - ymin);context.lineTo(x,y);}context.strokeStyle = color;context.stroke();}}</script>
绘制曲线:角度和弧度:
夹角:从一个点发射(延伸)出两条线段,两条线相交的部分会构成一个夹角;角度:两条相交直线中的任何一条与另一条相叠合时必须迁徙改变的量的量度,单位符号为°;周角:一条直线环绕出发点须要与自己相叠合时必须迁徙改变的量的量度被称为周角,周角平分为360度;弧度:角的度量单位,弧长即是半径的弧其所对的圆心角为1弧度(弧长即是半径时,射线夹角为1弧度);角度与弧度的换算公式为:弧度 = 角度 (Math.PI / 180);
在利用JavaScript编写代码进行干系打算的时候,常常须要利用Math供应的方法:
Math.sin(弧度) 夹角对面的边与斜边的比值;Math.cos(弧度) 夹角侧面的边与斜边的比值;
圆形上点坐标的打算公式:坐标 = ( x0 + Math.cos(angle) x R,y0 + Math.sin(angle) x R )个中x0和y0为圆心坐标,angle为弧度,R为圆的半径;
例如:利用三角函数来绘制曲线:
context.beginPath();for(var x = 30, y = 0; x<1000; x++){// 高度 波长 + 中央轴位置y = 50 Math.sin(x / 25) + 100;context.lineTo(x, y);}context.stroke();
绘制弧形和圆形:arc(x, y, radius, startAngle, endAngle[, anticlockwise])方法:此方法可以绘制一条弧,其以(x, y)为圆心,以radius为半径绘制一条弧线(圆),从 startAngle开始到endAngle结束的弧度,按照anticlockwise给定的方向(默认为顺时针)来绘制;参数anticlockwise指定是否按逆时针方向进行绘制,为true时,逆时针,反之顺时针;
// context.arc(100,100,50,0, Math.PI3/2);context.arc(100,100,50,0, Math.PI3/2,true);context.stroke();
arc()方法中表示角的单位是弧度,不是角度;角度都是按顺序时针打算的,无论是按时针还是按逆时针;
由于arc()绘制的是路径,以是在必要的情形下,须要调用beginPath();如果须要扇形,可以利用closePath()方法闭合路径;
当第一个调用arc()或在beginPath()方法后第一个调用arc()方法时,会在确定其圆心、半径及开始角度的情形下,自动确定出发点位置;其它情形下,须要利用moveTo()指定出发点位置,例如:先将当前点和弧形的出发点用一条直线连接,然后再用圆的一部分(圆弧)来连接弧形的出发点和终点,并把弧形终点作为新确当前点;
// 绘制一个圆弧context.beginPath();context.moveTo(50, 50);context.arc(100, 100, 50, Math.PI3/2, 0);context.lineTo(150,150);context.stroke();// 绘制一个san形(楔形)context.beginPath();context.moveTo(200, 50);context.arc(200, 50, 50, 0, Math.PI / 2, false);context.closePath();context.stroke();// 同样的san形(楔形),方向不同context.beginPath();context.moveTo(350, 100);context.arc(350, 100, 50, 0, Math.PI / 2, true);context.closePath();context.stroke();
arc()不仅可以绘制圆弧,也可以用来绘制圆形,即startAngle, endAngle形成Math.PI2(360度),如:
context.beginPath();context.arc(100, 100, 80, 0, Math.PI2);// context.arc(100, 100, 80, Math.PI / 4, Math.PI 2 + Math.PI / 4);context.closePath(); // 没有必要调用这一句context.lineWidth = 10;context.fillStyle = "red";context.fill();context.stroke();
绘制有交叉路径时弧时,要把稳添补规则,如:
context.beginPath();context.arc(100, 100, 50, 0, Math.PI 2, true);context.arc(100, 100, 60, 0, Math.PI 2, false);context.fill();context.beginPath();context.arc(300, 100, 50, 0, Math.PI, true);context.arc(300, 100, 60, 0, Math.PI 2, true);// 利用奇偶环抱规则context.fill('evenodd');context.beginPath();context.arc(100, 100, 60, 0, Math.PI 2, true);context.arc(140, 100, 60, 0, Math.PI 2, false);context.arc(180, 100, 60, 0, Math.PI 2, true);context.fill();
循环绘制多个,如:
for(var i=0;i<15;i++){context.strokeStyle = "#FF00FF";context.beginPath();context.arc(0,350,i10,0,Math.PI3/2,true);context.closePath();context.stroke();}for(var i=0;i<10;i++){context.beginPath();context.arc(i25,i25,i10,0,Math.PI2,true);context.closePath();context.fillStyle = 'rgba(255,0,0,0.25)';context.fill();}
绘制不同的圆弧:
for(var i = 0; i < 4; i++){for(var j = 0; j < 3; j++){context.beginPath();var x = 25 + j 50; // x 坐标值var y = 25 + i 50; // y 坐标值var radius = 20; // 圆弧半径var startAngle = 0; // 开始点var endAngle = Math.PI + (Math.PI j) / 2; // 结束点var anticlockwise = i % 2 == 0 ? false : true; // 顺时针或逆时针context.arc(x, y, radius, startAngle, endAngle, anticlockwise);if (i>1){context.fill();} else {context.stroke();}}}
绘制不同圆角的矩形:
// 绘制圆角矩形context.beginPath();context.moveTo(360, 0);context.arc(380, 30, 30, Math.PI 3 / 2, 0); // 上边和右上角context.arc(390, 100, 20, 0, Math.PI / 2); // 右上角和右下角context.arc(340, 110, 10, Math.PI / 2, Math.PI); // 底边和左下角context.arc(330, 0, 0, Math.PI, 0); // 左边和左上角context.closePath();context.fill();context.stroke();
示例:绘制一个WIFI图标:
// wifi图标context.lineWidth = 3;for(var i=0;i<3;i++){context.beginPath();context.arc(100, 150, 15 + i12, Math.PI, Math.PI3/2);context.stroke();}context.beginPath();context.arc(98, 148, 3, 0, Math.PI2);context.fill();
示例:绘制一个时钟表盘
context.beginPath();// 外圆context.arc(100,100,99,0,Math.PI 2, false);// 内圆context.moveTo(194, 100);context.arc(100,100,94,0,Math.PI 2, false);// 分针context.moveTo(100,100);context.lineTo(100,15);// 时针context.moveTo(100,100);context.lineTo(35,100);context.closePath();context.stroke();
示例:绘制一个手镯
// 利用了非零环抱规则function bracelet(){context.beginPath();context.arc(300,190,150,0,Math.PI2,false); //顺时针context.arc(300,190,100,0,Math.PI2,true); //逆时针context.fillStyle="rgba(100,140,230,0.5)";context.strokeStyle = context.fillStyle;context.shadowColor="rgba(0,0,0,0.8)";context.shadowOffsetX = 12;context.shadowOffsetY = 12;context.shadowBlur = 15;context.fill();context.stroke();}bracelet();
示例:绘制平分圆:
//描边drawCircle(100,100,40,2,true);drawCircle(200,100,40,3,true);drawCircle(300,100,40,4,true);drawCircle(400,100,40,20,true);drawCircle(500,100,40,100,true);drawCircle(600,100,40,200,true);//添补drawCircle(100,200,40,2);drawCircle(200,200,40,3);drawCircle(300,200,40,4);drawCircle(400,200,40,20);drawCircle(500,200,40,100);drawCircle(600,200,40,200);function drawCircle(x,y,r,n,isStroke){for(var i = 0 ; i < n ; i++){//打算开始和结束的角度var angle = 2 Math.PI / n;var startAngle = angle i;var endAngle = angle (i + 1);context.beginPath();//设置绘制圆的出发点context.moveTo(x,y);context.arc(x,y,r,startAngle,endAngle,false);if(isStroke){// context.strokeStyle = getRandomColor();context.stroke();}else{context.fillStyle = getRandomColor();context.fill();}}}//获取添补的颜色/随机function getRandomColor(){var r = getRandom();var g = getRandom();var b = getRandom();return "rgb("+r+","+g+","+b+")";}function getRandom(){return Math.floor(Math.random() 256);}
示例:绘制饼形图
var data = [100, 50, 20, 30, 100];context.fillStyle = "white";context.fillRect(0,0,canvas.width,canvas.height);var colors = [ "red","orange", "yellow","green", "blue"];var total = 0;for(var i=0; i<data.length; i++)total += data[i];var prevAngle = 0;for(var i=0; i<data.length; i++) {var fraction = data[i]/total; var angle = prevAngle + fraction Math.PI 2;context.fillStyle = colors[i];context.beginPath();context.moveTo(150,150);context.arc(150,150, 100, prevAngle, angle, false);context.lineTo(150,150);context.fill();context.strokeStyle = "black";context.stroke();prevAngle = angle;}
示例:饼形的类
function PieChart(context){ this.context = context || document.getElementById("canvas").getContext("2d");this.x = this.context.canvas.width / 2;this.y = this.context.canvas.height / 2;this.r = 120;this.outLine = 20;this.dataList = null;}PieChart.prototype = {constructor:PieChart,init:function(dataList){this.dataList = dataList || [{value:100}];this.transformAngle();this.drawPie();},drawPie:function(){var startAngle = 0,endAngle;for(var i = 0 ; i < this.dataList.length ; i++){var item = this.dataList[i];endAngle = startAngle + item.angle;this.context.beginPath();this.context.moveTo(this.x,this.y);this.context.arc(this.x,this.y,this.r,startAngle,endAngle,false);var color= this.context.strokeStyle= this.context.fillStyle= this.getRandomColor();this.context.stroke();this.context.fill();this.drawPieLegend(i);startAngle = endAngle;}},drawPieLegend:function(index){var space = 10;var rectW = 40;var rectH = 20;var rectX = this.x + this.r + 80;var rectY = this.y + (index 30);this.context.fillRect(rectX,rectY,rectW,rectH);// this.context.beginPath();},getRandomColor:function(){var r = Math.floor(Math.random() 256);var g = Math.floor(Math.random() 256);var b = Math.floor(Math.random() 256);return 'rgb('+r+','+g+','+b+')';},transformAngle:function(){var self = this;var total = 0;this.dataList.forEach(function(item,i){total += item.value;})this.dataList.forEach(function(item,i){self.dataList[i].angle = 2 Math.PI item.value/total;})},}var data = [{value:20},{value:26},{value:20},{value:63},{value:25}]var pie = new PieChart().init(data);
arcTo(x1, y1, x2, y2, radius)方法:根据掌握点(x1, y1)、(x2, y2)和半径绘制圆弧路径;其根据当前点与给定的掌握点1连接的直线,和掌握点1与掌握点2连接的直线,作为利用指定半径的圆的切线,画出两条切线之间的弧线路径;
解释:图例中的x0和y0,为当前点,x1和y1代表第一个掌握点的坐标,x2和y2代表第二个掌握点的坐标,radius代表圆弧半径;
context.beginPath();context.moveTo(50,50);context.arcTo(200, 60, 250, 300, 60);context.stroke();// 绘制提示点context.fillRect(48,48, 4, 4); // 出发点context.fillRect(198,58, 4, 4); // 出发点context.fillRect(248,298, 4, 4); // 出发点// 绘制条切线context.setLineDash([3]);context.beginPath();context.moveTo(50, 50);context.lineTo(200, 60);context.lineTo(250, 300);context.stroke();
如果半径radius为0,则会绘制一点直线;
如,绘制一个圆角矩形:
context.moveTo(400, 50);context.arcTo(500, 50, 500, 150, 30);context.arcTo(500, 150, 400, 150, 20);context.arcTo(400, 150, 400, 50, 10);context.arcTo(400, 50, 500, 50, 0);context.stroke();
bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)方法:贝塞尔曲线(也称为三次贝塞尔曲线),将从当前点到指定坐标点中间的贝塞尔曲线添加到路径中,贝塞尔曲线须要两个掌握点,分别为cp1x、cp1y和cp2x、cp2y,x和y是贝塞尔曲线的终点坐标;
context.moveTo(100, 300);context.bezierCurveTo(160,200,280,300,320,250)context.stroke();context.fillRect(97, 297, 6, 6); // 出发点context.fillRect(317, 247, 6, 6); // 终点context.fillRect(160-3, 200-3, 6,6); // 标记掌握点context.fillRect(280-3, 300-3, 6,6); // 标记掌握点// 线context.beginPath();context.strokeStyle = "red";context.moveTo(100, 300);context.lineTo(160, 200);context.moveTo(320, 250);context.lineTo(280, 300);context.stroke();
结合正反弦函数绘图:
var dx = dy = 150;var s = 100;context.beginPath();context.fillStyle = "lightgreen";var dig = Math.PI / 15 11;context.moveTo(dx,dy);for(var i=0; i<30; i++){var x = Math.sin(i dig);var y = Math.cos(i dig);context.bezierCurveTo(dx+xs, dy+ys-100, dx+xs+100, dy+ys, dx+xs, dy+ys);}context.closePath();context.fill();context.stroke();
示例:绘制心形
//三次贝塞尔曲线,绘制心形context.beginPath();context.moveTo(75, 40);context.bezierCurveTo(75, 37, 70, 25, 50, 25);context.bezierCurveTo(20, 25, 20, 62.5, 20, 62.5);context.bezierCurveTo(20, 80, 40, 102, 75, 120);context.bezierCurveTo(110, 102, 130, 80, 130, 62.5);context.bezierCurveTo(130, 62.5, 130, 25, 100, 25);context.bezierCurveTo(85, 25, 75, 37, 75, 40);context.fill();
quadraticCurveTo(cx, cy, x, y)方法:绘制二次贝塞尔曲线,相对来说,二次贝塞尔曲线的绘制比贝塞尔曲线的绘制随意马虎一些,由于绘制贝塞尔曲线须要两个掌握点,而绘制二次贝塞尔曲线时只须要一个掌握点;因此quadraticCurveTo方法只须要四个参数就可以了,分别是掌握点的坐标(cx, cy)、二次贝塞尔曲线终点的坐标(x,y);
context.moveTo(75, 250);context.quadraticCurveTo(100,200,175,250);context.stroke();context.fillRect(72, 247, 6, 6); // 出发点context.fillRect(172, 247, 6, 6); // 出发点context.fillRect(100-3, 200-3, 6,6); // 掌握点// 线context.beginPath();context.strokeStyle = "red";context.moveTo(75, 250);context.lineTo(100, 200);context.lineTo(175, 250);context.stroke();
示例:对话气泡
context.beginPath();context.moveTo(75, 25);context.quadraticCurveTo(25, 25, 25, 62.5);context.quadraticCurveTo(25, 100, 50, 100);context.quadraticCurveTo(50, 120, 30, 125);context.quadraticCurveTo(60, 120, 65, 100);context.quadraticCurveTo(125, 100, 125, 62.5);context.quadraticCurveTo(125, 25, 75, 25);context.stroke();
绘制圆弧或椭圆:ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, anticlockwise)方法:绘制一条椭圆路径;椭圆的圆心在(x,y)位置,半径分别是radiusX 和 radiusY ,按照anticlockwise (默认顺时针)指定的方向,从 startAngle 开始绘制,到 endAngle 结束;参数rotation表示椭圆的旋转角度(单位是度,不是弧度),而startAngle和endAngle单位是弧度;
context.lineWidth = 20;context.beginPath();context.ellipse(200,100,100,50,0,0,Math.PI2);context.stroke();context.beginPath();context.ellipse(200,250,50,30,Math.PI / 4,0,Math.PI2,true);context.stroke();
如果radiusX与radiusY的值同等,绘制出来的是一个正圆;
// 把上例代码改成context.ellipse(i25,i25,i10,i20,30,0,Math.PI2,true);
该方法一开始不是标准方法,现在还未完备标准化,一开始只有Chrome支持,IE不支持此方法;
参数方程法绘制椭圆:
function ParamEllipse(context, x, y, a, b){var step = (a > b ) ? 1 / a : 1 / b;context.beginPath();context.moveTo(x + a, y);for(var i=0; i<2Math.PI; i += step){context.lineTo(x + a Math.cos(i), y + b Math.sin(i));}context.closePath();context.stroke();}context.lineWidth = 10;ParamEllipse(context, 130, 80, 100,20);
均匀压缩法绘制椭圆:
function EvenCompEllipse(context, x, y, a, b){context.save();var r = (a > b) ? a : b;var ratioX = a / r; // x轴缩放比var ratioY = b / r; // y轴缩放比context.scale(ratioX, ratioY); // 进行缩放(均匀压缩)context.beginPath();// 从椭圆的左端点开始逆时针绘制context.moveTo((x + a) / ratioX, y / ratioY);context.arc(x / ratioX, y / ratioY, r, 0 , Math.PI2);context.closePath();context.stroke();context.restore();}context.lineWidth = 10;EvenCompEllipse(context, 130, 200, 100, 20);
利用三次贝塞尔曲线仿照椭圆:
function BezierEllipse(context, x, y, a, b){// 0.5和0.6是两个关键系数var ox = 0.5 a, oy = 0.6 b;context.save();context.translate(x, y);context.beginPath();// 从椭圆的下端点开始逆时针绘制context.moveTo(0, b);context.bezierCurveTo(ox, b, a, oy, a, 0);context.bezierCurveTo(a, -oy, ox, -b, 0, -b);context.bezierCurveTo(-ox, -b, -a, -oy, -a, 0);context.bezierCurveTo(-a, oy, -ox, b, 0, b);context.closePath();context.stroke();context.restore();}context.lineWidth = 10;BezierEllipse(context, 470, 80, 100, 20);
二次贝塞尔曲线仿照椭圆:
// 贝塞尔掌握点x=(椭圆宽度/0.75)/2CanvasRenderingContext2D.prototype.oval = function (x, y, width, height) {var k = (width/0.75)/2,w = width/2,h = height/2;this.beginPath();this.moveTo(x, y-h);this.bezierCurveTo(x+k, y-h, x+k, y+h, x, y+h);this.bezierCurveTo(x-k, y+h, x-k, y-h, x, y-h);this.closePath(); return this;}context.oval(300,100,200,50);context.lineWidth = 10;context.stroke();
rect(x, y, width, height)方法:绘制一个左上角坐标为(x,y),宽高为width以及height的矩形路径,其路径会自动闭合;当该方法实行的时候,moveTo()方法自动设置坐标参数(0,0),即当前笔触自动重置回默认坐标;
context.rect(10, 10, 100, 100);context.fill();
roundrect(x, y, width, height)方法:这是由Chrome实现的绘制圆角矩形的方法,其它浏览器都不支持,如:
context.beginPath();context.roundRect(50,40,100,100,50);context.closePath();context.stroke();
兼容处理:
if(!CanvasRenderingContext2D.prototype.roundRect){CanvasRenderingContext2D.prototype.roundRect = function(x, y, w, h, r){this.x = x;this.y = y;this.w = w;this.h = h;this.r = r;this.moveTo(this.x + this.r ,this.y);this.arcTo(this.x + this.w , this.y , this.x + this.w , this.y + this.h , this.r);this.arcTo(this.x + this.w , this.y + this.h , this.x , this.y + this.h , this.r);this.arcTo(this.x , this.y + this.h , this.x , this.y , this.r);this.arcTo(this.x , this.y , this.x + this.r , this.y , this.r);}}var canvas = document.getElementsByTagName("canvas")[0];if(canvas.getContext){var context = canvas.getContext("2d");context.beginPath();context.roundRect(50,40,100,100,50);context.closePath();context.stroke();context.beginPath();context.roundRect(200,40,100,100,50);context.closePath();context.fill();context.beginPath();context.roundRect(350,40,100,100,10);context.stroke();context.beginPath();context.roundRect(500,40,100,100,20);context.closePath();context.fill();context.beginPath();context.roundRect(650,40,120,100,30);context.closePath();context.stroke();}
组合运用:
var canvas = document.getElementsByTagName("canvas")[0];if(canvas.getContext){var context = canvas.getContext("2d");roundedRect(context, 12, 12, 150, 150, 15);roundedRect(context, 19, 19, 150, 150, 9);roundedRect(context, 53, 53, 49, 33, 10);roundedRect(context, 53, 119, 49, 16, 6);roundedRect(context, 135, 53, 49, 33, 10);roundedRect(context, 135, 119, 25, 49, 10);context.beginPath();context.arc(37, 37, 13, Math.PI / 7, -Math.PI / 7);context.lineTo(31, 37);context.fill();for(var i = 0; i < 8; i++){context.fillRect(51 + i 16, 35, 4, 4);}for(i = 0; i < 6; i++){context.fillRect(115, 51 + i 16, 4, 4);}for(i = 0; i < 8; i++){context.fillRect(51 + i 16, 99, 4, 4);}context.beginPath();context.moveTo(83, 116);context.lineTo(83, 102);context.bezierCurveTo(83, 94, 89, 88, 97, 88);context.bezierCurveTo(105, 88, 111, 94, 111, 102);context.lineTo(111, 116);context.lineTo(106.333, 111.333);context.lineTo(101.666, 116);context.lineTo(97, 111.333);context.lineTo(92.333, 116);context.lineTo(87.666, 111.333);context.lineTo(83, 116);context.fill();context.fillStyle = "white";context.beginPath();context.moveTo(91, 96);// context.bezierCurveTo(88, 96, 87, 99, 87, 101);// context.bezierCurveTo(87, 103, 88, 106, 91, 106);// context.bezierCurveTo(94, 106, 95, 103, 95, 101);// context.bezierCurveTo(95, 99, 94, 96, 91, 96);// 利用这一句代替以上的4个方法context.ellipse(91,101,4,5,0,0,Math.PI2);context.moveTo(103, 96);// context.bezierCurveTo(100, 96, 99, 99, 99, 101);// context.bezierCurveTo(99, 103, 100, 106, 103, 106);// context.bezierCurveTo(106, 106, 107, 103, 107, 101);// context.bezierCurveTo(107, 99, 106, 96, 103, 96);// 利用这一句代替以上的4个方法context.ellipse(103,101,4,5,0,0,Math.PI2);context.fill();context.fillStyle = "black";context.beginPath();context.arc(101, 102, 2, 0, Math.PI 2, true);context.fill();context.beginPath();context.arc(89, 102, 2, 0, Math.PI 2, true);context.fill();}// 封装的一个用于绘制圆角矩形的函数function roundedRect(context, x, y, width, height, radius){context.beginPath();context.moveTo(x, y + radius);context.lineTo(x, y + height - radius);context.quadraticCurveTo(x, y + height, x + radius, y + height);context.lineTo(x + width - radius, y + height);context.quadraticCurveTo(x + width, y + height, x + width, y + height - radius);context.lineTo(x + width, y + radius);context.quadraticCurveTo(x + width, y, x + width - radius, y);context.lineTo(x + radius, y);context.quadraticCurveTo(x, y, x, y + radius);context.stroke();}
Path2D:Canvas 2D API包含一个Path2D的接口,此接口用来声明路径,此路径会被2D高下文利用; Path2D拥有2D高下文的相同的路径方法,它许可在canvas中根据须要创建可以保留并重用的路径;
可以利用Path2D工具的各种方法绘制直线、矩形、圆形、椭圆以及曲线;如:moveTo(x,y)、lineTo(x,y)、rect(x,y,w,h)、arc(x,y,radius,startAngle,endAngle[,anticlockwise])、arcTo(x1,1,x2,y2,radiusX[,radius,rotation])、ellipse(x,y,radius,radius,rotation,startAngle,endAngle[,anticlockwise])、bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)、quadraticCurveTo(cpx,cpy,x,y)、closePath()
布局函数:Path([path | svgPath]),返回一个新的 Path2D 工具;参数path为另一个Path2D工具,这将该工具所代表的路径复制给新创建的Path2D工具;
new Path2D();new Path2D(path);new Path2D(d);
还可以在Path2D的布局函数中通报一个代表SVG路径的字符串;
var path = new Path2D("M10 10 h 80 v 80 h -80 Z");context.fill(path);
创建完Path2D工具后,就可以利用所有绘制路径的方法绘制路径,并可以调用closePath()方法闭合路径,如:
var path1 = new Path2D();path1.rect(10,10,100,100);var path2 = new Path2D(path1);path2.moveTo(220,60);path2.arc(170,60,50,0,Math.PI2);
之后可以利用context工具的fill(path)和stroke(path)方法进行添补和描边,参数path指向的Path2D工具;
context.stroke(path2);// ...for(var i=0;i<10;i++){var path = new Path2D();path.arc(i25,i25,i10,0,Math.PI2,true);path.closePath();context.fillStyle = "rgba(255,0,0,0.25)";context.fill(path);}
如:
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);context.stroke(rectangle);context.fill(circle);
addPath(path [, transform])方法:添加一条新路径到对当前路径;参数path为须要添加的 Path2D 路径,参数transform是可选的,作为新增路径的变换矩阵;
var p1 = new Path2D();p1.rect(0,0,100,100);var p2 = new Path2D();p2.rect(0,0,100,100);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;p1.addPath(p2, m);context.fill(p1);
图形属性:图形属性指定了画布的通用图形状态,例如fillStyle、strokeStyle等属性,绘图方法并没有设置这些属性,以是这些属性该当在调用干系绘图方法之提高行设置;这种将从图形状态和绘制指令分离的思想是画布API很主要的观点,和在HTML文档中运用CSS样式来实现构造和样式分离是类似的;
context工具上定义了15个图形属性:
fillStyle:添补颜色、渐变和图案strokeStyle:勾勒线段时的颜色、渐变或图案linewidth属性:指定线条的宽度,值可以是任意整数;lineCap属性:如何渲染线段的末端,即为直线添加线帽,值:butt默认值,不为直线添加线帽,round圆形线帽,square正方形线帽;lineJoin属性:如何渲染顶点,即指定两条直线交汇时的拐角形状,值:miter默认值,尖角;round圆角;bevel斜角;miterLimit:斜接顶点的最大长度;font:绘制文本时的CSS字体;textAlign:文本水平对齐;textBaseline:垂直对齐办法;shadowBlur:阴影模糊程序;shadowColor:阴影颜色;shadowOffsetX:阴影水平偏移量;shadowOffsetY:阴影垂直偏移量;globalAlpha:绘制像素时要添加的透明度;globalCompositeOperation:如何合并新的像素点和下面的像素点;fillStyle和strokeStyle属性:指定了区域添补和线条勾勒的样式;即可以指定颜色,也可以把它们设置为CanvasPattern或者CanvasGradient工具,以实现背景图案或渐变色;如果利用颜色,其利用标准的CSS颜色值,默认为“#000000”;详细的颜色格式有:
var colors = ["#f44", // 16进制RGB,赤色"#44ff44", // 16进制RRGGBB,绿色"rgb(60,60,255)", // 0-255之间的整数表示的RGB,蓝色"rgb(100%, 25%, 100%)", // 百分比表示的RGB,紫色"rgba(100%, 25%, 100%, 0.5)", // RGB加上0-1的alpha,半透明紫色"rgba(0,0,0,0)", // 全透明玄色"transparent", // 全透明"hsl(60,100%, 50%)", // 全饱和黄色"hsl(60, 75%, 50%)", // 低包包黄色"hsl(60, 100%, 75%)", // 全饱和暗黄色"hsl(60, 100%, 25%)", // 全饱和亮黄色"hsla(60, 100%, 50%, 0.5)" // 全饱和黄色,50%透明];
一旦设置了strokeStyle或fillStyle的值,那么这个新值就会成为新绘制的图形的默认值,如果要给后续绘制的图形利用不同的颜色,须要重新设置fillStyle或strokeStyle的值;
for (var i=0;i<6;i++){for (var j=0;j<6;j++){ // 255/6=42.5context.fillStyle = 'rgb(' + Math.floor(255-42.5i) + ',' +Math.floor(255-42.5j) + ',0)';context.fillRect(j25,i25,25,25);}}
如:
for (var i=0;i<6;i++){for (var j=0;j<6;j++){context.strokeStyle = 'rgb(0,' + Math.floor(255-42.5i) + ',' +Math.floor(255-42.5j) + ')';context.beginPath();context.arc(12.5+j25,12.5+i25,10,0,Math.PI2,true);context.stroke();}}
如:
context.fillStyle = 'rgb(255,221,0)';context.fillRect(0,0,150,37.5);context.fillStyle = 'rgb(102,204,0)';context.fillRect(0,37.5,150,37.5);context.fillStyle = 'rgb(0,153,255)';context.fillRect(0,75,150,37.5);context.fillStyle = 'rgb(255,51,0)';context.fillRect(0,112.5,150,37.5);// 画半透明矩形for (var i=0; i<10; i++){context.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')';for (var j=0; j<4; j++){context.fillRect(5+i14, 5+j37.5, 14, 27.5)}}
globalAlpha属性:全局透明度,其值是介于0到1之间的值,用于指定所有绘制的透明度,默认值为1;如:
context.fillStyle = "#ff0000";context.fillRect(50,50,100,100);// 修正全局透明度context.globalAlpha= 0.5;context.fillStyle = "rgba(0,0,255,1)";context.fillRect(50,100,100,100);
如果将其设为0,所有绘制的图形都会变成全透明;绘制的每个像素都会将其alpha值乘以设置的globalAlpha值,例如:如果设置为0.5,那么所有绘制的原来不透明的像素都会变成50%的透明度,而原来是50%透明的,就变成25%的不透明度;globalAlpha 属性在须要绘制大量拥有相同透明度的图形时候相称高效;如:
// 画背景context.fillStyle = '#FD0';context.fillRect(0,0,75,75);context.fillStyle = '#6C0';context.fillRect(75,0,75,75);context.fillStyle = '#09F';context.fillRect(0,75,75,75);context.fillStyle = '#F30';context.fillRect(75,75,75,75);context.fillStyle = '#FFF';context.globalAlpha = 0.2;// 画半透明圆for (var i=0;i<7;i++){context.beginPath();context.arc(75, 75, 10+10i, 0, Math.PI2, true);context.fill();}
线型Line styles:lineWidth属性:指定线条的宽度,值可以是任意整数;实际上可以是小于1的小数
context.lineWidth = 0.5;context.strokeRect(100,50, 300, 200);
线可以通过stroke()、strokeRect()和strokeText()方法绘制;
lineWidth属性没有相像中的那么大略:我们可以将路径视为一个无限细的线条,当调用stroke()方法进行轮廓描边时,它们是处于路径的中间,两边都是lineWidth宽度的一半;
for (var i = 0; i < 10; i++){context.lineWidth = 1+i;context.beginPath();context.moveTo(5+i14, 5);context.lineTo(5+i14, 140);context.stroke();}
示意图:
如果勾勒一条闭合的路径并只希望线段涌如今路径之外,那么首先勾勒该路径,然后用不透明颜色添补闭合区域,将涌如今路径内的勾勒部分隐蔽;又或者如果只希望出在闭合路径内,可以调用clip()方法;
线段宽度是受当前坐标系变换影响的,如果通过坐标系变换来对坐标轴进行缩放,例如调用scale(2,1)方法就会对X轴进行缩放,但是对Y轴不产生影响,如此,垂直的线段要比原来和它一样宽的水平线段宽一倍;
lineCap属性:为直线添加线帽,值:butt默认值,不为直线添加线帽,即在线段端直接结束;round圆形线帽,即在原端点的根本上延长一个半圆;square正方形线帽,即在原端点的根本上,再延长线段宽度一半的长度;如图:
context.beginPath();context.lineWidth = 10;context.lineCap = "round";context.moveTo(20,20);context.lineTo(20,200);context.stroke();var lineCap = ['butt','round','square'];// 绘制高下两条水平线条context.strokeStyle = '#09f';context.beginPath();context.moveTo(60,10);context.lineTo(200,10);context.moveTo(60,140);context.lineTo(200,140);context.stroke();// 绘制垂直三条不同端点的线条context.strokeStyle = 'black';for (var i = 0; i < lineCap.length; i++) {context.lineWidth = 15;context.lineCap = lineCap[i];context.beginPath();context.moveTo(80+i50,10);context.lineTo(80+i50,140);context.stroke();}
lineJoin属性:指定两条线段交汇时的拐角形状,即在顶点处如何连接,值:miter默认值,尖角,表示一贯延伸两条线段的外侧边缘直到在某一点汇合;round圆角,表示将汇合的顶点变成圆形;bevel斜角,表示把汇合的顶点切除;
context.beginPath();context.lineWidth = 10;context.lineJoin = "bevel";context.moveTo(20,150);context.lineTo(80,50);context.lineTo(160,150);context.stroke();// 分别利用3种不同的lineJoinvar lineJoin = ['round','bevel','miter'];context.lineWidth = 10;for (var i = 0; i < lineJoin.length; i++) {context.lineJoin = lineJoin[i];context.beginPath();context.moveTo(200, 50+i40);context.lineTo(250, 95+i40);context.lineTo(300, 50+i40);context.lineTo(350, 95+i40);context.lineTo(400, 50+i40);context.stroke();}
miterLimit属性:当只有lineJoin属性值是miter时才会起浸染;当两条线段相交的夹角为锐角的时候,两条线段的斜接部分可以变得很长,该属性可以指定斜接部分长度的上限,默认值是10.0;
context.lineWidth = 20;console.log(context.miterLimit); // 10context.miterLimit = 7;context.moveTo(50,50);context.lineTo(150,50);// context.lineTo(50,150);// context.lineTo(50,120);context.lineTo(50,80); // 夹角小,斜接长度更长context.stroke();
斜接最大长度是当前哨宽与miterLimit属性值的乘积的一半,即斜接限定值(miterLimit)是斜接长度与一半线宽的比值;如果指定的miterLimit值比这个比值还小的话,终极绘制出来的顶点就会是斜切的(bevel)而不是斜接的;当给属性赋值时,0、负数、 Infinity 和 NaN 都会被忽略;
setLineDash(segments)方法:自定义虚线形状;参数segments为一个数组,描述了线段和线段间隙的交替长度;
context.beginPath();context.setLineDash([5, 15]);context.moveTo(0, 50);context.lineTo(300, 50);context.stroke();
如果数组只存在一个值,则表示线段与间隙长度都即是这个值;如果数组中的元素超过2个,则数组中的数值数量为偶数,即第奇个数代表线段长度,第偶数个数表示间隙长度;如果数组中的元素超过2个,且不为偶数,会自动复制一份该值,使其成为偶数量;如果要切换回至实线模式,将 dash list 设置为一个空数组即可;
context.setLineDash([5]);context.setLineDash([5,10,15,20]);// context.setLineDash([5,10,15])变成context.setLineDash([5,10,15,5,10,15]);context.setLineDash([5,10,15]);context.setLineDash([]);
常见的虚线模式,如:
var y = 15;drawDashedLine([]);drawDashedLine([1, 1]); // 或[1]drawDashedLine([10, 10]);drawDashedLine([20, 5]);drawDashedLine([15, 3, 3, 3]);drawDashedLine([20, 3, 3, 3, 3, 3, 3, 3]);drawDashedLine([12, 3, 3]); // Equals [12, 3, 3, 12, 3, 3]function drawDashedLine(pattern) {context.beginPath();context.setLineDash(pattern);context.moveTo(0, y);context.lineTo(300, y);context.stroke();y += 20;}
getLineDash方法:返回一个包含当前虚线样式、长度为非负偶数的数组,如果数组元素的数量是奇数,数组元素会被复制并重复,如:
console.log(context.getLineDash()); // [12, 3, 3, 12, 3, 3]
lineDashOffset属性:设置虚线偏移量,值是浮点数,初始值为0.0,可以为负;正值向左偏,负值向右偏,如:
context.lineDashOffset = 5;
示例:蚂蚁线效果:
function draw(id){var canvas = document.getElementById(id);if(canvas==null){return false;}var context = canvas.getContext("2d");context.fillStyle = "#EEEEFF";context.fillRect(0,0,400,300);march(context); // [mɑːtʃ]}var i=0;function march(context){i++;if(i>16){i=0;}console.log(i);context.clearRect(5,5,110,110);context.setLineDash([4,2]);context.lineDashOffset = -i;context.strokeRect(10,10,100,100);setTimeout(function(){march(context);},50);}draw("canvas");
绘制渐变:绘制线性渐变:fillStyle方法除了添补指定颜色外,还可以用来指定添补的工具,比如渐变工具;createLinearGradient(x1, y1, x2, y2)方法:返回一个CanvasGradient渐变工具,参数x1与y1为渐变起始点坐标,x2和y2为结束点坐标;如:
var gradient = context.createLinearGradient(30, 30, 70, 70);console.log(gradient); // CanvasGradient
利用该工具的addColorStop(pos, color)方法指定色标,参数pos指定色标位置,即一个偏移量,值为0到1之间的浮动点,如0.5 表示颜色会涌如今正中间,第二个参数color指定颜色;该方法必须调用二次以上;
gradient.addColorStop(0, "white");gradient.addColorStop(1, "black");
创建了渐变工具并指定了颜色后,再把该工具赋值给fillStyle或strokeStyle属性,从而利用渐变来进行添补或描边;
context.fillStyle = gradient;context.strokeStyle = gradient;context.fillRect(0, 0, 50, 50);context.lineWidth = 20;context.strokeRect(30, 30, 50, 50);
渐变的坐标位置是相对付画布的,以是绘制的图形和渐变工具的坐标必须匹配;否则,有可能只会显示部分渐变效果;因此,确保渐变与图形对齐非常主要,如:
function createRectLinearGradient(context, x, y, width, height){return context.createLinearGradient(x, y, x + width, y + height);}// ... 利用var gradient = createRectLinearGradient(context, 30, 30, 50, 50);console.log(gradient); // CanvasGradient
如利用两个渐变工具分别进行添补和描边:
// Create gradientsvar lingrad = context.createLinearGradient(0,0,0,150);lingrad.addColorStop(0, '#00ABEB');lingrad.addColorStop(0.5, '#fff');lingrad.addColorStop(0.5, '#26C000');lingrad.addColorStop(1, '#fff');var lingrad2 = context.createLinearGradient(0,50,0,95);lingrad2.addColorStop(0.5, '#000');lingrad2.addColorStop(1, 'rgba(0,0,0,0)');context.fillStyle = lingrad;context.strokeStyle = lingrad2;context.fillRect(10,10,130,130);context.strokeRect(50,50,50,50);
如绘制多少个渐变圆和矩形:
var g = context.createLinearGradient(0,0,300,0);g.addColorStop(0,"rgba(0,0,255,0.5)");g.addColorStop(1,"rgba(255,0,0,0.5)");context.fillStyle = g;for(var i=0; i<10; i++){// 渐变圆// context.beginPath();// context.arc(i25,i25,i10,0,Math.PI2,true);// context.closePath();// context.fill();// 渐变矩形context.fillRect(i25, i25, i10, i10);}
绘制径向渐变:同理,可以利用createRadialGradient(x1, y1, radius1, x2, y2, radius2)方法来绘制径向渐变; x1和y1为开始圆的圆心坐标,radius1为开始圆的半径,x2和y2为结果圆的圆心坐标,radius2为结束圆的半径;其他同线性渐变相同,也是利用addColorStop()方法添加色标;
var g = context.createRadialGradient(300,300,100,300,300,300);g.addColorStop(0.0, "transparent");g.addColorStop(0.7, "rgba(100,100,100,.9)");g.addColorStop(1.0, "rgba(0,0,0,0)");context.fillStyle = g;context.fillRect(0,0,drawing.width, drawing.height);
如,绘制渐变圆:
var g1 = context.createRadialGradient(400,0,0,400,0,400);g1.addColorStop(0.1,'rgb(255,255,0)');g1.addColorStop(0.3,'rgb(255,0,255)');g1.addColorStop(1,'rgb(0,255,255)');context.fillStyle = g1;context.fillRect(0,0,400,300);var g2 = context.createLinearGradient(250,250,0,250,250,300);g2.addColorStop(0.1,"rgba(255,0,0,0.5)");g2.addColorStop(0.7,"rgba(255,255,0,0.5)");g2.addColorStop(1,"rgba(0,0,255,0.5)");context.fillStyle = g2;for(var i=0;i<10;i++){context.beginPath();context.arc(i25,i25,i10,0,Math.PI2,true);context.closePath();context.fill();}
如:
// 创建渐变var radgrad = context.createRadialGradient(40,40,0,52,50,30);radgrad.addColorStop(0, '#A7D30C');radgrad.addColorStop(0.9, '#019F62');radgrad.addColorStop(1, 'rgba(1,159,98,0)');var radgrad2 = context.createRadialGradient(105,105,20,112,120,50);radgrad2.addColorStop(0, '#FF5F98');radgrad2.addColorStop(0.75, '#FF0188');radgrad2.addColorStop(1, 'rgba(255,1,136,0)');var radgrad3 = context.createRadialGradient(95,15,10,102,20,40);radgrad3.addColorStop(0, '#00C9FF');radgrad3.addColorStop(0.8, '#00B5E2');radgrad3.addColorStop(1, 'rgba(0,201,255,0)');var radgrad4 = context.createRadialGradient(0,150,50,0,140,90);radgrad4.addColorStop(0, '#F4F201');radgrad4.addColorStop(0.8, '#E4C700');radgrad4.addColorStop(1, 'rgba(228,199,0,0)');// 画图形context.fillStyle = radgrad4;context.fillRect(0,0,150,150);context.fillStyle = radgrad3;context.fillRect(0,0,150,150);context.fillStyle = radgrad2;context.fillRect(0,0,150,150);context.fillStyle = radgrad;context.fillRect(0,0,150,150);
模式(图案)Pattern:模式便是重复的图像,也称为添补图案,可以用来添补或描边图形;利用createPattern(image,type)方法即可创建新模式,参数image为一个<img>元素或image工具,参数type指定重复的类型,其值与CSS的background-repeat值相同:no-repeat、repeat-x、repeat-y、repeat;返回的工具是CanvasPattern类型的模式工具;创建完模式工具后,再赋给fillStyle即可,如:
var image = document.getElementsByTagName("img")[0];var pattern = context.createPattern(image, "repeat");console.log(pattern); // CanvasPatterncontext.fillStyle = pattern;context.fillRect(50, 50, 100, 100);
模式与渐变一样,都是从画布的原点(0,0)开始的;将模式工具赋给fillStyle属性,只表示在某个特定的区域内显示重复的图像,而不是要从某个位置开始绘制重复的图像;
还可以采取一个<canvas>元素作为其余一个<canvas>元素的图案,如:
var offscreen = document.createElement("canvas"); // 创建一个屏幕外canvasoffscreen.width = offscreen.height = 10; // 设置大小offscreen.getContext("2d").strokeRect(0,0,6,6); // 绘制var pattern = context.createPattern(offscreen, "repeat");context.fillStyle = pattern;context.fillRect(0,0, drawing.width,drawing.height);
CanvasPattern类型的模式工具没有任何属性,只有一个setTransform()方法,其用于对图案进行变形;createPattern()方法的第一个参数,也可以是一个video元素,或者另一个canvas元素
绘制阴影:2D高下文可以给图形绘制阴影效果,其利用context工具的关于阴影属性进行设置:shadowOffsetX、shadowOffsetY:横向或纵向位移,负值表示阴影会往上或左位移,正值则表示会往下或右位移,默认值为0;shadowColor:阴影颜色,默认是完备透明的,如果不指定透明度或颜色,该阴影是不可见的;shadowBlur:可选属性,表示图形阴影边缘的模糊范围,值设定在0到10之间;
context.shadowOffsetX = 5;context.shadowOffsetY = 5;context.shadowColor = "rgba(0, 0, 0, 0.5)";context.shadowBlur = 4;context.fillStyle = '#FF0000';context.fillRect(10,10, 50, 50);context.fillStyle = 'rgba(0, 0, 255, 0.5)';context.fillRect(10, 100, 50, 50);context.shadowOffsetX = 10;context.shadowOffsetY = 10;context.shadowColor = "rgba(100,100,100,0.5)";context.shadowBlur = 7.5;context.translate(0,50);for(var i=0;i<3;i++){context.translate(50,50);createStar(context);context.fill();}function createStar(context){var dx = 100;var dy = 0;var s = 50;context.beginPath();context.fillStyle = "rgba(255,0,0,0.5)";var dig = Math.PI / 5 4;for(var i=0;i<5;i++){var x = Math.sin(idig);var y = Math.cos(idig);context.lineTo(dx+xs,dy+ys);}context.closePath();}
shadowColor不许可利用图案和渐变色;shadowOffsetX和shdowOffsetY属性总是在默认的坐标空间中度量的,它不受rotate()和scale()方法的影响;
保存与规复状态:利用save()和restore()两个方法,可以分别用于保存和规复图形高下文确当前绘制状态;这里的绘画状态指的是坐标原点、变换矩阵,以及图形高下文工具确当前属性值等很多内容;canvas状态存储在栈中,当调用save()方法时会将当前状态设置保存到栈中,当多次调用save()时,会在栈中保存多个状态,之后,在做完想做的事情后,再调用restore()从栈中取出之前保存的状态进行规复,即在栈构造中向前返回一级,连续调用restore()则可以逐级返回;如:
context.fillStyle = "#FF0000";context.save();context.fillStyle = "#00FF00";context.translate(100, 100);context.save();context.fillStyle = "#0000FF";// 从点(100,100)开始绘制蓝色矩形context.fillRect(0,0, 100, 100);context.restore();// 从点(110,110)开始绘制绿色矩形context.fillRect(10,10,100,200);context.restore();// 从点(0,0,)开始绘制赤色矩形context.fillRect(0,0,100,200);save()保存的只是对绘图高下文的设置和变换,不会保存绘图的内容;详细可以运用在以了局所:图像或图形变形(包括即移动,旋转和缩放)、图像裁剪、改变图形高下文的属性(strokeStyle、fillStyle、globalAlpha、lineWidth、lineCap、lineJoin、miterLimit、lineDashOfffset、shadowBlur、shadowColor、shadowOffsetX、shadowOffsetY、globalCompositeOperation、font、textAlign、textBaseline、direction、imageSmothingEnabled);
示例:
context.fillRect(0,0,150,150); // 利用默认设置绘制一个矩形context.save(); // 保存默认状态context.fillStyle = '#09F' // 在原有配置根本上对颜色做改变context.fillRect(15,15,120,120); // 利用新的设置绘制一个矩形context.save(); // 保存当前状态context.fillStyle = '#FFF' // 再次改变颜色配置context.globalAlpha = 0.5;context.fillRect(30,30,90,90); // 利用新的配置绘制一个矩形context.restore(); // 重新加载之前的颜色状态context.fillRect(45,45,60,60); // 利用上一次的配置绘制一个矩形context.restore(); // 加载默认颜色配置context.fillRect(60,60,30,30); // 利用加载的配置绘制一个矩形
绘制变换图形:在绘制图形的时候,有可能须要旋转、缩放图形等变形处理;
变换方法:translate(dx, dy):移动坐标原点到(dx, dy)处;参数dx和dy为X轴和Y轴的偏移量;
context.translate(100,100)context.strokeRect(0,0,200,100);context.beginPath();context.arc(100,100,100,0,Math.PI2);context.stroke();
示例:
for (var i = 0; i < 3; i++) {for (var j = 0; j < 3; j++) {context.save();context.fillStyle = 'rgb(' + (51 i) + ', ' + (255 - 51 i) + ', 255)';context.translate(10 + j 50, 10 + i 50);context.fillRect(0, 0, 25, 25); // 只关注宽和高,不再关注起始坐标context.restore();}}
在一样平常的利用中,在利用变换时,会与状态的保存与规复合营利用,便于下一次的绘制;
rotate(angle):以坐标原点为中央点旋转,angle为弧度,为正以顺时针旋转,为负则以逆时针旋转;
context.rotate(Math.PI / 4);context.strokeRect(200,50,200,100);context.beginPath();context.moveTo(200, 20);context.lineTo(300, 20);context.stroke();
在旋转时,因此原点为中央点旋转的,必要时,须要移动原点,否则有可能会旋转到画布的外侧;
context.translate(100,100);context.rotate(Math.PI / 4);context.fillRect(0,0,200,100);
示例:绘制多少小不同颜色的小圆
context.translate(75,75);for (var i=1; i<6; i++){context.save();context.fillStyle = 'rgb('+(51i)+','+(255-51i)+',255)';for (var j=0; j<i6; j++){context.rotate(Math.PI2/(i6));context.beginPath();context.arc(0, i12.5, 5, 0, Math.PI2, true);context.fill();}context.restore();}
scale(scaleX, scaleY):放大缩小,两个参数都是实数,值为0到1,默认值为1;可以为负数,x 为水平缩放因子,y 为垂直缩放因子,如果比1小,会缩小图形,如果比1大会放大图形;
context.scale(2, 1);context.lineWidth = 20;context.strokeRect(50,50,100,50);
默认情形下,canvas的1个单位为1个像素,如果设置的缩放因子是0.5,那么1个单位就变成对应0.5个像素,这样绘制出来的形状就会是原来的一半;同理,设置为2.0时,1个单位就对应变成了2像素,绘制的结果便是图形放大了2倍;如果线宽为1像素,且缩放因子小于1,此时,还是1像素呈现
context.save();context.scale(0.5, 1); // 线宽为1的,因子小于1的还是1像素呈现// context.lineWidth = 2;context.strokeRect(50, 50, 100, 50);context.restore();context.save();context.scale(10, 3);context.fillRect(1, 100, 10, 10);context.restore();
画布初始情形下,因此左上角坐标为原点的第一象限,如果参数为负实数,相称于以 x 或 y 轴作为对称轴镜像反转;例如:
context.translate(0, canvas.height);context.scale(1,-1);context.save();context.lineWidth = 20;context.moveTo(180,10);context.lineTo(330,150);context.lineTo(30,150);context.closePath();context.stroke(); // 变成倒三角了context.restore();context.translate(canvas.width, 0);context.scale(-1, 1);context.font = '48px serif';context.fillText('ZERO', 10, 200);
变换有可能很大略,有可能很繁芜,这都要视情形而定,如绘制螺旋的长方形:
context.fillStyle = "#EEEEFF";context.fillRect(0,0,400,300);context.translate(200,50);// context.strokeRect(0, 0, 4, 4); // 现原点context.fillStyle = 'rgba(255,0,0,0.25)';for(var i=0; i<50; i++){ // 把50转成3,不雅观察绘制过程context.translate(25,25);// context.strokeRect(0, 0, 4, 4); // 现原点context.scale(0.95,0.95);context.rotate(Math.PI / 10);context.fillRect(0,0,100,50);}
示例:绘制表盘和表针
// ...把前的代码换成以下的context.translate(100,100);context.rotate(1);// 分针context.moveTo(0, 0);context.lineTo(0, -85);// 时针context.moveTo(0,0);context.lineTo(-65,0);// ...
示例:科赫雪花分形:
var deg = Math.PI / 180;function snowflake(c, n, x, y, len){c.save();c.translate(x, y);c.moveTo(0, 0);draw(n);c.rotate(-120 deg);draw(n);c.rotate(-120 deg);draw(n);c.closePath();c.restore();function draw(n){c.save();if(n == 0){c.lineTo(len, 0);}else{c.scale(1/3, 1/3);draw(n - 1);c.rotate(60 deg);draw(n - 1);c.rotate(-120 deg);draw(n - 1);c.rotate(60 deg);draw(n - 1);}c.restore();c.translate(len, 0);}}snowflake(context, 0, 5, 115, 125);snowflake(context, 1, 145, 115, 125);snowflake(context, 2, 285, 115, 125);snowflake(context, 3, 425, 115, 125);snowflake(context, 4, 565, 115, 125);context.stroke();
矩阵变换:当利用坐标变换不能知足我们的须要时,可以利用矩阵变换的技能;矩阵变换是专门用来实现图形变形的,它与坐标一起合营利用,以达到变形的目的;当context创建完毕后,进行一系列的属性设置和绘图操作,都是利用默认的坐标系;除了这个默认的坐标系,还有一个默认的“当前变换矩阵“,其作为当前图形状态的一部分,定义了画布确当前坐标系;如果不对这个变换矩阵进行修正,那么接下来绘制的图形将以画布的最左上角为坐标原点绘制图形,绘制出来的图形也不经由缩放、变形处理,会被直接绘制,但是如果对这个变换矩阵进行修正,会导致利用不同的变换矩阵运用场置,从而产生不同的效果;当前变换矩阵是用来将指定的坐标转换成为默认坐标系中的等价坐标。
transform(a, b, c, d, e, f),变换矩阵;其利用一个新的变换矩阵与当前变换矩阵进行乘法运算,该变换矩阵的描述是:a(m11) c(m21) e(dx)b(m12) d(m22) f(dy)0 0 1个中,a(m11)、b(m12)、c(m21)、d(m22)这4个参数用来决定如何变形,分别表示水平缩放、垂直倾斜、水平倾斜、垂直缩放;e(dx)和f(dy)参数表示水平和垂直移动坐标原点;
context.save();context.fillStyle = "red";context.fillRect(0,0,100,100);context.fillRect(100,120,100,100);context.restore();context.translate(100,120);context.transform(2, Math.PI / 4, -Math.PI / 4, 0.5, 50, 50);context.fillRect(0,0,100,100);
示例:通过变换绘制螺旋
context.translate(200,50);for(var i=0; i<50; i++){context.save();context.transform(0.95,0,0,0.95,30,30);context.rotate(Math.PI/12);context.beginPath();context.fillStyle = 'rgba(255,0,0,0.5)';context.arc(0,0,50,0,Math.PI2,true);context.closePath();context.fill();}
示例:多条彩色弧
var colors = ["red","orange","yellow","green","blue","navy","purple"];context.lineWidth = 10;context.transform(1,0,0,1,100,0);for(var i=0;i<colors.length;i++){context.transform(1,0,0,1,0,10);context.strokeStyle = colors[i];context.beginPath();context.arc(50,100,100,0,Math.PI,true);context.stroke();}
setTransform(a, b, c, d, e, f)方法:这个方法会将当前的变形矩阵重置为默认矩阵,然后用相同的参数调用transform()方法;从根本上来说,该方法是取消了当前变形,然后再设置为指定的变形,如:
// context.transform(1,1,0,1,0,0);context.setTransform(1,1,0,1,0,0); // 起到相同的效果context.fillRect(0,0,100,100);
它常常被用在,临时将画布重置为默认坐标系,如:
context.strokeStyle = "red";context.strokeRect(30,10,60,20);// 旋转45度var rad = 45 Math.PI / 180;context.setTransform(Math.cos(rad),Math.sin(rad),-Math.sin(rad),Math.cos(rad),0,0);context.strokeStyle = "blue";context.strokeRect(30,10,60,20);// 放大2.5倍context.setTransform(2.5,0,0,2.5,0,0);context.strokeStyle = "green";context.strokeRect(30,10,60,20);// 移动坐标原点context.setTransform(1,0,0,1,40,80);context.strokeStyle = "gray";context.strokeRect(30,10,60,20);context.save(); // 保存当前默认坐标系context.setTransform(1,0,0,1,0,0); // 规复到默认坐标系// 利用默认的坐标进行绘图操作context.restore(); // 规复保存的坐标系setTransform()与transform()差异:// context.transform(2,0,0,1,0,0);context.setTransform(2,0,0,1,0,0);context.fillRect(0,0,100,100);// context.transform(2,0,0,2,0,0);context.setTransform(2,0,0,2,0,0);context.fillStyle = "red";context.fillRect(50,50,100,100);
再如:
var sin = Math.sin(Math.PI/6);var cos = Math.cos(Math.PI/6);context.translate(100, 100);var c = 0;for (var i=0; i <= 12; i++) {c = Math.floor(255 / 12 i);context.fillStyle = "rgb(" + c + "," + c + "," + c + ")";context.fillRect(0, 0, 100, 10);context.transform(cos, sin, -sin, cos, 0, 0);}context.setTransform(-1, 0, 0, 1, 100, 100);context.fillStyle = "rgba(255, 128, 255, 0.5)";context.fillRect(0, 50, 100, 100);
resetTransform():方法重置当前变形矩阵,它和调用以下语句是一样的:context.setTransform(1, 0, 0, 1, 0, 0);
context.rotate(45 Math.PI / 180);context.fillRect(70,0,100,30);context.resetTransform();
深入谈论:translate、scale、rotate三个方法,实际上都是隐式的修正了变换矩阵;translate()方法只是大略的将坐标原点进行高下旁边移动;而rotate()方法会将坐标轴根据指定的角度进行顺时针旋转;scale()方法实现对X轴或Y轴上的间隔进行延长和缩短;假定变换后坐标系中的点的坐标是(x, y),其对应的默认坐标系的坐标为(x‘, y‘);
调用translate(dx, dy)方法等效于:
x’ = x + dx; y’ = y + dy;
调用scale(sx, sy)方法等效于:x’ = sx x;y’ = sy y;
调用rotate(a)方法,可以通过三角法则打算,如:x’ = x cos(a) – y sin(a);y’ = y cos(a) + x sin(a);
坐标系变换是与变换顺序干系的;假设从画布默认的坐标系开始,前辈行位移,再进行伸缩;如此操作后,要想将现有坐标系中的点(x, y)映射成默认坐标系中的点(x’’, y’’),必须首先运用等效缩放等式把该点映射到未缩放坐标系中的一个中间点(x’, y’),然后再利用等效的位移将中间点再映射到原来坐标系中的点(x’’, y’’),结果是:x’’ = sx x + dx;y’’ = sy y + dy;
如果先调用scale()再调用translate()的话,那等效的结果就不同了,结果是:x’’ = sx (x + dx);y’’ = sy (y + dy);
translate、scale、rotate三个方法都可以利用transform方法进行处理:translate(x, y)可以利用transform(1, 0, 0, 1, x, y)代替,参数1,0,0,1表示不对图形进行缩放倾斜操作,只是位移x和y;scale(x, y)可以利用transform(x, 0, 0, y, 0, 0)代替,参数x,0,0,y表示将图形横向扩大x倍,纵向扩大y倍;rotate(angle)更换方法:transform(Math.cos(angle Math.PI / 180), Math.sin(angle Math.PI / 180),-Math.sin(angle Math.PI / 180), Math.cos(angle Math.PI / 180),0,0)
个中前4个参数以三角函数的形式结合起来,共同完成图形按angle角度的顺时针旋转处理;