Canvas的概念最初是由苹果公司提出的,用于在Mac OS X WebKit中创建控制板部件(dashboard widget)。在Canvas出现之前,开发人员若要在浏览器中使用绘图API,只能使用Adobe的Flash和SVG(Scalable Vector Graphics,可伸缩矢量图形)插件,或者只有IE才支持的VML(Vector Markup Language,矢量标记语言),以及其他一些稀奇古怪的JavaScript技巧。
假设我们要在没有canvas元素的条件下绘制一条对角线--听起来似乎很简单,但实际上如果没有一套二维绘图API的话,这会是一项相当复杂的工作。HTML5 Canvas能够提供这样的功能,对浏览器端来说此功能非常有用,因此Canvas被纳入了HTML5规范。
起初,苹果公司曾暗示可能会为WHATWG(Web Hypertext Application Technology Working Group,Web超文本应用技术工作组)草案中的Canvas规范申请知识产权,这在当时引起了一些Web标准化追随者的关注。不过,苹果公司最终还是按照W3C的免版税专利权许可条款公开了其专利。
"Canvas本质上是一个位图画布,其上绘制的图形是不可缩放的,不能像SVG图像那样可以被放大缩小。此外,用Canvas绘制出来的对象不属于页面DOM结构或者任何命名空间--这点被认为是一个缺陷。SVG图像却可以在不同的分辨率下流畅地缩放,并且支持点击检测(能检测到鼠标点击了图像上的哪个点)。
既然如此,为什么WHATWG的HTML5规范不使用SVG呢?尽管Canvas有明显的不足,但HTML Canvas API有两方面优势可以弥补:首先,不需要将所绘制图像中的每个图元当做对象存储,因此执行性能非常好;其次,在其他编程语言现有的优秀二维绘图API的基础上实现Canvas API相对来说比较简单。毕竟,二鸟在林不如一鸟在手。
在网页上使用canvas元素时,它会创建一块矩形区域。默认情况下该矩形区域宽为300像素,高为150像素,但也可以自定义具体的大小或者设置canvas元素的其他特性。一下代码是可放到HTML页面中的最基本的canvas元素。
<canvas></canvas>
在页面中加入了canvas元素后,我们便可以通过JavaScript来自由地控制它。可以在其中添加图片、线条以及文字,也可以在里面绘图,甚至还可以加入高级动画。
大多数主流操作系统和框架支持的二维绘制操作,HTML5 Canvas API都支持。如果你在近年来曾经有过二维图像编程的经验,那么会对HTML5 Canvas API感觉非常顺手,因为这个API就是参照既有系统设计的。如果没有这方面经验,则会发现与这么多年来一直使用的图片加CSS开发Web图形的方式比起来,Canvas的渲染系统有多么强大。
使用canvas编程,首先要获取其上下文(context)。接着在上下文中执行动作,最后将这些动作应用到上下文中。可以将canvas的这种编辑方式想象成为数据库事务:开发人员先发起一个事务,然后执行某些操作,最后提交事务。
尽管canvas元素功能非常强大,用处也很多,但在某些情况下,如果其他元素已经够用了,就不应该再使用canvas元素。例如,用canvas元素在HTML页面中动态绘制所有不同的标题,就不如直接使用标题样式标签(H1、H2等),它们所实现的效果是一样的。
提示:以下所有实验请使用的浏览器为Google Chrome 33.0.1701.0
HBuilder是DCloud推出的一款支持HTML5的Web开发IDE。快,是HBuilder的最大优势,通过完整的语法提示和代码输入法、代码块等,大幅提升HTML、js、css的开发效率。同时,它还包括最全面的语法库和浏览器兼容性数据。
context:一直觉得这个翻译成“上下文”,context是一个封装了很多绘图功能的对象,获取这个对象的方法是
var context = canvas.getContext("2d");
注:html5暂时不支持3d功能。
canvas元素绘制图像的时候有两种方法,分别是
context.fill()//填充
context.stroke()//绘制边框
style:在进行图形绘制前,要设置好绘图的样式
context.fillStyle//填充的样式
context.strokeStyle//边框样式
context.lineWidth //图形边框宽度
颜色的表示方式:
直接用颜色名称:"red" "green" "blue"
十六进制颜色值: "#EEEEFF"
rgb(1-255,1-255,1-255)
rgba(1-255,1-255,1-255,透明度)
语法:
fillRect(x,y,width,height)
strokeRect(x,y,width,height)
参数说明:
x:矩形起点横坐标
y:矩形起点纵坐标
width:矩形长度
height:矩形高度
代码:
function drawRect() { var canvas = document.getElementById("mycanvas"); if (canvas == null) return false; var context = canvas.getContext("2d"); //实践表明在不设施fillStyle下的默认fillStyle=black context.fillRect(0, 0, 100, 100); //实践表明在不设施strokeStyle下的默认strokeStyle=black context.strokeRect(120, 0, 100, 100); //设置纯色 context.fillStyle = "red"; context.strokeStyle = "blue"; context.fillRect(0, 120, 100, 100); context.strokeRect(120, 120, 100, 100); //设置透明度实践证明透明度值>0,<1值越低,越透明,值>=1时为纯色,值<=0时为完全透明 context.fillStyle = "rgba(255,0,0,0.2)"; context.strokeStyle = "rgba(255,0,0,0.2)"; context.fillRect(240,0 , 100, 100); context.strokeRect(240, 120, 100, 100); }
语法:
clearRect(x,y,width,height)
参数说明:
x:清除矩形起点横坐标
y:清除矩形起点纵坐标
width:清除矩形长度
height:清除矩形高度
代码:
function clearRect() { var canvas = document.getElementById("mycanvas"); if (canvas == null) return false; var context = canvas.getContext("2d"); //实践表明在不设施fillStyle下的默认fillStyle=black context.fillRect(0, 0, 100, 100); //实践表明在不设施strokeStyle下的默认strokeStyle=black context.strokeRect(120, 0, 100, 100); //设置纯色 context.fillStyle = "red"; context.strokeStyle = "blue"; context.fillRect(0, 120, 100, 100); context.strokeRect(120, 120, 100, 100); //设置透明度实践证明透明度值>0,<1值越低,越透明,值>=1时为纯色,值<=0时为完全透明 context.fillStyle = "rgba(255,0,0,0.2)"; context.strokeStyle = "rgba(255,0,0,0.2)"; context.fillRect(240, 0, 100, 100); context.strokeRect(240, 120, 100, 100); // 清除矩形 context.clearRect(50, 50, 240, 120); }
语法:
arc(x, y, radius, starAngle,endAngle, anticlockwise)
参数说明:
x:圆心的x坐标
y:圆心的y坐标
radius:圆半径
straAngle:开始角度
endAngle:结束角度
anticlockwise:是否逆时针(true)为逆时针,(false)为顺时针
代码:
function drawArc() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); context.beginPath(); context.arc(200, 150, 100, 0, Math.PI * 2, true); context.closePath(); context.fillStyle = 'rgba(0,255,0,0.25)'; context.fill(); }
语法:
beginPath() //绘制路径的开始点
closePath() //绘制路径的结束点
代码:
function drawPath() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); var n = 0; //左侧1/4圆弧 context.beginPath(); context.arc(100, 150, 50, 0, Math.PI/2 , false); context.fillStyle = 'rgba(255,0,0,0.25)'; context.fill(); context.strokeStyle = 'rgba(255,0,0,0.25)' context.closePath(); context.stroke(); //右侧1/4圆弧 context.beginPath(); context.arc(300, 150, 50, 0, Math.PI*1.5 , true); context.fillStyle = 'rgba(0,255,0,0.25)'; context.fill(); context.strokeStyle = 'rgba(0,255,0,0.25)'; context.closePath(); context.stroke(); }
语法:
moveTo(x,y)
lineTo(x,y)
参数说明:
x:x坐标
y:y坐标
每次画线都从moveTo的点到lineTo的点,如果没有moveTo那么第一次lineTo的效果和moveTo一样,每次lineTo后如果没有moveTo,那么下次lineTo的开始点为前一次lineTo的结束点。
代码:
function drawLine() { var canvas = document.getElementById("mycanvas"); if (canvas == null) return false; var context = canvas.getContext("2d"); context.strokeStyle = "rgb(250,0,0)"; context.fillStyle = "rgb(250,0,0)" //实验证明第一次lineTo的时候和moveTo功能一样 context.lineTo(100, 100); //之后的lineTo会以上次lineTo的节点为开始 context.lineTo(200, 200); context.lineTo(200, 100); context.moveTo(200, 50); context.lineTo(100,50); context.stroke(); }
语法:
bezierCurveTo(cp1x,cp1y,cp2x,cp2y,x,y)
参数说明:
cp1x:第一个控制点x坐标
cp1y:第一个控制点y坐标
cp2x:第二个控制点x坐标
cp2y:第二个控制点y坐标
x:终点x坐标
y:终点y坐标
代码:
function drawBezier() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext("2d"); context.moveTo(50, 50); context.bezierCurveTo(50, 50,150, 50, 150, 150); context.stroke(); context.moveTo(50, 50); context.lineTo(150, 50); context.lineTo(150, 150); context.stroke(); }
语法:
quadraticCurveTo(qcpx,qcpy,qx,qy)
参数说明:
qcpx:二次样条曲线控制点x坐标
qcpy:二次样条曲线控制点y坐标
qx:二次样条曲线终点x坐标
qy:二次样条曲线终点y坐标
代码:
function drawQuadratic() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext("2d"); context.moveTo(150, 150); context.quadraticCurveTo(150, 250, 250, 250); context.stroke(); context.moveTo(150, 150); context.lineTo(150, 250); context.lineTo(250, 250); context.stroke(); }
语法:
createLinearGradient(xStart,yStart,xEnd,yEnd)
addColorStop(offset,color)
参数说明:
xstart:渐变开始点x坐标
ystart:渐变开始点y坐标
xEnd:渐变结束点x坐标
yEnd:渐变结束点y坐标
offset:设定的颜色离渐变结束点的偏移量(0~1)
color:绘制时要使用的颜色
代码:
function drawLinear() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); var g1 = context.createLinearGradient(0, 0, 0, 300); g1.addColorStop(0, 'rgb(255,0,0)'); //红 g1.addColorStop(0.5, 'rgb(0,255,0)');//绿 g1.addColorStop(1, 'rgb(0,0,255)'); //蓝 //可以把lg对象理解成GDI中线性brush context.fillStyle = g1; //再用这个brush来画正方形 context.fillRect(0, 0, 400, 300); }
语法:
createRadialGradient(xStart,yStart,radiusStart,xEnd,yEnd,radiusEnd)
addColorStop(offset,color)
参数说明:
xStart:发散开始圆心x坐标
yStart:发散开始圆心y坐标
radiusStart:发散开始圆的半径
xEnd:发散结束圆心的x坐标
yEnd:发散结束圆心的y坐标
radiusEnd:发散结束圆的半径
offset:设定的颜色离渐变结束点的偏移量(0~1)
color:绘制时要使用的颜色
代码:
function drawRadial() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); var g1 = context.createRadialGradient(200, 150, 0, 200, 150, 100); g1.addColorStop(0.1, 'rgb(255,0,0)'); g1.addColorStop(1, 'rgb(50,0,0)'); context.fillStyle = g1; context.beginPath(); context.arc(200, 150, 100, 0, Math.PI * 2, true); context.closePath(); context.fill(); var g1 = context.createRadialGradient(100, 150, 10, 300, 150, 50); g1.addColorStop(0.1, 'rgb(255,0,0)'); g1.addColorStop(0.5, 'rgb(0,255,0)'); g1.addColorStop(1, 'rgb(0,0,255)'); context.fillStyle = g1; context.fillRect(0, 0, 400, 300); }
语法:
平移 translate(x,y)
参数说明:
x:坐标原点向x轴方向平移x
y:坐标原点向y轴方向平移y
语法:
缩放 scale(x,y)
参数说明:
x:x坐标轴按x比例缩放
y:y坐标轴按y比例缩放
语法:
旋转 rotate(angle)
参数说明:
angle:坐标轴旋转x角度(角度变化模型和画圆的模型一样)
代码:
function changeGraph() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); context.save(); //保存了当前context的状态 context.fillStyle = "#EEEEFF"; context.fillRect(0, 0, 400, 300); context.fillStyle = "rgba(255,0,0,0.1)"; //平移 缩放 旋转 1 2 3 context.translate(100, 100); context.scale(0.5, 0.5); context.rotate(Math.PI / 4); context.fillRect(0, 0, 100, 100); context.restore(); //恢复到刚刚保存的状态,保存恢复只能使用一次 context.save(); //保存了当前context的状态 //实验表明应该移动的是坐标轴 //实验表明缩放的是坐标轴比例 //实验表明旋转的是坐标轴 //综合上述,因此平移 缩放 旋转 三者的顺序不同都将画出不同的结果 }
语法:
shadowOffsetX :阴影的横向位移量(默认值为0)
shadowOffsetY :阴影的纵向位移量(默认值为0)
shadowColor :阴影的颜色
shadowBlur :阴影的模糊范围(值越大越模糊)
代码:
function drawShadow() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); context.shadowOffsetX = 20; context.shadowOffsetY = 20; context.shadowColor = 'rgba(100,100,100,0.5)'; context.shadowBlur = 2.5; context.fillStyle = 'rgba(255,0,0,0.5)'; context.fillRect(100, 100, 200, 100); }
语法:
填充文字:fillText(text,x,y)
绘制文字轮廓: strokeText(text,x,y)
计算字体长度(px): measureText(text)
参数说明:
text:要绘制的文字
x:文字起点的x坐标轴
y:文字起点的y坐标轴
代码:
function drawText() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); context.fillStyle = "#EEEEFF"; context.fillRect(0,0,400,300); context.fillStyle = "#00f"; context.font = "italic 30px sans-serif"; //填充字符串 var txt="fill示例文字"; context.fillText(txt, 0, 30); var length=context.measureText(txt); context.fillText("长" + length.width + "px", 0, 80); context.font = "bolid 30px sans-serif"; txt = "stroke示例文字"; length = context.measureText(txt); context.strokeText(txt,0,130); context.fillText("长" + length.width + "px", 0, 180); }
语法:
保存save()
恢复restore()
使用说明:
save()调用该方法,会保存当前context的状态、属性(把他理解成游戏存档);restore()调用该方法就能恢复到save时候context的状态、属性(游戏回档)
代码:
function saveAndRestore() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); context.fillStyle = "red"; context.save(); //保存了当前context的状态 context.fillStyle = "black"; context.fillRect(0, 0, 100, 100); context.restore();//恢复到刚刚保存的状态 context.fillRect(0,120,100,100); }
语法:
toDataURL(MIME)
使用说明:
在canvas中绘出的图片只是canvas标签而已,并非是真正的图片,是不能右键,另存为的,我们可以利用canvas.toDataURL()这个方法把canvas绘制的图形生成一幅图片,生成图片后,就能对图片进行相应的操作了。
代码:
function saveFile() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); context.fillStyle = "rgb(0,0,225)"; context.fillRect(0, 0, canvas.width, canvas.height); context.fillStyle = "rgb(255,255,0)"; context.fillRect(10, 20, 50, 50); //把图像保存到新的窗口 var w = window.open(canvas.toDataURL("image/jpeg"),"smallwin","width=400,height=350"); }
语法:
globalCompositeOperation=type
使用说明:
source-over(默认值):在原有图形上绘制新图形
destination-over:在原有图形下绘制新图形
source-in:显示原有图形和新图形的交集,新图形在上,所以颜色为新图形的颜色
destination-in:显示原有图形和新图形的交集,原有图形在上,所以颜色为原有图形的颜色
source-out:只显示新图形非交集部分
destination-out:只显示原有图形非交集部分
source-atop:显示原有图形和交集部分,新图形在上,所以交集部分的颜色为新图形的颜色
destination-atop:显示新图形和交集部分,新图形在下,所以交集部分的颜色为原有图形的颜色
lighter:原有图形和新图形都显示,交集部分做颜色叠加
xor:重叠飞部分不现实
copy:只显示新图形
代码:
function graphComb() { var canvas = document.getElementById("mycanvas"); if (canvas == null) { return false; } var context = canvas.getContext('2d'); var oprtns = new Array( "source-over", "destination-over", "source-in", "destination-in", "source-out", "destination-out", "source-atop", "destination-atop", "lighter", "xor", "copy" ); var i = 0;//组合效果编号 //结合setinterval动态显示组合 var interal = setInterval(function () { if (i == 10) { i=0; } else { i++; } //蓝色矩形 context.fillStyle = "blue"; context.fillRect(10, 10, 60, 60); //设置组合方式 context.globalCompositeOperation = oprtns[i]; //设置新图形(红色圆形) context.beginPath(); context.fillStyle = "red"; context.arc(60, 60, 30, 0, Math.PI * 2, false); context.fill(); }, 5000); }
语法:
绘图drawImage
图形平铺createPattern(image,type)
图形裁剪 clip()
像素处理getImageData(sx,sy,sw,sh)
参数说明:
drawImage(image,x,y)
image:Image对象var img=new Image(); img.src="url(...)";
x:绘制图像的x坐标
y:绘制图像的y坐标
drawImage(image,x,y,w,h)
image:Image对象var img=new Image(); img.src="url(...)";
x:绘制图像的x坐标
y:绘制图像的y坐标
w:绘制图像的宽度
h:绘制图像的高度
drawImage(image,sx,sy,sw,sh,dx,dy,dw,dh):选取图像的一部分矩形区域进行绘制
image:Image对象var img=new Image(); img.src="url(...)";
sx:图像上的x坐标
sy:图像上的y坐标
sw:矩形区域的宽度
sh:矩形区域的高度
dx:画在canvas的x坐标
dy:画在canvas的y坐标
dw:画出来的宽度
dh:画出来的高度
createPattern(image,type)
image:Image对象var img=new Image(); img.src="url(...)";
no-repeat:不平铺
repeat-x:横方向平铺
repeat-y:纵方向平铺
repeat:全方向平铺
clip()只绘制封闭路径区域内的图像,不绘制路径外部图像,用的时候先创建裁剪区域,再绘制图像。
getImageData(sx,sy,sw,sh)
sx:cavas的x轴坐标点
sy:canvas的y轴坐标点
sw:距离x的宽度
sh:距离y的高度
可以利用context.getImageData返回的一个像素颜色数组,顺序是所取像素范围的从左到右,从上到下,数组的元素是(所有图形,包括图片,和绘制的图形)每个像素的rgba。