第十一周第三天笔记之canvas知识
2018-10-09 本文已影响0人
果木山
1 canvas知识
1.1 canvas基础知识
- canvas绘制线,矩形,描边,填充,使用图片
- 注意点:
- canvas的宽高属性,必须设置在行内,设置系统属性,不能设置css样式,会出错;
- 获取canvas的宽高数值,在JS中可以通过
c.width
和c.height
获取; - fillRect,strokeRect均为复合形式,即先用rect创建矩形,然后再用fill或stroke填充或描边;
- lineWidth值不加单位;
- 使用drawImg()方法时,需要在图片加载完成后再执行;指的是在画布上定位被剪切的部分;
<script> var canvas=document.getElementById("canvas"); var cxt=canvas.getContext("2d"); var img=new Image();//新建一个图片对象; img.src="img/1.jpg";//获取图片地址; img.onload=function () {//指的是在画布上定位被剪切的部分; cxt.drawImage(img,328,0,216,285,50,50,216,285); } //注意:必须等到图片加载后,才能剪切; </script>
- clearRect(x,y.width,height)方法,清空画布
- 注意点:
- canvas设置渐变颜色
- LinearGradient:水平渐变颜色
- 语法:
cxt.createLinearGradinet(x0,y0,x1,y1)
; - 注意:x0,y0必须与调用时的矩形起点位置相同,x1,y1为起点位置值加上矩形宽高值
- 验证代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas知识解读</title> </head> <body> <canvas width="500" height="300" id="canvas" style="border: 2px solid red;">你的浏览器不支持canvas</canvas> <canvas width="400" height="300" id="canvas1" style="border: 2px solid red;">你的浏览器不支持canvas</canvas> <script> var canvas=document.getElementById("canvas"); var ctx=canvas.getContext("2d"); var my_gradient=ctx.createLinearGradient(250,0,450,0);//设置的初始坐标与使用的坐标要对应,长度为使用的坐标的宽度; //var my_gradient=ctx.createLinearGradient(100,0,270,0);//当使用的坐标为100,0时,渐变也要设置相应的坐标,并且170要变为270;才能使两种情况拿到的渐变颜色相同; my_gradient.addColorStop(0,"black"); my_gradient.addColorStop(0.5,"red"); my_gradient.addColorStop(1,"blue"); ctx.fillStyle=my_gradient; ctx.fillRect(250,0,200,200); //ctx.fillRect(100,0,170,200); </script> </body> </html>
- canvas知识讲解链接
canvas知识
1.2 canvas时钟实例
- 知识点:
- 画布偏移:translate(x,y);将画布原点(0,0)位置移动到(x,y)点;
- 画布旋转:rotate(angle);其中angle为弧度;
- 整个画布会被旋转;原点位置被改变;
- save():保存当前环境的状态;
- restore():返回之前保存过的路径状态和属性;
- 配合使用:
- 改变画布的位置:偏移translate(),旋转rotate();
- 在改变画布位置之前,通过save()来保存原来画布的位置;然后进行修改;
- 当需要使用修改之前的画布位置时,通过restore恢复原来的位置;
- 使用:只要修改位置,就必须在此之前保存初始位置,修改位置后,相对应的操作完事后,恢复初始位置;
- 注意:通过save()保存住第一次的位置,然后通过restore()获取第一次的位置,紧接着,若需要修改位置,必须通过save()再次保存住第一次的位置,否则,再通过restore()拿到的位置不再是第一次的位置,而是修改后的位置;
- arc()绘圆,其中半径值为圆心到圆线的宽度中心线的距离;
-
ctx.textBaseline="top/bottom/middle/alphabetic(默认)/hanging"
:设置文本的对齐方式;
- 思路:
- 绘制时钟时,通过translate()来偏移画布原点到时钟中心点;
- 绘制时针,分针,秒针时,通过rotate()来旋转画布,进行设置;需注意:画布位置会被修改,所以需要通过save()保存住原位置,通过restore()来恢复原位置;
- 重点:
- 在绘制过程中必须时刻关注画布是否被修改,即添加偏移和旋转;
- 使用的所有命令都是相对于原点来进行赋值的;
- 当使用translate(x,y)来偏移原点位置到画布的x,y坐标处,然后再使用translate(0,0)进行偏移设置,此时原点位置还在原地,没有变化,因为0,0取值是相对于此刻的原点位置进行设置的,如果想回到原来的位置,可以通过translate(-x,-y)来回到原位置;
- 在绘制画布时,要使用save()和restore()来保存原位置和恢复原位置;
- 只要修改位置,就必须在此之前保存初始位置,修改位置后,相对应的操作完事后,恢复初始位置;
- 代码:
- html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>时钟实例</title> <style> *{ margin: 0; padding: 0; } .container{ width: 400px; height:580px; margin: 0 auto; } .container h1{ width: 100%; height: 80px; line-height: 80px; text-align: center; color: green; } .container .cas{ width: 100%; height: 500px; border: 2px solid lightcoral; } </style> </head> <body> <div class="container"> <h1>时钟制作</h1> <div class="cas"><canvas width="400" height="500" id="mycanvas">你的浏览器不支持canvas</canvas></div> </div> <script src="./canvas.js"></script> </body> </html>
- canvas.js代码:
//全局变量 var x0=200; var y0=300; var r=190; var smr=5; var mlr=r-25; var numr=r-50; var year,month,date,h,m,s,curH,curM,curS; //canvas环境; var c=document.getElementById("mycanvas"); var ctx=c.getContext("2d"); toDraw(); setInterval(toDraw,1000); function toDraw() { //获取实时时间 var myDate=new Date(); year=myDate.getFullYear(); month=myDate.getMonth()+1; date=myDate.getDate(); h=myDate.getHours(); m=myDate.getMinutes(); s=myDate.getSeconds(); curH=h+m/60+s/60/60;//分和秒转化为时 curM=m+s/60; curS=s; //在绘制之前,先清理画布 ctx.clearRect(0,0,c.width,c.height); //绘制图形 draw(); } //绘制图形 function draw() { //日期绘制 ctx.font="30px Arial"; ctx.fillStyle="blue"; ctx.textAlign="center"; ctx.fillText(year+" 年 "+month+" 月 "+date+" 日",200,65); //中心线绘制 ctx.strokeStyle="lightcoral"; ctx.lineWidth="2"; ctx.moveTo(0,99); ctx.lineTo(c.width,99); ctx.stroke(); //钟表图外圈绘制 ctx.beginPath(); ctx.strokeStyle="black"; ctx.lineWidth=20; ctx.arc(x0,y0,r,0,Math.PI*2,false); ctx.stroke(); ctx.closePath(); //绘制钟表内刻度点 drawPoint(); //绘制钟表内的数字 drawNum(); //开始绘制时钟表盘; ctx.save();//改变画布位置之前先保存位置; //偏移原点位置时钟中心点; ctx.translate(c.width/2,c.height/2+50);//在绘制结束后,必须将原点偏移到原来位置; //绘制时针 drawHourhand(curH); //绘制分针 drawMinutehand(curM); //绘制秒针 drawSecondhand(curS); //绘制中心圆 drawCore(); //绘制时钟表盘结束 //恢复原来画布位置; ctx.restore(); } //绘制钟表内刻度点 function drawPoint() { var a,x1,y1; //钟表图内刻度点绘制 for(var i=0; i<60; i++){ a=i*2*Math.PI/60; x1=x0+mlr*Math.sin(a); y1=y0-mlr*Math.cos(a); if(i%5===0){ ctx.fillStyle="red"; }else{ ctx.fillStyle="black"; } ctx.beginPath(); ctx.arc(x1,y1,smr,0,Math.PI*2,false); ctx.closePath(); ctx.fill(); } } //绘制钟表内的数字 function drawNum() { var a,x2,y2; for(var i=1; i<=12; i++){ a=i*Math.PI*2/12; x2=x0+numr*Math.sin(a); y2=y0-numr*Math.cos(a); ctx.textAlign="center"; ctx.fillText(i,x2,y2+12); } } //绘制时针 function drawHourhand(hour) { ctx.save();//保存原位置; ctx.rotate(Math.PI*2/12*hour); ctx.beginPath(); ctx.strokeStyle="black"; ctx.lineWidth=16; ctx.lineCap="round"; ctx.moveTo(0,-r/2); ctx.lineTo(0,10); ctx.stroke(); ctx.restore();//恢复原位置; } //绘制分针 function drawMinutehand(min) { ctx.save();//保存原位置; ctx.rotate(Math.PI*2/60*min); ctx.beginPath(); ctx.strokeStyle="black"; ctx.lineWidth=6; ctx.lineCap="round"; ctx.moveTo(0,-r/2-35); ctx.lineTo(0,20); ctx.stroke(); ctx.restore();//恢复原位置; } //绘制秒针 function drawSecondhand(sec) { ctx.save();//保存原位置; ctx.rotate(Math.PI*2/60*sec); ctx.beginPath(); ctx.fillStyle="red"; ctx.moveTo(1.5,-r+30); ctx.lineTo(4,40); ctx.lineTo(-4,40); ctx.lineTo(-1.5,-r+30); ctx.fill(); ctx.restore();//恢复原位置; } //绘制中心圆 function drawCore() { //此处没有改变画布位置;无需保存; ctx.fillStyle="white"; ctx.beginPath(); ctx.arc(0,0,6,0,Math.PI*2,false); ctx.closePath(); ctx.fill(); }
1.3 炫酷小球实例
- 需求:实现鼠标在canvas画布上移动时,创建多个小球,向四周分散,小球半径逐渐变小,直至消失;
- 思路解析:
- 创建数组ary:储存每个小球实例,在定时器中,每隔一段时间,执行ary中的this实例;
- 创建Ball类函数:
- 构造函数:设置每个实例小球自己的属性;
- 形参x,y,r,即创建小球的原点坐标和半径;实参为移动过程中光标的位置(计算后的相对坐标值)和小球半径值;
- 设置私有属性x,y,r
- 设置私有属性dx,dy,dr,color;
- dx,dy为-5到5之间的随机数,作为原点坐标的变化值;
- dr为0.3到1.3之间的随机数,作为半径递减的数值;
- color为小球的背景色,为随机颜色;
- 将实例this插入到数组中;
- 公共方法update():计算出小球下一个位置的数据
- 私有属性x,y,r的累加累减,数据更新;
- 边界值判断:
- 判断条件:1)小球移动出画布;2)半径小于等于0;二者“或”的关系;
- 将数组中储存的该实例赋值为null;注:不能删除,赋值为null,在执行时筛选删除null;
- 公共方法render():绘制小球
- 获取圆心坐标和半径绘制小球
- 构造函数:设置每个实例小球自己的属性;
- 类函数创建完就需要调用方法
- 添加事件
- mouseover事件:当光标滑上canvas画布上,开启定时器,获取数组中的实例,然后,调用update()方法和render()方法;
- 遍历数组时,前提是数组长度大于0;
- 遍历数组后,对元素进行筛选,将null元素删除,避免i的塌陷;
- mousemove事件:光标移动过程中,创建小球实例;实参为光标的位置相对于canvas画布的相对位置坐标和小球半径;
- mouseout事件:光标移出画布后,开启另外一个定时器,判断数组的长度是否为0,若为0,则关闭第一个定时器,同时也关闭自己;
- mouseover事件:当光标滑上canvas画布上,开启定时器,获取数组中的实例,然后,调用update()方法和render()方法;
- 实例实现的整体思路:
- onmouseover事件触发:开启定时器,执行数组ary中实例对象的update()和render()方法;不断地绘制小球;
- onmousemove事件触发:鼠标移动中,不断创建新的实例,插入到数组中,待指定间隔时间后,获取数组,绘制小球;
- onmouseout事件触发:关闭开启的所有定时器;
- 绘制小球之前,必须通过clearRect()来清空画布,然后再绘制,代码放在定时器中;
- 定时器的开启和关闭
- 第一个定时器:目的执行数组中实例身上的方法,绘制小球;
- 开启:在mouseover事件中开启;
- 关闭:在mouseout事件中,通过开启另一个定时器间隔时间判断ary的长度,当长度为0时,关闭此定时器;
- 第二个定时器:当光标移出画布后,间隔时间判断ary的长度;长度为0,关闭第一个定时器;
- 开启:在mouseout事件中开启;
- 关闭:当ary长度为0后,关闭第一个定时器,同时也关闭自己的定时器;
- 第一个定时器:目的执行数组中实例身上的方法,绘制小球;
- 注意:
- 当开启定时器之前,一般要关闭其他定时器,包括自己;(此实例除外,在第二个定时开启时,无需关闭第一个定时器);
- 不能在第一个定时器内设置关闭定时器的操作,会出错,如果判断ary的长度不大于0时,关闭第一个定时器;那么在画布中停止移动后,待所有小球都消失后,定时器会停止,那么此时再移动光标,就不会开启定时器创建新的小球了;会出错;最好的方法是,在光标移出画布后,再关闭第一个定时器;
- 光标位置获取使用pageX/pageY,不能使用clientX/clientY;获取canvas元素距离浏览器左上角的距离,可以使用自己封装的offset()方法,也可以使用jQuery中的offset()方法;不能使用offsetLeft/offsetTop;
- 代码:
- html代码:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>炫酷小球实例</title> <style> *{ margin: 0; padding: 0; } .container{ width: 100%; } .container h1{ width: 100%; height: 60px; line-height: 60px; text-align: center; color: green; margin-top: 1000px; } .container .can{ width: 800px; height: 500px; border: 2px solid blue; margin: 0 auto; } .container .can canvas{ cursor: none; } </style> </head> <body> <div class="container"> <h1>炫酷小球实例</h1> <div class="can"> <canvas id="mycanvas" width="800px" height="500px">your brower is not support canvas</canvas> </div> </div> <script src="../toolLibrary/jquery.js"></script> <script src="moveball.js"></script> </body> </html>
- moveball.js代码:
(function () { //获取元素 var $con=$(".container"); var $can=$(".can"); var oC=document.getElementById("mycanvas"); var cLeft=$(oC).offset().left; var cTop=$(oC).offset().top; //全局变量 var ary=[]; var ballR=30; var timer=null; var outtimer=null; //创建canvas环境 var ctx=oC.getContext("2d"); //创建Ball类函数 class Ball{ constructor(x,y,r){ this.x=x; this.y=y; this.r=r; this.dx=Math.random()*10-5; this.dy=Math.random()*10-5; this.dr=Math.random()+0.3; this.color="rgb("+parseInt(Math.random()*255)+","+parseInt(Math.random()*255)+","+parseInt(Math.random()*255)+")"; ary.push(this); } update(){ //计算出下一个小球创建的位置; this.x+=this.dx; this.y+=this.dy; this.r-=this.dr; //边界值判断 if(this.r<=0){ this.r=0;//赋值为0; for(var i=0; i<ary.length; i++){ if(ary[i]===this){ ary[i]=null;//赋值为null; } } } return this;//链式操作 } render(){ //绘制小球 ctx.fillStyle=this.color; ctx.beginPath(); ctx.arc(this.x,this.y,this.r,0,Math.PI*2,false); ctx.closePath(); ctx.fill(); return this;//链式操作; } } //添加事件 $(oC).on("mouseover",curOver).on("mousemove",curMove).on("mouseout",curOut); //光标移动到画布上执行的函数 function curOver() { //关闭所有定时器,包括自己 clearInterval(outtimer); clearInterval(timer); //开启定时器 timer=setInterval(function () { //绘制图形前,清空屏幕; ctx.clearRect(0,0,oC.width,oC.height); //遍历数组,筛选执行 if(ary.length>0){ for(var i=0; i<ary.length; i++){ if(ary[i]!==null){ ary[i].update().render();//绘制小球 }else{ ary.splice(i,1);//删除null项; i--;//防止数组塌陷 } } } },30) } //光标在画布上移动时执行的函数 function curMove(e) { //获取光标相对于画布的坐标值 var curX=e.pageX-cLeft-oC.clientLeft; var curY=e.pageY-cTop-oC.clientTop; //移动时,创建不同的小球实例 new Ball(curX,curY,ballR); } //光标移动出画布 function curOut() { //移出画布后,关闭定时器; clearInterval(outtimer); outtimer=setInterval(function () { if(ary.length===0){ clearInterval(timer); clearInterval(outtimer); } },10); } })();