canvas實(shí)現(xiàn)環(huán)形進(jìn)度條效果
昨下午睡著了,晚上打開(kāi)手機(jī)才發(fā)現(xiàn)朋友給我發(fā)了一個(gè)QQ消息,問(wèn)我這個(gè)怎么實(shí)現(xiàn)?
這里就選canvas來(lái)簡(jiǎn)單寫(xiě)一下 先上代碼,然后在說(shuō)一說(shuō)需要注意的點(diǎn):
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>canvas環(huán)形進(jìn)度條</title> <style> body{ background-color:#000; text-align: center; } .canvas1{ margin-top: 100px; display: inline-block; background-color: #FFF; } </style> </head> <body> <canvas id="circle_process" class="canvas1"></canvas> <script> /* 需求:環(huán)形、一周分為10個(gè)片段,根據(jù)進(jìn)度去走的一個(gè)狀態(tài) 技術(shù)選型:canvas (挑戰(zhàn)加熟悉) 思路: 01 首先中間的文字部分不用說(shuō),使用canvas的畫(huà)文字。 02 圓形是個(gè)規(guī)則圖形,那么為了避免畫(huà)不規(guī)則圖形,我們可以用圓和矩形來(lái)重疊出效果。 a. 大的灰色背景圓 b. 小一圈的白色背景圓 c. 以同心圓的圓心為圓心,小圓為半徑為半徑復(fù)制畫(huà)10個(gè)小的矩形 */ //初始化動(dòng)畫(huà)變量 var requestAnimationFrame = window.requestAnimationFrame || window.msRequestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame; var cancelAnimationFrame = window.cancelAnimationFrame || window.msCancelAnimationFrame || window.mozCancelAnimationFrame || window.webkitCancelRequestAnimationFrame; //初始化當(dāng)前進(jìn)度數(shù) var curPercentCount = 0; //獲取canvas對(duì)象,設(shè)置畫(huà)布大小 var oC = document.querySelector('#circle_process'); oC.width = 300; oC.height = 300; //獲取canvas執(zhí)行上下文 var ctx = oC.getContext('2d'); //定義小矩形的個(gè)數(shù) var miniRectCount = 10; //定義圓心位置 var cirCenter = { x:oC.width/2, y:oC.height/2 }; //定義小矩形的大小rectSize var rectSize = { width:0, height:0 }; //圓對(duì)象構(gòu)造函數(shù) function Circle(center,radius){ this.center = center; this.radius = radius; } //小矩形對(duì)象構(gòu)造函數(shù) function MiniRect(length,width){ this.length = length; this.width = width; } //角度轉(zhuǎn)換成弧度的函數(shù) function d2a(angleInt){ return angleInt*Math.PI / 180; } //百分比轉(zhuǎn)換角度函數(shù)(這里減90因?yàn)閍rc0度是從右側(cè)開(kāi)始的) function percentTurn(percentFloat){ return percentFloat * 360 / 100 - 90; } //畫(huà)當(dāng)前百分比扇形的方法 function drawFanForPercent(percentFloat){ ctx.beginPath(); ctx.moveTo(cirCenter.x,cirCenter.y); ctx.lineTo(oC.width/2,(oC.height-baseCircle.radius*2)/2); ctx.arc(cirCenter.x,cirCenter.y,baseCircle.radius,d2a(-90),d2a(percentTurn(percentFloat))); ctx.fillStyle = 'aqua'; ctx.fill(); ctx.closePath(); } //畫(huà)圓的函數(shù) function drawArc(center,radius,start,end,type,color){ start = start || 0; end = end || 360; ctx.beginPath(); ctx.arc(center.x,center.y,radius,d2a(start),d2a(end)); ctx.fillStyle = color; ctx.strokeStyle = color; if(!!type){ (type === 'fill') && ctx.fill(); (type === 'stroke') && ctx.stroke(); } ctx.closePath(); } //畫(huà)文字的函數(shù) function drawPercentText(text,percentInt){ ctx.beginPath(); ctx.fillStyle = 'aqua'; ctx.font="italic small-caps bold 40px Calibri"; ctx.textAlign = 'center'; ctx.fillText(text,cirCenter.x,cirCenter.y-18,100); ctx.closePath(); ctx.beginPath(); ctx.fillStyle = 'aqua'; ctx.font="italic small-caps bold 60px Calibri"; ctx.textAlign = 'center'; ctx.fillText(percentInt+'%',cirCenter.x,cirCenter.y+40,100); ctx.closePath(); } //畫(huà)小方塊的方法 function drawMiniRect(startPoint,width,height,axisPoint,rotateAngle){ /* ctx.beginPath(); //平移,畫(huà)出第一個(gè) ctx.save(); ctx.translate(startPoint.x,startPoint.y); ctx.fillStyle = '#FFF'; ctx.fillRect(0,0,rectSize.width,rectSize.height); ctx.restore(); ctx.closePath(); //這種先平移畫(huà)出在旋轉(zhuǎn)的思路是錯(cuò)的,畫(huà)之后就不能轉(zhuǎn)了 ctx.save(); ctx.translate(axisPoint.x,axisPoint.y); ctx.rotate(rotateAngle); ctx.restore(); */ ctx.save(); ctx.translate(axisPoint.x,axisPoint.y); /*畫(huà)布平移到圓的中心*/ ctx.rotate(d2a(rotateAngle)); /*旋轉(zhuǎn)*/ /*畫(huà)*/ ctx.beginPath(); ctx.fillStyle = '#FFF'; ctx.fillRect(startPoint.x,startPoint.y,rectSize.width,rectSize.height); ctx.closePath(); ctx.restore(); } //畫(huà)整體 function draw(curPercent){ //底部灰色圓 drawArc(baseCircle.center,baseCircle.radius,null,null,'fill','#CCC'); //進(jìn)度扇形 drawFanForPercent(curPercent); //內(nèi)部白色遮擋圓 drawArc(innerCircle.center,innerCircle.radius,null,null,'fill','#FFF'); //畫(huà)文字 drawPercentText('當(dāng)前進(jìn)度',curPercent); //十個(gè)小的矩形 for(var i=0; i<miniRectCount; i++){ drawMiniRect(startPoint,rectSize.width,rectSize.height,cirCenter,i*360/miniRectCount); } } //實(shí)例化底圓和內(nèi)圓 var baseCircle = new Circle(cirCenter,130); var innerCircle = new Circle(cirCenter,100); //設(shè)置rectSize數(shù)值 rectSize.width = 15; rectSize.height = baseCircle.radius - innerCircle.radius + 5; //設(shè)置第一個(gè)小矩形的起始點(diǎn) (這里有誤差) // var startPoint = { // x: oC.width /2 - 7.5, // y: (oC.height - baseCircle.radius*2) / 2 // }; //由于平移到中心點(diǎn)之后畫(huà)的位置是在畫(huà)布外的,所以重新定義 var startPoint = { x:-7.5, y:-baseCircle.radius - 2 }; //這里開(kāi)定時(shí)去顯示當(dāng)前是百分之幾的進(jìn)度 var raf = null; var percent = 0; function actProcess(percentFloat){ percentFloat = percentFloat || 100; percent = Math.round(percentFloat); console.log(percent); curPercentCount++; raf = requestAnimationFrame(function(){ actProcess(percentFloat); }); draw(curPercentCount); if(curPercentCount >= percent){ cancelAnimationFrame(raf); return; } } actProcess(50); // cancelAnimationFrame(raf); //這里沒(méi)搞懂為什么percent會(huì)加 ? //解: requestAnimationFrame中方法還是需要有參數(shù),這里就用匿名函數(shù)回調(diào)的執(zhí)行體去指定。 /* //setInterval的方式 function actProcess(percentFloat){ if(curPercentCount >= percentFloat){ clearInterval(timer); return; } curPercentCount++; draw(curPercentCount); } clearInterval(timer); var timer = setInterval(function(){ actProcess(50); },16.7); */ //直接畫(huà)弧形的測(cè)試: //drawArc(innerCircle.center,innerCircle.radius,0,260,'fill','red'); /* 用到的技術(shù)點(diǎn): 01 canvas平移 02 canvas畫(huà)布狀態(tài)保存于恢復(fù) 03 canvas旋轉(zhuǎn) 04 canvas clearRect配合動(dòng)畫(huà)requestAnimationFrame 05 canvas寫(xiě)文字 */ </script> </body> </html>
接下來(lái)說(shuō)一些注意點(diǎn)和我寫(xiě)的過(guò)程中碰到的疑問(wèn):
疑問(wèn):
01 整體代碼沒(méi)有封裝成一個(gè)組件,感興趣的同學(xué)可以封裝一下。 我這有時(shí)間也會(huì)封裝。
02 畫(huà)文字的時(shí)候只能單獨(dú)畫(huà)一行文字么? 怎樣進(jìn)行換行?
03 canvas怎樣處理響應(yīng)式?
注意點(diǎn):
01 畫(huà)布平移之后,畫(huà)布上的點(diǎn)也會(huì)被平移,所以我在定義第一個(gè)小矩形的起始點(diǎn)的時(shí)候才會(huì)重新定義一個(gè)負(fù)值。
02 直接畫(huà)弧形來(lái)控制進(jìn)度不準(zhǔn)確,因?yàn)閍rc會(huì)自動(dòng)closePath(),最終形成這樣的一個(gè)效果。
03 默認(rèn)圓的0度起始位置是從3點(diǎn)鐘方向開(kāi)始的(見(jiàn)上圖),那么想從12點(diǎn)鐘位置開(kāi)始走進(jìn)度,需要減去90度的角度。
04 requestAnimationFrame的回調(diào)函數(shù)在有參數(shù)的情況下還是需要傳參數(shù)的,需要借助匿名函數(shù)回調(diào),在執(zhí)行體里面去執(zhí)行想要loop的函數(shù)內(nèi)容(可傳參數(shù))。否者會(huì)出現(xiàn)注釋中寫(xiě)道的pecent不規(guī)則增加的問(wèn)題。
先就這樣,之后可能會(huì)結(jié)合一個(gè)上傳圖片的小功能?chē)L試把它封裝成一個(gè)組件。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
javascript模擬實(shí)現(xiàn)C# String.format函數(shù)功能代碼
這篇文章主要介紹了javascript模擬實(shí)現(xiàn)C# String.format函數(shù)功能,相信大家可以用的到2013-11-11JavaScript高級(jí)程序設(shè)計(jì) 事件學(xué)習(xí)筆記
JavaScript高級(jí)程序設(shè)計(jì) 事件學(xué)習(xí)筆記,需要的朋友可以參考下。2011-09-09django js 實(shí)現(xiàn)表格動(dòng)態(tài)標(biāo)序號(hào)的實(shí)例代碼
本文通過(guò)實(shí)例代碼給大家介紹了django js 實(shí)現(xiàn)表格動(dòng)態(tài)標(biāo)序號(hào) ,代碼簡(jiǎn)單易懂,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07將JSON字符串轉(zhuǎn)換成Map對(duì)象的方法
下面小編就為大家?guī)?lái)一篇將JSON字符串轉(zhuǎn)換成Map對(duì)象的方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-11-11IScroll5 中文API參數(shù)說(shuō)明和調(diào)用方法
IScroll是移動(dòng)頁(yè)面上被使用的一款仿系統(tǒng)滾動(dòng)插件。IScroll5相對(duì)于之前的IScroll4改進(jìn)了許多,使得大家可以更方便的定制所需的功能了。2016-05-05Javascript進(jìn)制轉(zhuǎn)換實(shí)例分析
這篇文章主要介紹了Javascript進(jìn)制轉(zhuǎn)換方法,實(shí)例分析了javascript實(shí)現(xiàn)進(jìn)制轉(zhuǎn)換的技巧,需要的朋友可以參考下2015-05-05JS中的prototype與面向?qū)ο蟮膶?shí)例講解
JS中的prototype與面向?qū)ο蟮膶?shí)例講解,需要的朋友可以參考一下2013-05-05