圆弧绘制
弧度概念
这就和数学中的概念知识是一样的。
弧度 : 是一种长度的描述单位
一个弧度怎么去描述 一个圆有多少个弧度 2 * π
一弧度有多长 一个弧度一个半径的长度
名词:
角度:一个圆是360度
半径:已一个点为中心多长为放射半径
周长:2 * π * r
最终的结论:一个角度等于多少弧度 π/180
用三角函数体验曲线的绘制
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*1.体验曲线的绘制*/ /*2.线是由点构成的*/ /*3.曲线可以由数学公式得来*/ /*公式:y = x/2 */ /*公式:y = (x + 2) ^2 */ /*公式:y = sin(x) */ for(var i = 1 ; i < 600 ; i ++){ var x = i; //var y = x/2; //var y = Math.pow(x/10-30,2); var y = 50*Math.sin(x/10) + 100; ctx.lineTo(x,y); } ctx.stroke(); </script> </body> </html>
arc() 方法画圆弧
arc(x,y,r,startAngle,endAngle,anticlockwise)
参数:
- x 圆心横坐标
- y 圆心纵坐标
- r 半径
- startAngle 开始角度
- endAngle 结束角度
- anticlockwise 是否逆时针方向绘制(默认false表示顺时针;true表示逆时针)
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*绘制圆弧*/ /*确定圆心 坐标 x y*/ /*确定圆半径 r */ /*确定起始绘制的位置和结束绘制的位置 确定弧的长度和位置 startAngle endAngle 弧度*/ /*取得绘制的方向 direction 默认是顺时针 false 逆时针 true */ /*在中心位置画一个半径150px的圆弧左下角*/ var w = ctx.canvas.width; var h = ctx.canvas.height; ctx.arc(w/2,h/2,150,Math.PI/2,Math.PI,true); ctx.stroke(); </script> </body> </html>
圆实例练习
绘制六种颜色等分的圆
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); var w = ctx.canvas.width; var h = ctx.canvas.height; /*分成几等分*/ var num = 360; /*一份多少弧度*/ var angle = Math.PI * 2 / num; /*原点坐标*/ var x0 = w / 2; var y0 = h / 2; /*获取随机颜色*/ var 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 + ')'; } /*上一次绘制的结束弧度等于当前次的起始弧度*/ //var startAngle = 0; for (var i = 0; i < num; i++) { var startAngle = i * angle; var endAngle = (i + 1) * angle; ctx.beginPath(); ctx.moveTo(x0, y0); ctx.arc(x0, y0, 150, startAngle, endAngle); /*随机颜色*/ ctx.fillStyle = getRandomColor(); ctx.fill(); } </script> </body> </html>
绘制扇形
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*在中心位置画一个半径150px的圆弧右上角 扇形 边 填充 */ var w = ctx.canvas.width; var h = ctx.canvas.height; /*把起点放到圆心位置*/ ctx.moveTo(w/2,h/2); ctx.arc(w/2,h/2,150,0,-Math.PI/2,true); /*闭合路径*/ //ctx.closePath(); ctx.fill(); </script> </body> </html>
绘制数据饼图
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*1.根据37期的年龄分布绘制饼图*/ /*2.准备统计的数据*/ /*15-20岁 6个*/ /*20-25岁 30个*/ /*25-30岁 10个*/ /*30-35岁 8个*/ var data = [6, 30, 10, 8]; /*3.在饼图表示出来*/ /*4.需要把数据转出弧度*/ var angleList = []; var total = 0; data.forEach(function (item, i) { total += item; }); console.log(total); /*第二是转换成弧度的时候就可以去绘制扇形 减少一次遍历*/ data.forEach(function (item, i) { var angle = Math.PI * 2 * (item/total); angleList.push(angle); }); console.log(angleList); /*5.根据弧度绘制扇形*/ var w = ctx.canvas.width; var h = ctx.canvas.height; var x0 = w/2; var y0 = h/2; /*获取随机颜色*/ var 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 + ')'; } var startAngle = 0; angleList.forEach(function (item,i) { /*上一次绘制的结束弧度等于当前次的起始弧度*/ var endAngle = startAngle + item; ctx.beginPath(); ctx.moveTo(x0,y0); ctx.arc(x0,y0,150,startAngle,endAngle); ctx.fillStyle = getRandomColor(); ctx.fill(); /*记录当前的结束位置作为下一次的起始位置*/ startAngle = endAngle; }); </script> </body> </html>
绘制文本
- ctx.font = ‘微软雅黑’ 设置字体
- strokeText()
- fillText(text,x,y,maxWidth)
text 要绘制的文本
x,y 文本绘制的坐标(文本左下角)
maxWidth 设置文本最大宽度,可选参数
- ctx.textAlign文本水平对齐方式,相对绘制坐标来说的
left
center
right
start 默认
end
- ctx.direction属性css(rtl ltr) start和end于此相关
如果是ltr,start和left表现一致
如果是rtl,start和right表现一致
- ctx.textBaseline 设置基线(垂直对齐方式 )
top 文本的基线处于文本的正上方,并且有一段距离
middle 文本的基线处于文本的正中间
bottom 文本的基线处于文本的证下方,并且有一段距离
hanging 文本的基线处于文本的正上方,并且和文本粘合
alphabetic 默认值,基线处于文本的下方,并且穿过文字
ideographic 和bottom相似,但是不一样
- measureText() 获取文本宽度obj.width
案例:画布中心绘制文字
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; display: block; margin: 100px auto; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*1.在画布的中心绘制一段文字*/ /*2.申明一段文字*/ var str = '您吃饭中饭了吗'; /*3.确定画布的中心*/ var w = ctx.canvas.width; var h = ctx.canvas.height; /*4.画一个十字架在画布的中心*/ ctx.beginPath(); ctx.moveTo(0, h / 2 - 0.5); ctx.lineTo(w, h / 2 - 0.5); ctx.moveTo(w / 2 - 0.5, 0); ctx.lineTo(w / 2 - 0.5, h); ctx.strokeStyle = '#eee'; ctx.stroke(); /*5.绘制文本*/ ctx.beginPath(); ctx.strokeStyle = '#000'; var x0 = w/2; var y0 = h/2; /*注意:起点位置在文字的左下角*/ /*有文本的属性 尺寸 字体 左右对齐方式 垂直对齐的方式*/ ctx.font = '40px Microsoft YaHei'; /*左右对齐方式 (center left right start end) 基准起始坐标*/ ctx.textAlign = 'center'; /*垂直对齐的方式 基线 baseline(top,bottom,middle) 基准起始坐标*/ ctx.textBaseline = 'middle'; //ctx.direction = 'rtl'; //ctx.strokeText(str,x0,y0); ctx.fillText(str,x0,y0); /*6.画一个下划线和文字一样长*/ ctx.beginPath(); /*获取文本的宽度*/ console.log(ctx.measureText(str)); var width = ctx.measureText(str).width; ctx.moveTo(x0-width/2,y0 + 20); ctx.lineTo(x0+width/2,y0 + 20); ctx.stroke(); </script> </body> </html>
圆与文本综合案例:数据饼状图
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; display: block; margin: 100px auto; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> /*var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d');*/ /*1.绘制饼状态图*/ /*1.1 根据数据绘制一个饼图*/ /*1.2 绘制标题 从扇形的弧中心伸出一条线在画一条横线在横线的上面写上文字标题*/ /*1.3 在画布的左上角 绘制说明 一个和扇形一样颜色的矩形 旁边就是文字说明*/ var PieChart = function (ctx) { /*绘制工具*/ this.ctx = ctx || document.querySelector('canvas').getContext('2d'); /*绘制饼图的中心*/ this.w = this.ctx.canvas.width; this.h = this.ctx.canvas.height; /*圆心*/ this.x0 = this.w / 2 + 60; this.y0 = this.h / 2; /*半径*/ this.radius = 150; /*伸出去的线的长度*/ this.outLine = 20; /*说明的矩形大小*/ this.rectW = 30; this.rectH = 16; this.space = 20; } PieChart.prototype.init = function (data) { /*1.准备数据*/ this.drawPie(data); }; PieChart.prototype.drawPie = function (data) { var that = this; /*1.转化弧度*/ var angleList = this.transformAngle(data); /*2.绘制饼图*/ var startAngle = 0; angleList.forEach(function (item, i) { /*当前的结束弧度要等于下一次的起始弧度*/ var endAngle = startAngle + item.angle; that.ctx.beginPath(); that.ctx.moveTo(that.x0, that.y0); that.ctx.arc(that.x0, that.y0, that.radius, startAngle, endAngle); var color = that.ctx.fillStyle = that.getRandomColor(); that.ctx.fill(); /*下一次要使用当前的这一次的结束角度*/ /*绘制标题*/ that.drawTitle(startAngle, item.angle, color , item.title); /*绘制说明*/ that.drawDesc(i,item.title); startAngle = endAngle; }); }; PieChart.prototype.drawTitle = function (startAngle, angle ,color , title) { /*1.确定伸出去的线 通过圆心点 通过伸出去的点 确定这个线*/ /*2.确定伸出去的点 需要确定伸出去的线的长度*/ /*3.固定伸出去的线的长度*/ /*4.计算这个点的坐标*/ /*5.需要根据角度和斜边的长度*/ /*5.1 使用弧度 当前扇形的起始弧度 + 对应的弧度的一半 */ /*5.2 半径+伸出去的长度 */ /*5.3 outX = x0 + cos(angle) * ( r + outLine)*/ /*5.3 outY = y0 + sin(angle) * ( r + outLine)*/ /*斜边*/ var edge = this.radius + this.outLine; /*x轴方向的直角边*/ var edgeX = Math.cos(startAngle + angle / 2) * edge; /*y轴方向的直角边*/ var edgeY = Math.sin(startAngle + angle / 2) * edge; /*计算出去的点坐标*/ var outX = this.x0 + edgeX; var outY = this.y0 + edgeY; this.ctx.beginPath(); this.ctx.moveTo(this.x0, this.y0); this.ctx.lineTo(outX, outY); this.ctx.strokeStyle = color; /*画文字和下划线*/ /*线的方向怎么判断 伸出去的点在X0的左边 线的方向就是左边*/ /*线的方向怎么判断 伸出去的点在X0的右边 线的方向就是右边*/ /*结束的点坐标 和文字大小*/ this.ctx.font = '14px Microsoft YaHei'; var textWidth = this.ctx.measureText(title).width ; if(outX > this.x0){ /*右*/ this.ctx.lineTo(outX + textWidth,outY); this.ctx.textAlign = 'left'; }else{ /*左*/ this.ctx.lineTo(outX - textWidth,outY); this.ctx.textAlign = 'right'; } this.ctx.stroke(); this.ctx.textBaseline = 'bottom'; this.ctx.fillText(title,outX,outY); }; PieChart.prototype.drawDesc = function (index,title) { /*绘制说明*/ /*矩形的大小*/ /*距离上和左边的间距*/ /*矩形之间的间距*/ this.ctx.fillRect(this.space,this.space + index * (this.rectH + 10),this.rectW,this.rectH); /*绘制文字*/ this.ctx.beginPath(); this.ctx.textAlign = 'left'; this.ctx.textBaseline = 'top'; this.ctx.font = '12px Microsoft YaHei'; this.ctx.fillText(title,this.space + this.rectW + 10 , this.space + index * (this.rectH + 10)); }; PieChart.prototype.transformAngle = function (data) { /*返回的数据内容包含弧度的*/ var total = 0; data.forEach(function (item, i) { total += item.num; }); /*计算弧度 并且追加到当前的对象内容*/ data.forEach(function (item, i) { var angle = item.num / total * Math.PI * 2; item.angle = angle; }); return data; }; PieChart.prototype.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 + ')'; }; var data = [ { title: '15-20岁', num: 6 }, { title: '20-25岁', num: 30 }, { title: '25-30岁', num: 10 }, { title: '30以上', num: 8 } ]; var pieChart = new PieChart(); pieChart.init(data); </script> </body> </html>
做动画
绘制图片
drawImage()
参数:
- 三个参数drawImage(img,x,y)
img 图片对象、canvas对象、video对象
x,y 图片绘制的左上角
- 五个参数drawImage(img,x,y,w,h)
img 图片对象、canvas对象、video对象
x,y 图片绘制的左上角
w,h 图片绘制尺寸设置(图片缩放,不是截取)
- 九个参数drawImage(img,x,y,w,h,x1,y1,w1,h1)
img 图片对象、canvas对象、video对象
x,y,w,h 图片中的一个矩形区域
x1,y1,w1,h1 画布中的一个矩形区域
代码示例:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <!--<img src="image/01.jpg" alt="">--> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); /*1.加载图片到内存即可*/ /*var img = document.createElement('img'); img.src = 'image/01.jpg';*/ /*创建对象*/ var image = new Image(); /*绑定加载完成事件*/ image.onload = function () { /*实现图片绘制*/ console.log(image); /*绘制图片的三种方式*/ /*3参数*/ /*图片对象*/ /*绘制在画布上的坐标 x y*/ //ctx.drawImage(image,100,100); /*5个参数*/ /*图片对象*/ /*绘制在画布上的坐标 x y*/ /*是图片的大小 不是裁剪 是缩放*/ //ctx.drawImage(image,100,100,100,100); /*9个参数*/ /*图片对象*/ /*图片上定位的坐标 x y */ /*在图片上截取多大的区域 w h*/ /*绘制在画布上的坐标 x y*/ /*是图片的大小 不是裁剪 是缩放*/ ctx.drawImage(image,400,400,400,400,200,200,100,100); }; /*设置图片路径*/ image.src = 'image/02.jpg'; </script> </body> </html>
序列帧动画
方向键控制行走的小人
动图中小人的移动是靠键盘的方向键
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var Person = function (ctx) { /*绘制工具*/ this.ctx = ctx || document.querySelector('canvas').getContext('2d'); /*图片路径*/ this.src = 'image/03.png'; /*画布的大小*/ this.canvasWidth = this.ctx.canvas.width; this.canvasHeight = this.ctx.canvas.height; /*行走相关参数*/ this.stepSzie = 20; /* 0 前 1 左 2 右 3 后 和图片的行数包含的图片对应上*/ this.direction = 0; /*x轴方向的偏移步数*/ this.stepX = 0; /*y轴方向的偏移步数*/ this.stepY = 0; /*初始化方法*/ this.init(); }; Person.prototype.init = function () { var that = this; /*1.加载图片*/ this.loadImage(function (image) { /*图片的大小*/ that.imageWidth = image.width; that.imageHeight = image.height; /*人物的大小*/ that.personWidth = that.imageWidth / 4; that.personHeight = that.imageHeight / 4; /*绘制图片的起点*/ that.x0 = that.canvasWidth / 2 - that.personWidth / 2; that.y0 = that.canvasHeight / 2 - that.personHeight / 2; /*2.默认绘制在中心位置正面朝外*/ that.ctx.drawImage(image, 0,0, that.personWidth,that.personHeight, that.x0,that.y0, that.personWidth,that.personHeight); /*3.能通过方向键去控制人物行走*/ that.index = 0; document.onkeydown = function (e) { if(e.keyCode == 40){ that.direction = 0; that.stepY ++; that.drawImage(image); /*前*/ }else if(e.keyCode == 37){ that.direction = 1; that.stepX --; that.drawImage(image); /*左*/ }else if(e.keyCode == 39){ that.direction = 2; that.stepX ++; that.drawImage(image); /*右*/ }else if(e.keyCode == 38){ that.direction = 3; that.stepY --; that.drawImage(image); /*后*/ } } }); } /*加载图片*/ Person.prototype.loadImage = function (callback) { var image = new Image(); image.onload = function () { callback && callback(image); }; image.src = this.src; }; /*绘制图片*/ Person.prototype.drawImage = function (image) { this.index ++; /*清除画布*/ this.ctx.clearRect(0,0,this.canvasWidth,this.canvasHeight); /*绘图*/ /*在精灵图上的定位 x 索引*/ /*在精灵图上的定位 y 方向*/ this.ctx.drawImage(image, this.index * this.personWidth,this.direction * this.personHeight, this.personWidth,this.personHeight, this.x0 + this.stepX * this.stepSzie ,this.y0 + this.stepY * this.stepSzie, this.personWidth,this.personHeight); /*如果索引超出了 变成0*/ if(this.index >= 3){ this.index = 0; } }; new Person(); </script> </body> </html>
坐标变换
- 平移 移动画布的原点
- translate(x,y) 参数表示移动目标点的坐标
- 缩放
- scale(x,y) 参数表示宽高的缩放比例
- 旋转
- rotate(angle) 参数表示旋转角度
案例:旋转的方块
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <style> canvas { border: 1px solid #ccc; } </style> </head> <body> <canvas width="600" height="400"></canvas> <script> var myCanvas = document.querySelector('canvas'); var ctx = myCanvas.getContext('2d'); //ctx.translate(100,100); //ctx.scale(0.5,1); //ctx.rotate(Math.PI/6); var startAngle = 0; ctx.translate(150,150); setInterval(function () { startAngle += Math.PI/180; ctx.rotate(startAngle); ctx.strokeRect(-50,-50,100,100); },500); </script> </body> </html>