canvas實(shí)現(xiàn)環(huán)形進(jìn)度條效果
昨下午睡著了,晚上打開手機(jī)才發(fā)現(xiàn)朋友給我發(fā)了一個(gè)QQ消息,問(wèn)我這個(gè)怎么實(shí)現(xiàn)?

這里就選canvas來(lái)簡(jiǎn)單寫一下 先上代碼,然后在說(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的畫文字。
02 圓形是個(gè)規(guī)則圖形,那么為了避免畫不規(guī)則圖形,我們可以用圓和矩形來(lái)重疊出效果。
a. 大的灰色背景圓
b. 小一圈的白色背景圓
c. 以同心圓的圓心為圓心,小圓為半徑為半徑復(fù)制畫10個(gè)小的矩形
*/
//初始化動(dòng)畫變量
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è)置畫布大小
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è)開始的)
function percentTurn(percentFloat){
return percentFloat * 360 / 100 - 90;
}
//畫當(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();
}
//畫圓的函數(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();
}
//畫文字的函數(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();
}
//畫小方塊的方法
function drawMiniRect(startPoint,width,height,axisPoint,rotateAngle){
/*
ctx.beginPath();
//平移,畫出第一個(gè)
ctx.save();
ctx.translate(startPoint.x,startPoint.y);
ctx.fillStyle = '#FFF';
ctx.fillRect(0,0,rectSize.width,rectSize.height);
ctx.restore();
ctx.closePath();
//這種先平移畫出在旋轉(zhuǎn)的思路是錯(cuò)的,畫之后就不能轉(zhuǎn)了
ctx.save();
ctx.translate(axisPoint.x,axisPoint.y);
ctx.rotate(rotateAngle);
ctx.restore();
*/
ctx.save();
ctx.translate(axisPoint.x,axisPoint.y); /*畫布平移到圓的中心*/
ctx.rotate(d2a(rotateAngle)); /*旋轉(zhuǎn)*/
/*畫*/
ctx.beginPath();
ctx.fillStyle = '#FFF';
ctx.fillRect(startPoint.x,startPoint.y,rectSize.width,rectSize.height);
ctx.closePath();
ctx.restore();
}
//畫整體
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');
//畫文字
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)之后畫的位置是在畫布外的,所以重新定義
var startPoint = {
x:-7.5,
y:-baseCircle.radius - 2
};
//這里開定時(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);
*/
//直接畫弧形的測(cè)試:
//drawArc(innerCircle.center,innerCircle.radius,0,260,'fill','red');
/*
用到的技術(shù)點(diǎn):
01 canvas平移
02 canvas畫布狀態(tài)保存于恢復(fù)
03 canvas旋轉(zhuǎn)
04 canvas clearRect配合動(dòng)畫requestAnimationFrame
05 canvas寫文字
*/
</script>
</body>
</html>
接下來(lái)說(shuō)一些注意點(diǎn)和我寫的過(guò)程中碰到的疑問(wèn):
疑問(wèn):
01 整體代碼沒(méi)有封裝成一個(gè)組件,感興趣的同學(xué)可以封裝一下。 我這有時(shí)間也會(huì)封裝。
02 畫文字的時(shí)候只能單獨(dú)畫一行文字么? 怎樣進(jìn)行換行?
03 canvas怎樣處理響應(yīng)式?
注意點(diǎn):
01 畫布平移之后,畫布上的點(diǎn)也會(huì)被平移,所以我在定義第一個(gè)小矩形的起始點(diǎn)的時(shí)候才會(huì)重新定義一個(gè)負(fù)值。
02 直接畫弧形來(lái)控制進(jìn)度不準(zhǔn)確,因?yàn)閍rc會(huì)自動(dòng)closePath(),最終形成這樣的一個(gè)效果。

03 默認(rèn)圓的0度起始位置是從3點(diǎn)鐘方向開始的(見上圖),那么想從12點(diǎn)鐘位置開始走進(jìn)度,需要減去90度的角度。
04 requestAnimationFrame的回調(diào)函數(shù)在有參數(shù)的情況下還是需要傳參數(shù)的,需要借助匿名函數(shù)回調(diào),在執(zhí)行體里面去執(zhí)行想要loop的函數(shù)內(nèi)容(可傳參數(shù))。否者會(huì)出現(xiàn)注釋中寫道的pecent不規(guī)則增加的問(wèn)題。
先就這樣,之后可能會(huì)結(jié)合一個(gè)上傳圖片的小功能嘗試把它封裝成一個(gè)組件。
以上就是本文的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作能帶來(lái)一定的幫助,同時(shí)也希望多多支持腳本之家!
相關(guān)文章
詳解JavaScript基于面向?qū)ο笾畡?chuàng)建對(duì)象(2)
這篇文章主要介紹了JavaScript基于面向?qū)ο笾畡?chuàng)建對(duì)象,詳細(xì)的分析面向?qū)ο蟮脑头绞揭约捌渌C合的方式,感興趣的小伙伴們可以參考一下2015-12-12
JS實(shí)現(xiàn)選項(xiàng)卡實(shí)例詳解
這篇文章主要介紹了JS實(shí)現(xiàn)選項(xiàng)卡的方法,結(jié)合實(shí)例形式詳細(xì)講述了JavaScript實(shí)現(xiàn)選項(xiàng)卡的布局與功能實(shí)現(xiàn)的完整步驟及相關(guān)技巧,需要的朋友可以參考下2015-11-11
JS中的算法與數(shù)據(jù)結(jié)構(gòu)之集合(Set)實(shí)例詳解
這篇文章主要介紹了JS中的算法與數(shù)據(jù)結(jié)構(gòu)之集合(Set),結(jié)合實(shí)例形式詳細(xì)分析了javascript中集合的概念、原理、定義及相關(guān)操作技巧,需要的朋友可以參考下2019-08-08
線路分流自動(dòng)智能跳轉(zhuǎn)代碼,自動(dòng)選擇最快鏡像網(wǎng)站(js)
線路分流自動(dòng)智能跳轉(zhuǎn)代碼,自動(dòng)選擇最快鏡像網(wǎng)站,自動(dòng)選擇電信或網(wǎng)通線路跳轉(zhuǎn)代碼2011-10-10
Javascript模擬scroll滾動(dòng)效果腳本
項(xiàng)目需要寫了一段模擬Scroll滾動(dòng)代碼,分享的同時(shí),我有些疑問(wèn),希望大家能幫忙解決,代碼如下2008-09-09

