最近笔者在学习HTML5的新元素<canvas>
,会分享一些基础知识以及小例子,最终使用<canvas>
实现一个绘制简单图表(条形图、线图或者饼图)的js库,会更新一到两篇文章~
下面咱们开始吧~css
咱们首先应该指定<canvas>
标签即画布的宽度和高度属性,并在开始和闭合标签之间添加后备信息:html
<html> <head> <meta charset="utf-8"> </head> <body> <canvas id="canvas" width="500" height="500">Canvas is not supported.</canvas> </body> </html>
调用canvas
的getContext()
方法,这个方法接收一个参数,即上下文的名字:前端
var canvas = document.getElementById("canvas"); if(canvas.getContext) { var context = canvas.getContext("2d") }
上述例子中,咱们在调用getContext()
方法时,首先检测其是否存在,这是因为有的浏览器遇到HTML规范以外的标签时,也会建立一个DOM对象,好比咱们在Chrome中尝试如下代码:html5
<ppp id="ppp"></ppp>
document.getElementById("ppp"); //<ppp id="ppp"></ppp>
所以即便当前浏览器不能兼容HTML规范中的canvas元素,一样会建立DOM对象,可其中却不存在getContext()
。context
上下文对象中包含了绘图须要的一系列属性和方法,你们在阅读本文时,记得区分属性和方法,改变属性会影响到后续的绘图效果,而调用方法每每是一次性的。git
2D上下文的坐标默认开始于左上角,原点坐标为(0, 0),使用translate(x, y)
能够更改坐标原点。github
填充,就是用指定的样式,好比颜色、渐变或图像填充指定区域,对应的上下文属性为fillStyle
,默认值为#000000
, 好比:canvas
context.fillStyle = "orange";
描边就是在指定区域边缘画指定样式的线,好比:浏览器
context.strokeStyle = "grey";
矩形是惟一一个能够在2D上下文中直接话的形状,其余的都须要绘制路径,可使用3个方法直接绘制矩形。ide
能够绘制一个由fillStyle
指定填充样式的矩形,好比:工具
context.translate(100, 100) context.fillStyle = "#99cccc"; context.fillRect(-100, -50, 200, 100); context.fillStyle="#3399cc"; context.fillRect(-60, -30, 120, 60);
能够绘制一个由strokeStyle
lineWidth
lineCap
lineJoin
等属性指定描边样式的矩形,好比:
context.strokeStyle = "#99cccc"; context.lineWidth = "50"; context.lineJoin = "bevel"; context.strokeRect(0, 0, 400, 200);
属性名 | 含义 | 取值 |
---|---|---|
lineCap | 线条末端的形状 | butt 平头round 圆头square 方头 |
lineWidth | 线条宽度 | 整数 |
lineJoin | 线条相交的方式 | round 圆交bevel 斜交mitter 斜接 |
能够清楚画布上的指定区域,好比第一个例子中的两个矩形,咱们将中间一小块清除:
context.translate(100, 100) context.fillStyle = "#99cccc"; context.fillRect(-100, -50, 200, 100); context.fillStyle="#3399cc"; context.clearRect(-60, -30, 120, 60);
使用路径咱们能够绘制出比较复杂的图形,开始绘制前,首先执行:
context.beginPath();
结束绘制时,执行:
context.closePath();
如下咱们列举了绘制路径的几个方法:
lineTo(x, y)
,从当前游标至(x, y)画一条直线。
moveTo(x, y)
将游标移动至(x, y),移动过程当中不画线。好比:
context.beginPath(); context.moveTo(10, 10); context.lineTo(50, 40); context.moveTo(50, 50); context.lineTo(100, 90); context.stroke(); context.closePath();
从startAngle到endAngle绘制一条以(x, y)为圆心,radius为半径的弧线,其中startAngle和endAngle用弧度表示,couterclockwise为false时,顺时针画弧线, 反之,逆时针画弧线。
var canvas = document.getElementById("canvas"); if(canvas.getContext) { var context = canvas.getContext("2d"); context.beginPath(); context.arc(400, 400, 50, arcUnit()*30, arcUnit()*180, false); context.stroke(); } function arcUnit() { return Math.PI/180; }
arcTo()
方法将利用当前坐标、起点(startX,startY)和终点(endX,endY)这三个点所造成的夹角,绘制一段与夹角的两边相切而且半径为radius的圆上的弧线。弧线的起点就是当前坐标所在边与圆的切点,弧线的终点就是终点(endX,endY)所在边与圆的切点,而且绘制的弧线是两个切点之间长度最短的那个圆弧。此外,若是当前端点不是(startX,startY),arcTo()方法还将添加一条当前端点到(startX,startY)的直线线段。
若是你们还记得高中数学的话,咱们应该能够猜到,使用这个方法画弧线大体有三种状况:
** 只有一种半径可使弧线两端正好在起点和终点
** 若是半径过大,画不出弧线
** 若是半径较小,一定会有一条当前端点到起点的直线
咱们举三个例子:
首先定义一些通用的
var context = canvas.getContext("2d"); var currentPoint = { x: 0, y: 0 }; var startPoint = { x: 50, y: 50 }; var endPoint = { x: 100, y: 0 };
而后绘制参考线
context.moveTo(currentPoint.x, currentPoint.y); context.lineTo(startPoint.x, startPoint.y); context.lineTo(endPoint.x, endPoint.y); context.strokeStyle = "red"; context.stroke();
画第一条弧线
context.moveTo(currentPoint.x, currentPoint.y); context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 80); context.strokeStyle = "grey"; context.stroke();
context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 120);
context.arcTo(startPoint.x, startPoint.y, endPoint.x, endPoint.y, 40);
quadraticCurveTo(cpX, cpY, x, y)
上述方法能够画一条从当前位置到(x, y), 以(cpX, cpY)为控制点的贝塞尔曲线
bezierCurveTo(cpX1, cpY1, cpX2, cpY2, x, y)
上述方法能够画一条从当前位置到(x, y), 以(cpX1, cpY1)和(cpX2, cpY2)为控制点的贝塞尔曲线。
二次贝塞尔与三次贝塞尔的绘制涉及到比较复杂的数学运算,笔者在此就忽略啦...
固然,大部分前端人士可能都跟笔者同样,只但愿能画出一条优美的曲线,并不关心实现细节,那么通常认为何样的曲线才算是优美的曲线呢:
根据以上规则,咱们写一个工具方法:
function drawCurvePath( ctx, start, end, curveness ) { var cp = [ ( start.x + end.x ) / 2 - ( start.y - end.y ) * curveness, ( start.y + end.y ) / 2 - ( end.x - start.x ) * curveness ]; ctx.moveTo( start.x, start.y ); ctx.quadraticCurveTo( cp[ 0 ], cp[ 1 ], end.x, end.y ); ctx.stroke(); }
以上参考自用canvas绘制一个曲线动画——深刻理解贝塞尔曲线,你们能够前往了解更深刻的贝塞尔曲线画法。
使用rect(x, y, width, height)
能够绘制一个左上角坐标为(x, y),宽width,高height的矩形路径。
context.rect(300, 300, 100, 200); context.stroke();
在绘制本文以前,若是有必要,咱们首先应该指定context的几个属性, 好比:
context.font = "bold 14px Arial"; // 格式同css中指定字体样式 context.textAlign = "center"; // start end center left right context.textBaseline = "middle"; // top hanging middle alphabetic ideographic bottom
fillText(text, x, y, maxWidth)
使用fillStyle
属性显示文本,strokeText(text, x, y, maxWidth)
使用strokeStyle
属性为文本描边。
使用measureText(text)
方法能够得到文本的宽度。若是咱们并不清楚指定的宽度够不够显示当前字体设置下的一段文字,可使用以下方法:
var fontSize = 50; var maxWidth = 100; context.font = "bold " + fontSize+"px Arial"; var text = "Hello World!"; while(context.measureText(text).width > maxWidth) { fontSize--; context.font = "bold " + fontSize+"px Arial"; } context.fillText(text, 50, 50, maxWidth);
rotate(angle)
context.rotate(Math.PI/4) context.fillText(text, 50, 50, maxWidth);
scale(scaleX, scaleY)
context.scale(1.2, 1.2); context.fillText(text, 50, 50, maxWidth); context.scale(0.5, 0.5); context.fillText(text, 50, 50, maxWidth);
translate(x, y)
假如咱们要绘制一个对称图形,移动坐标原点将会大大简化对坐标的计算。
使用transform(scaleX,skewX,skewY,scaleY,transX,transY)
能够进行矩阵变换,其实以上讲的三个方法本质上都在调用矩阵变换,从参数名中能够看出,它们分别表示X轴方向的缩放,Y轴方向的缩放,X轴方向的斜切,Y轴方向的斜切,X轴方向的偏移量,Y轴方向的偏移量。默认值分别为1 0 0 1 0 0。
咱们如今使用transform()
从新定义一遍以前的三个方法:
rotate
function rotate (ctx, degree) { var unit = Math.PI/180; ctx.transform(Math.cos(degree*unit),Math.sin(degree*unit),-Math.sin(degree*unit),Math.cos(degree*unit),0,0) }
scale
function scale (ctx, scale) { ctx.transform(scale.x, 0, 0, scale.y, 0, 0); }
translate
function translate (ctx, translate) { ctx.transform(1, 0, 0, 1, translate.x, translate.y); }
咱们如今绘制一段文本,先平移(50, 50),而后缩放2倍,最后旋转30度。
context.fillText(text, 50, 50, maxWidth); translate(context, {x: 50, y: 50}); scale(context, {x: 2, y: 2}); rotate(context, 30); context.fillText(text, 50, 50, maxWidth);
每次执行transform()
都是基于上一次的结果,并非初始状态,不少时候咱们想执行的是基于初始状态旋转、平移或缩放,而非上一次的状态,此时咱们可使用setTransfrom(scaleX,skewX,skewY,scaleY,transX,transY)
,此方法首先会重置变换矩阵,而后执行transform()
。
更加详细的内容能够参考html5 Canvas画图教程26:用transform来实现位移,缩放,旋转等
使用drawImage(image, x1, y1, width1, height1, x2, y2, width2, height2)
能够将图像的指定部分按照指定的大小绘制到画布指定的位置上。
参数 | 含义 |
---|---|
image | 要绘制的图像,能够是HTMLImageElement,也能够是canvas |
x1 | 源图像的x坐标 |
y1 | 源图像的y坐标 |
width1 | 源图像的宽度 |
height1 | 源图像的高度 |
x2 | 画布的x坐标 |
y2 | 画布的y坐标 |
width2 | 图像在画布上显示的宽度 |
height2 | 图像在画布上显示的高度 |
若是要给形状或路径加上阴影,咱们要在绘制前设置context
对象的如下属性:
属性 | 含义 |
---|---|
shadowColor | 阴影颜色 |
shadowOffsetX | x轴偏移量 |
shadowOffsetY | y轴偏移量 |
shadowBlur | 模糊的像素数 |
context.shadowColor = "grey"; context.shadowOffsetX = "20"; context.shadowBlur = "5"; context.fillText(text, 50, 50, maxWidth);
使用createLinearGradient(startX, startY, endX, endY)
来建立线性渐变,这个方法确认了渐变的起始和方向,而后咱们经过addColorStop(position, color)
来添加渐变的颜色,position是0到1的数字。一下笔者画一个超级喜欢的条纹渐变:
var grad = context.createLinearGradient(50, 50, 200, 200) grad.addColorStop(0, "grey"); grad.addColorStop(0.3, "grey"); grad.addColorStop(0.3, "red"); grad.addColorStop(0.5, "red"); grad.addColorStop(0.5, "orange"); grad.addColorStop(0.7, "orange"); context.fillStyle = grad; context.fillRect(50, 50, 150, 150);
使用createRadialGradient(centerX1, centerY1, radius1, centerX2, centerY2, radius2)
建立径向渐变。
使用createPattern(image, repeatType)
能够绘制重复的图像,用来填充或描边,第一个参数是要重复的HTMLImageElement或canvas或video,第二个参数表示如何重复该图像,可取repeat
repeat-x
repeat-y
no-repeat