1 canvas知識
1.1 canvas基礎(chǔ)知識
- canvas繪制線,矩形,描邊,填充,使用圖片
- 注意點(diǎn):
- canvas的寬高屬性,必須設(shè)置在行內(nèi),設(shè)置系統(tǒng)屬性,不能設(shè)置css樣式,會出錯;
- 獲取canvas的寬高數(shù)值,在JS中可以通過
c.width和c.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)方法,清空畫布
- 注意點(diǎn):
- 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);
}
})();
- 創(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)和半徑繪制小球
- 構(gòu)造函數(shù):設(shè)置每個實(shí)例小球自己的屬性;
- 類函數(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)閉自己;
- mouseover事件:當(dāng)光標(biāo)滑上canvas畫布上,開啟定時(shí)器,獲取數(shù)組中的實(shí)例,然后,調(diào)用update()方法和render()方法;
- 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í)器;
- 第一個定時(shí)器:目的執(zhí)行數(shù)組中實(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);
}
})();