第十一周第三天筆記之canvas知識

1 canvas知識

1.1 canvas基礎(chǔ)知識

  • canvas繪制線,矩形,描邊,填充,使用圖片
    • 注意點(diǎn):
      • canvas的寬高屬性,必須設(shè)置在行內(nèi),設(shè)置系統(tǒng)屬性,不能設(shè)置css樣式,會出錯;
      • 獲取canvas的寬高數(shù)值,在JS中可以通過c.widthc.height獲取;
      • fillRect,strokeRect均為復(fù)合形式,即先用rect創(chuàng)建矩形,然后再用fill或stroke填充或描邊;
      • lineWidth值不加單位;
      • 使用drawImg()方法時(shí),需要在圖片加載完成后再執(zhí)行;指的是在畫布上定位被剪切的部分;
       <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設(shè)置漸變顏色
    • LinearGradient:水平漸變顏色
    • 語法:cxt.createLinearGradinet(x0,y0,x1,y1);
    • 注意:x0,y0必須與調(diào)用時(shí)的矩形起點(diǎn)位置相同,x1,y1為起點(diǎn)位置值加上矩形寬高值
    • 驗(yàn)證代碼:
     <!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);//設(shè)置的初始坐標(biāo)與使用的坐標(biāo)要對應(yīng),長度為使用的坐標(biāo)的寬度;
         //var my_gradient=ctx.createLinearGradient(100,0,270,0);//當(dāng)使用的坐標(biāo)為100,0時(shí),漸變也要設(shè)置相應(yīng)的坐標(biāo),并且170要變?yōu)?70;才能使兩種情況拿到的漸變顏色相同;
         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時(shí)鐘實(shí)例

  • 知識點(diǎn):
    • 畫布偏移:translate(x,y);將畫布原點(diǎn)(0,0)位置移動到(x,y)點(diǎn);
    • 畫布旋轉(zhuǎn):rotate(angle);其中angle為弧度;
      • 整個畫布會被旋轉(zhuǎn);原點(diǎn)位置被改變;
    • save():保存當(dāng)前環(huán)境的狀態(tài);
    • restore():返回之前保存過的路徑狀態(tài)和屬性;
    • 配合使用:
      • 改變畫布的位置:偏移translate(),旋轉(zhuǎn)rotate();
      • 在改變畫布位置之前,通過save()來保存原來畫布的位置;然后進(jìn)行修改;
      • 當(dāng)需要使用修改之前的畫布位置時(shí),通過restore恢復(fù)原來的位置;
      • 使用:只要修改位置,就必須在此之前保存初始位置,修改位置后,相對應(yīng)的操作完事后,恢復(fù)初始位置;
      • 注意:通過save()保存住第一次的位置,然后通過restore()獲取第一次的位置,緊接著,若需要修改位置,必須通過save()再次保存住第一次的位置,否則,再通過restore()拿到的位置不再是第一次的位置,而是修改后的位置;
    • arc()繪圓,其中半徑值為圓心到圓線的寬度中心線的距離;
    • ctx.textBaseline="top/bottom/middle/alphabetic(默認(rèn))/hanging":設(shè)置文本的對齊方式;
  • 思路:
    • 繪制時(shí)鐘時(shí),通過translate()來偏移畫布原點(diǎn)到時(shí)鐘中心點(diǎn);
    • 繪制時(shí)針,分針,秒針時(shí),通過rotate()來旋轉(zhuǎn)畫布,進(jìn)行設(shè)置;需注意:畫布位置會被修改,所以需要通過save()保存住原位置,通過restore()來恢復(fù)原位置;
  • 重點(diǎn):
    • 在繪制過程中必須時(shí)刻關(guān)注畫布是否被修改,即添加偏移和旋轉(zhuǎn);
    • 使用的所有命令都是相對于原點(diǎn)來進(jìn)行賦值的;
    • 當(dāng)使用translate(x,y)來偏移原點(diǎn)位置到畫布的x,y坐標(biāo)處,然后再使用translate(0,0)進(jìn)行偏移設(shè)置,此時(shí)原點(diǎn)位置還在原地,沒有變化,因?yàn)?,0取值是相對于此刻的原點(diǎn)位置進(jìn)行設(shè)置的,如果想回到原來的位置,可以通過translate(-x,-y)來回到原位置;
    • 在繪制畫布時(shí),要使用save()和restore()來保存原位置和恢復(fù)原位置;
    • 只要修改位置,就必須在此之前保存初始位置,修改位置后,相對應(yīng)的操作完事后,恢復(fù)初始位置;
  • 代碼:
    • html代碼:
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>時(shí)鐘實(shí)例</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>時(shí)鐘制作</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環(huán)境;
     var c=document.getElementById("mycanvas");
     var ctx=c.getContext("2d");
     
     
     toDraw();
     setInterval(toDraw,1000);
     function toDraw() {
         //獲取實(shí)時(shí)時(shí)間
         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;//分和秒轉(zhuǎn)化為時(shí)
         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();
         //繪制鐘表內(nèi)刻度點(diǎn)
         drawPoint();
         //繪制鐘表內(nèi)的數(shù)字
         drawNum();
     
         //開始繪制時(shí)鐘表盤;
         ctx.save();//改變畫布位置之前先保存位置;
         //偏移原點(diǎn)位置時(shí)鐘中心點(diǎn);
         ctx.translate(c.width/2,c.height/2+50);//在繪制結(jié)束后,必須將原點(diǎn)偏移到原來位置;
         //繪制時(shí)針
         drawHourhand(curH);
         //繪制分針
         drawMinutehand(curM);
         //繪制秒針
         drawSecondhand(curS);
         //繪制中心圓
         drawCore();
     
         //繪制時(shí)鐘表盤結(jié)束
         //恢復(fù)原來畫布位置;
         ctx.restore();
     }
     //繪制鐘表內(nèi)刻度點(diǎn)
     function drawPoint() {
         var a,x1,y1;
         //鐘表圖內(nèi)刻度點(diǎn)繪制
         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();
         }
     }
     //繪制鐘表內(nèi)的數(shù)字
     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);
         }
     }
     //繪制時(shí)針
     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();//恢復(fù)原位置;
     }
     //繪制分針
     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();//恢復(fù)原位置;
     }
     //繪制秒針
     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();//恢復(fù)原位置;
     }
     //繪制中心圓
     function drawCore() {
         //此處沒有改變畫布位置;無需保存;
         ctx.fillStyle="white";
         ctx.beginPath();
         ctx.arc(0,0,6,0,Math.PI*2,false);
         ctx.closePath();
         ctx.fill();
     }
    

