html5 canvas 学习
1.定义画布,取得画布上下文下文
<canvas id="canvas"></canvas>
js:
var cvs = document.getElementById("canvas"); var context = cvs.getContext('2d'); cvs.width = 1000; cvs.height = 600;
2.绘制一条直线
context.moveTo(100, 100); context.lineTo(300, 200); context.strokeStyle="red";//笔刷颜色,填充颜色用 .fillStyle context.lineWidth=10;//笔刷宽度 context.stroke();//填充用.fill()方法
如果要绘制多条线段 ,并且分别对线段指定样式,但最终显示都是最后指定的一个样式,这是因为canvas是状态绘制,所以对每个线段绘制可都应该用beginPath方法来告知是一个全新绘制就没有问题了
context.lineWidth = 10; context.beginPath(); context.lineTo(100, 100); context.lineTo(300, 200); context.lineTo(100, 300); context.strokeStyle = "#003399"; context.stroke(); context.beginPath(); context.moveTo(300, 100); context.lineTo(600, 200); context.lineTo(300, 300); context.strokeStyle = "#330022"; context.stroke();
以上线段没有首尾闭合,如要闭合添加context.closePath()即可。
注意,context.beignPath()与context.closePath()不是一定要成对出现的,只要线段间需要闭合时才使用closePath
3.画矩形方法
context.rect(100, 100, 200, 200); context.fill()//或context.stroke() context.fillRect(100, 100, 200, 200); context.strokeRect(100, 100, 200, 200);
对fillStyle样式可以指定颜色,还可以指定透明度:
context.lineWidth = 10; context.beginPath(); context.fillStyle = "red"; context.fillRect(100, 100, 300, 300); context.beginPath(); context.fillStyle = "rgba(0, 255, 0, 0.5)"; context.fillRect(200, 200, 300, 300);
4.直线两端的样式:context.lineCap="round",值分别可以butt(默认),round,square,注意使用round和square会比默认的多出来一小段长度,
直线与直线相交的形式:context.lineJoin,值分别:miter(默认)|bevel|round .如果绘制的角很尖时,还需要考虑另一个值: cxt.miterLimit = 20;默认值为10
5.画五角形,函数如下:
function drawStar(cxt, x, y, outerR, innerR, rot) { cxt.beginPath(); for (var i = 0; i < 5; i++) { cxt.lineTo(Math.cos((18+i*72-rot)/180*Math.PI)*outerR+x, -Math.sin((18+i*72-rot)/180*Math.PI)*outerR+y); cxt.lineTo(Math.cos((54 + i * 72 - rot) / 180 * Math.PI) * innerR + x, -Math.sin((54 + i * 72 - rot) / 180 * Math.PI) * innerR + y); } cxt.closePath(); cxt.fillStyle = "#fb3"; cxt.strokeStyle = "#fd5"; cxt.lineWidth = 3; cxt.lineJoin = "rount"; cxt.fill(); cxt.stroke(); }
在画布上添加200个五角星
for (var i = 0; i < 200; i++) { var r = Math.random() * 10 + 10; var x = Math.random() * cvs.width; var y = Math.random() * cvs.height; var a = Math.random() * 360; drawStar(context, x, y, r, r / 2.0, a); }
6.图形变换:tanslate,rotate,scale
如果对图形时行多次变换,最终结果可能会与实现想象中有差异,最好使用context.save()和context.restore(),这里context.save();和context.restore();是两个相互匹配出现的,作用是用来保存画布的状态和取出保存的状态的
context.translate(100, 0),是向右移动100个像素,如果没有save和restore,会对多次出现的平移进行叠加
context.scale(x,y),缩放使用可注意,它不仅对图形大小时行缩放,还会对边框和起点坐标也会一起缩放,所以使用scale一定要注意。
如果绘制起点设置在(0,0)坐标开始绘制,那缩放将对起点不起任何作用,这样就可能可保证起点不发生改变,此时tanslate移动起点是正确的显示器。
通过学习图形变换,把显示200星星的实例修改一下,使用图形变换的方式显示出来,不过由于使用缩放方法会使描边也发生缩放,所以这里就去掉星星的边框。
context.fillStyle = "#fb3"; context.lineJoin = "rount"; for (var i = 0; i < 200; i++) { var r = Math.random() * 10 + 10; var x = Math.random() * cvs.width; var y = Math.random() * cvs.height; var a = Math.random() * 360; drawStar(context, x, y, r, a); } function drawStar(context,x,y,r,rot) { context.save(); context.translate(x, y); context.rotate(rot / 180 * Math.PI); context.scale(r, r); starPath(context); context.fill(); context.restore(); } function starPath(context) { context.beginPath(); for (var i = 0; i < 5; i++) { context.lineTo(Math.cos((18 + i * 72) / 180 * Math.PI) , -Math.sin((18 + i * 72 ) / 180 * Math.PI) ); context.lineTo(Math.cos((54 + i * 72) / 180 * Math.PI) * 0.5, -Math.sin((54 + i * 72 ) / 180 * Math.PI) * 0.5); } context.closePath(); }
除了用translate,rotate,scale对图形变换外,还可以使用transform一次设定这几个方法的参数 :transform(a,b,c,d,e,f) ,如果参数设置成(1,0,0,1,0,0)对图形的变化不会产生任何变化。参数:a
、d是控件水平和垂直缩放的,b和c是倾斜,e和f是位移。多个 transform()一起使用会是效果级联叠加,如果想恢复到最初状态开始可以使用setTransform();
7.渐变:
线性渐变:
var linearGradient = context.createLinearGradient(0, 0, 800, 0); linearGradient.addColorStop(0.0, "red"); linearGradient.addColorStop(1.0, "blue"); context.fillStyle = linearGradient; context.fillRect(0, 0, 800, 800);
径向渐变:
var linearGradient = context.createRadialGradient(400, 400, 0,400,400,800); linearGradient.addColorStop(0.0, "red"); linearGradient.addColorStop(1.0, "blue"); context.fillStyle = linearGradient; context.fillRect(0, 0, 800, 800);
为了图像颜色鲜艳可以添加多个addColorStop
图案填充:
var img = new Image(); img.src = "imgs/head.jpg"; img.onload = function () { var pattern = context.createPattern(img,"repeat"); context.fillStyle = pattern; context.fillRect(0, 0, 800, 800); };
画布作为图案填充:
var pattern = context.createPattern(drawRect(), "repeat"); context.fillStyle = pattern; context.fillRect(0, 0, 800, 800); function drawRect() { var v = document.createElement("canvas"); v.width = 50; v.height = 50; var c = v.getContext('2d'); c.lineWidth = 5; c.strokeRect(0, 0, 45, 45); return v; }
还可以使用视频来作为图案填充
8.画园角矩形
var cvs = document.getElementById("canvas"); var context = cvs.getContext('2d'); cvs.width = 1000; cvs.height = 700; context.transform(1, 0, 0, 1, 100, 100); drawRoundRect(350, 150, 20); context.stroke(); function drawRoundRect( w, h, r) { context.beginPath(); context.arc(r, r, r, Math.PI, 3 * Math.PI / 2); context.arc(w - r, r, r, Math.PI * 3 / 2, 0); context.arc(w - r, h - r, r, 0, Math.PI / 2); context.arc(r, h - r, r, Math.PI / 2, Math.PI); context.closePath(); }
9.曲线,不细说了,可以查看贝塞尔曲线(二次,三次),网上有直接生成三次贝塞尔曲线的网页
10.字体:
context.font = "bold 40px 雅黑"; context.fillText("我是一个兵", 200, 200);
context.measureText("我是一个兵").width,可以取宽度
11.阴影
context.shadowBlur = 5; context.shadowOffsetX = 20; context.shadowOffsetY = 20; context.shadowColor = "gray";
12.生成100个小球
var cvs = document.getElementById("canvas"); var context = cvs.getContext('2d'); cvs.width = 1000; cvs.height = 700; context.globalAlpha = 0.7; for (var i = 0; i < 100; i++) { var x = Math.random() * cvs.width; var y = Math.random() * cvs.height; var r = Math.random() * 50; var cr = Math.floor(Math.random() * 255); var g = Math.floor(Math.random() * 255); var b = Math.floor(Math.random() * 255); context.fillStyle = "rgb(" + cr + "," + g + "," + b + ")"; context.beginPath(); context.arc(x, y, r, 0, 2 * Math.PI); context.fill(); }
context.globalAlpha 是透明度,context.globalCompositeOperation 属性设置或返回如何将一个源(新的)图像绘制到目标(已有)的图像上
值 | 描述 |
---|---|
source-over | 默认。在目标图像上显示源图像。 |
source-atop | 在目标图像顶部显示源图像。源图像位于目标图像之外的部分是不可见的。 |
source-in | 在目标图像中显示源图像。只有目标图像内的源图像部分会显示,目标图像是透明的。 |
source-out | 在目标图像之外显示源图像。只会显示目标图像之外源图像部分,目标图像是透明的。 |
destination-over | 在源图像上方显示目标图像。 |
destination-atop | 在源图像顶部显示目标图像。源图像之外的目标图像部分不会被显示。 |
destination-in | 在源图像中显示目标图像。只有源图像内的目标图像部分会被显示,源图像是透明的。 |
destination-out | 在源图像外显示目标图像。只有源图像外的目标图像部分会被显示,源图像是透明的。 |
lighter | 显示源图像 + 目标图像。 |
copy | 显示源图像。忽略目标图像。 |
source-over | 使用异或操作对源图像与目标图像进行组合。 |
11.剪辑区域
context.clip();
12.非零环绕原则,
context.beginPath(); context.arc(400, 400, 300, 0, 2 * Math.PI,true); context.arc(400, 400, 150, 0, 2 * Math.PI); context.closePath(); context.fillStyle = "red"; context.shadowColor = "gray"; context.shadowOffsetX = 10; context.shadowOffsetY = 10; context.shadowBlur = 5; context.fill();
非零环绕规则:对于路径中指定范围区域,从该区域内部画一条足够长的线段,使此线段的完全落在路径范围之外。
非零环绕规则计数器:
然后,将计数器初始化为0,每当这个线段与路径上的直线或曲线相交时,就改变计数器的值,如果是与路径顺时针相交时,那么计数器就加1, 如果是与路径逆时针相交时,那么计数器就减1.
如果计数器始终不为0,那么此区域就在路径范围里面,在调用fill()方法时,浏览器就会对其进行填充。如果最终值是0,那么此区域就不在路径范围内,浏览器就不会对其进行填充。
13.事件:
var ball = []; var cvs = document.getElementById("canvas");
var context = cvs.getContext('2d');
cvs.width = 1000;
cvs.height = 700; window.onload=function() { for (var i = 0; i < 10; i++) { ball[i] = { x: Math.random() * cvs.width, y:Math.random()*cvs.height, r:Math.random()*100 }; } draw(); cvs.addEventListener("mouseup", function (e) { e.stopPropagation(); var x =e.clientX- cvs.getBoundingClientRect().left; var y = e.clientY - cvs.getBoundingClientRect().top; for (var i = 0; i < 10; i++) { context.beginPath(); context.arc(ball[i].x, ball[i].y, ball[i].r, 0, 2 * Math.PI); if(context.isPointInPath(x, y)){ context.fillStyle = "red"; context.fill(); } } }); } function draw() { for (var i = 0; i < 10; i++) { context.beginPath(); context.arc(ball[i].x, ball[i].y, ball[i].r, 0, 2 * Math.PI); context.fillStyle = "gray"; context.fill(); } }
14.如果想偷懒,当然可以找一些现成的图形库,直接应用就可以,比如RGraph是一个使用HTML5 Canvas标签实现的图表制作Library。利用该Library生成的Chart具有可交互性,当鼠标点击或移过时会显示相应的信息,可以动态加载Chart或对特殊点进行缩放。当前支持的图表类型包括:
- bar、pie、donut、gantt、radar、funnel、bi-polar charts
- line and scatter graphs
- LED display
- meter
- odometer
- progress bar
http://www.rgraph.net/download
15.最后来一个动画:
var bgcolor = "#000"; var balls = []; var canvas = document.getElementById("canvas"); var context = canvas.getContext('2d'); canvas.width = 1000, canvas.height = 700; //添加小球 getBall(); setInterval(function () { drawBall(); }, 50); function drawBall() { context.globalCompositeOperation = "lighter"; context.clearRect(0, 0, canvas.width, canvas.height); //画背景色 context.beginPath(); context.fillStyle = bgcolor; context.fillRect(0, 0, canvas.width, canvas.height); //画球 for (var i = 0; i < balls.length; i++) { var ball = balls[i]; if ((ball.x - ball.r) < 0 || (ball.x + ball.r) >= canvas.width) { ball.vx = -ball.vx; } if ((ball.y - ball.r) <= 0 || (ball.y + ball.r) >= canvas.height) { ball.vy = -ball.vy; } ball.x += ball.vx; ball.y += ball.vy; context.beginPath(); context.arc(ball.x, ball.y, ball.r, 0, 2 * Math.PI); context.closePath(); context.fillStyle = ball.color; context.fill(); } } function getBall() { for (var i = 0; i < 100; i++) { var r =Math.random() * 40 + 10; var x =Math.random() * (canvas.width-2*r)+r; var y = Math.random() * (canvas.height - 2 * r)+ r; var vx = (Math.random() * 5 + 2) * Math.pow(-1, Math.floor(Math.random() * 100)); var vy = (Math.random() * 5 + 2) * Math.pow(-1, Math.floor(Math.random() * 100)); var R = Math.floor(Math.random() * 255); var G = Math.floor(Math.random() * 255); var B = Math.floor(Math.random() * 255); var color = "rgba(" + R + "," + G + "," + B + ","+Math.random()+")"; balls[i] = { x: x, y: y, r: r, vx: vx, vy: vy, color: color }; } }