第十一周第三天笔记之canvas知识

1 canvas知识

1.1 canvas基础知识

  • canvas绘制线,矩形,描边,填充,使用图片
    • 注意点:
      • canvas的宽高属性,必须设置在行内,设置系统属性,不能设置css样式,会出错;
      • 获取canvas的宽高数值,在JS中可以通过c.widthc.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,则关闭第一个定时器,同时也关闭自己;
  • 实例实现的整体思路:
    • 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);
         }
     })();
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容