1.3 炫酷小球?qū)嵗?/h4>
  • 需求:實(shí)現(xiàn)鼠標(biāo)在canvas畫布上移動時(shí),創(chuàng)建多個小球,向四周分散,小球半徑逐漸變小,直至消失;
  • 思路解析:
    • 創(chuàng)建數(shù)組ary:儲存每個小球?qū)嵗诙〞r(shí)器中,每隔一段時(shí)間,執(zhí)行ary中的this實(shí)例;
    • 創(chuàng)建Ball類函數(shù):
      • 構(gòu)造函數(shù):設(shè)置每個實(shí)例小球自己的屬性;
        • 形參x,y,r,即創(chuàng)建小球的原點(diǎn)坐標(biāo)和半徑;實(shí)參為移動過程中光標(biāo)的位置(計(jì)算后的相對坐標(biāo)值)和小球半徑值;
        • 設(shè)置私有屬性x,y,r
        • 設(shè)置私有屬性dx,dy,dr,color;
          • dx,dy為-5到5之間的隨機(jī)數(shù),作為原點(diǎn)坐標(biāo)的變化值;
          • dr為0.3到1.3之間的隨機(jī)數(shù),作為半徑遞減的數(shù)值;
          • color為小球的背景色,為隨機(jī)顏色;
        • 將實(shí)例this插入到數(shù)組中;
      • 公共方法update():計(jì)算出小球下一個位置的數(shù)據(jù)
        • 私有屬性x,y,r的累加累減,數(shù)據(jù)更新;
        • 邊界值判斷:
          • 判斷條件:1)小球移動出畫布;2)半徑小于等于0;二者“或”的關(guān)系;
          • 將數(shù)組中儲存的該實(shí)例賦值為null;注:不能刪除,賦值為null,在執(zhí)行時(shí)篩選刪除null;
      • 公共方法render():繪制小球
        • 獲取圓心坐標(biāo)和半徑繪制小球
    • 類函數(shù)創(chuàng)建完就需要調(diào)用方法
    • 添加事件
      • mouseover事件:當(dāng)光標(biāo)滑上canvas畫布上,開啟定時(shí)器,獲取數(shù)組中的實(shí)例,然后,調(diào)用update()方法和render()方法;
        • 遍歷數(shù)組時(shí),前提是數(shù)組長度大于0;
        • 遍歷數(shù)組后,對元素進(jìn)行篩選,將null元素刪除,避免i的塌陷;
      • mousemove事件:光標(biāo)移動過程中,創(chuàng)建小球?qū)嵗?;?shí)參為光標(biāo)的位置相對于canvas畫布的相對位置坐標(biāo)和小球半徑;
      • mouseout事件:光標(biāo)移出畫布后,開啟另外一個定時(shí)器,判斷數(shù)組的長度是否為0,若為0,則關(guān)閉第一個定時(shí)器,同時(shí)也關(guān)閉自己;
  • 實(shí)例實(shí)現(xiàn)的整體思路:
    • onmouseover事件觸發(fā):開啟定時(shí)器,執(zhí)行數(shù)組ary中實(shí)例對象的update()和render()方法;不斷地繪制小球;
    • onmousemove事件觸發(fā):鼠標(biāo)移動中,不斷創(chuàng)建新的實(shí)例,插入到數(shù)組中,待指定間隔時(shí)間后,獲取數(shù)組,繪制小球;
    • onmouseout事件觸發(fā):關(guān)閉開啟的所有定時(shí)器;
    • 繪制小球之前,必須通過clearRect()來清空畫布,然后再繪制,代碼放在定時(shí)器中;
    • 定時(shí)器的開啟和關(guān)閉
      • 第一個定時(shí)器:目的執(zhí)行數(shù)組中實(shí)例身上的方法,繪制小球;
        • 開啟:在mouseover事件中開啟;
        • 關(guān)閉:在mouseout事件中,通過開啟另一個定時(shí)器間隔時(shí)間判斷ary的長度,當(dāng)長度為0時(shí),關(guān)閉此定時(shí)器;
      • 第二個定時(shí)器:當(dāng)光標(biāo)移出畫布后,間隔時(shí)間判斷ary的長度;長度為0,關(guān)閉第一個定時(shí)器;
        • 開啟:在mouseout事件中開啟;
        • 關(guān)閉:當(dāng)ary長度為0后,關(guān)閉第一個定時(shí)器,同時(shí)也關(guān)閉自己的定時(shí)器;
  • 注意:
    • 當(dāng)開啟定時(shí)器之前,一般要關(guān)閉其他定時(shí)器,包括自己;(此實(shí)例除外,在第二個定時(shí)開啟時(shí),無需關(guān)閉第一個定時(shí)器);
    • 不能在第一個定時(shí)器內(nèi)設(shè)置關(guān)閉定時(shí)器的操作,會出錯,如果判斷ary的長度不大于0時(shí),關(guān)閉第一個定時(shí)器;那么在畫布中停止移動后,待所有小球都消失后,定時(shí)器會停止,那么此時(shí)再移動光標(biāo),就不會開啟定時(shí)器創(chuàng)建新的小球了;會出錯;最好的方法是,在光標(biāo)移出畫布后,再關(guān)閉第一個定時(shí)器;
    • 光標(biāo)位置獲取使用pageX/pageY,不能使用clientX/clientY;獲取canvas元素距離瀏覽器左上角的距離,可以使用自己封裝的offset()方法,也可以使用jQuery中的offset()方法;不能使用offsetLeft/offsetTop;
  • 代碼:
    • html代碼:
     <!DOCTYPE html>
     <html lang="en">
     <head>
         <meta charset="UTF-8">
         <title>炫酷小球?qū)嵗?lt;/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>炫酷小球?qū)嵗?lt;/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;
     
         //創(chuàng)建canvas環(huán)境
         var ctx=oC.getContext("2d");
     
         //創(chuàng)建Ball類函數(shù)
         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(){
                 //計(jì)算出下一個小球創(chuàng)建的位置;
                 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;//鏈?zhǔn)讲僮?         }
             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;//鏈?zhǔn)讲僮鳎?         }
         }
         //添加事件
         $(oC).on("mouseover",curOver).on("mousemove",curMove).on("mouseout",curOut);
         //光標(biāo)移動到畫布上執(zhí)行的函數(shù)
         function curOver() {
             //關(guān)閉所有定時(shí)器,包括自己
             clearInterval(outtimer);
             clearInterval(timer);
             //開啟定時(shí)器
             timer=setInterval(function () {
                 //繪制圖形前,清空屏幕;
                 ctx.clearRect(0,0,oC.width,oC.height);
                 //遍歷數(shù)組,篩選執(zhí)行
                 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項(xiàng);
                             i--;//防止數(shù)組塌陷
                         }
                     }
                 }
             },30)
         }
         //光標(biāo)在畫布上移動時(shí)執(zhí)行的函數(shù)
         function curMove(e) {
             //獲取光標(biāo)相對于畫布的坐標(biāo)值
             var curX=e.pageX-cLeft-oC.clientLeft;
             var curY=e.pageY-cTop-oC.clientTop;
             //移動時(shí),創(chuàng)建不同的小球?qū)嵗?         new Ball(curX,curY,ballR);
         }
         //光標(biāo)移動出畫布
         function curOut() {
             //移出畫布后,關(guān)閉定時(shí)器;
             clearInterval(outtimer);
             outtimer=setInterval(function () {
                 if(ary.length===0){
                     clearInterval(timer);
                     clearInterval(outtimer);
                 }
             },10);
         }
     })();
    

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容