用canvas寫(xiě)了一個(gè)簡(jiǎn)單的轉(zhuǎn)盤抽獎(jiǎng)插件,
給大家參考下下。。。
做的時(shí)候的想法是,通過(guò)傳進(jìn)來(lái)的標(biāo)簽以及屬性,直接可以new出一個(gè)轉(zhuǎn)盤
如:var a = new createRound(obj,{...}) ;
因?yàn)槭怯胏anvas來(lái)寫(xiě),所以肯定會(huì)用到角度,弧度了什么的,所以做好準(zhǔn)備工作:
1,角度轉(zhuǎn)弧度?
說(shuō)明:因?yàn)?π弧度等于360度,所以通過(guò)這個(gè)封裝成以下函數(shù)
function d2a(n){
? ? ? ?return n * Math.PI / 180;
};
2,n - m 區(qū)間獲取隨機(jī)數(shù),一會(huì)后面會(huì)用到
function rnd(n,m){
? ? ? return parseInt(Math.random()*(m-n))+n;
};
3,因?yàn)橐貜?fù)使用,所以想著用面向?qū)ο螅F(xiàn)在編輯構(gòu)造函數(shù)。根據(jù)我們開(kāi)始定義的使用方法來(lái)傳值:

說(shuō)明:
傳進(jìn)obj后,我們?cè)趏bj里面添加創(chuàng)建一個(gè)canvas,并且給他賦值寬高,(canvas寬度以及高度,必須用屬性來(lái)寫(xiě),寫(xiě)在樣式里會(huì)進(jìn)行拉伸哦),所以我直接寫(xiě)的是 this.oC.width = this.config.width ; 來(lái)定義屬性值;
this.init() 里面放的時(shí)直接調(diào)用的一些函數(shù),之所以都放到this.config 的 json 里,是因?yàn)檫@樣更靈活,不用擔(dān)心順序而出現(xiàn)錯(cuò)誤,里面有一些在繪制時(shí)需要用到的值 和一些默認(rèn)值 ;
4,this.init() 里的內(nèi)容

說(shuō)明:因?yàn)槿绻麤](méi)有抽獎(jiǎng)信息的話,轉(zhuǎn)盤是不成立的,所以做了初始判斷,this.config.data ,如果沒(méi)有數(shù)據(jù)就 return 出去;
5,畫(huà)弧,也可以說(shuō)是畫(huà)圓

說(shuō)明:
this.gd.save() : 保存畫(huà)布坐標(biāo)系統(tǒng)的狀態(tài)
this.gd.beginPath() : 重新畫(huà)(清除之前的路徑)
this.gd.closePath() : 閉合路徑(自動(dòng)連接到起始點(diǎn))
this.gd.restore() : 恢復(fù)save之后設(shè)置的狀態(tài)
可以簡(jiǎn)單理解為調(diào)用restore之后,restore方法前調(diào)用的rotate/translate/scale方法全部就還原了,畫(huà)布的坐標(biāo)系統(tǒng)恢復(fù)到save方法之前,但是這里要注意的是,restore方法的調(diào)用只影響restore之后繪制的內(nèi)容,對(duì)restore之前已經(jīng)繪制到屏幕上的圖形不會(huì)產(chǎn)生任何影響。(這段話是從網(wǎng)上找的,我自己總結(jié)不了這么清楚。。。) ;
接下來(lái)要開(kāi)始繪制圓了
this.gd.moveTo( 起點(diǎn)x , 起點(diǎn)y ) ; 通過(guò)這連接其他地方
this.gd.arc(圓心x,圓心y,半徑,起始角度,結(jié)束角度) ?,因?yàn)槔锩嫘枰?的是弧度,所以我們剛才封裝的d2a(n) 用到了。
this.gd.fillStyle = 填充顏色;
如上圖所示,我們將它封裝好,因?yàn)橐粫?huì)轉(zhuǎn)盤內(nèi)部分塊的時(shí)候還需要用到;

我們要根據(jù)傳進(jìn)來(lái)的 data 來(lái)確定分成多少塊,所以在上面 第 4 步的時(shí)候,我們把數(shù)據(jù)傳進(jìn)去調(diào)用,
c 是數(shù)據(jù) 傳進(jìn)來(lái)的顏色 。
a 是起始角度,從 0度 開(kāi)始 減去偏移量,因?yàn)楫?huà)弧度是默認(rèn)開(kāi)始角度 是90度(如圖1所示),所以需要減90(得到圖2) 想讓第一塊內(nèi)容在中心位置(如圖3),所以需要減去當(dāng)前塊的一半角度 360 / arr.length / 2,找了個(gè)變量將要減的值存儲(chǔ)下來(lái) this.config.pyl 。
b 是結(jié)束角度,值是(i + 1),也同樣需要減去偏移量。
數(shù)據(jù)下新增 start 和 end 將起始角度和結(jié)束角度存儲(chǔ)下來(lái) , ?因?yàn)檫@個(gè)需要填寫(xiě)的是正確的角度 ,所以需要將剛才減去的 90度 加上,后面的 90/arr.length 是在隨機(jī)區(qū)域內(nèi)的角度的時(shí)候 ,不到于太靠邊上。
調(diào)用剛才封裝好的 drow 函數(shù),并將值 傳進(jìn)去 循環(huán)創(chuàng)建多塊內(nèi)容



6,繪制指針

說(shuō)明:畫(huà)箭頭,用數(shù)組存儲(chǔ)位置,這個(gè)箭頭稍微有點(diǎn)長(zhǎng),不過(guò)如果圓盤小的時(shí)候可以根據(jù)需要進(jìn)行適當(dāng)調(diào)整
this.gd.moveTo() : 起始位置定義為中心靠左 5 像素 ;
循環(huán)數(shù)組 連接 里點(diǎn)的點(diǎn) ,接著做了一個(gè)陰影的處理,設(shè)置了箭頭的顏色 ;
找了半天沒(méi)有找到怎么移動(dòng)自己劃出來(lái)的圖形的方法,因?yàn)閳D形是由點(diǎn)連成線繪制,所以也沒(méi)法使用 translate 或者 rotate ,所以將它轉(zhuǎn)換成圖片,然后對(duì)圖片進(jìn)行操作,當(dāng)圖片加載完成后,調(diào)用 _this.clearOther(0) 函數(shù),下面會(huì)對(duì)該函數(shù)說(shuō)明。
7,為指針轉(zhuǎn)動(dòng)做準(zhǔn)備工作

說(shuō)明:當(dāng)指針旋轉(zhuǎn)的時(shí)候必須清空畫(huà)布,要不然會(huì)連續(xù)繪制,達(dá)不到我們想要的效果,所以里面的好多內(nèi)容需要重新繪制,上面對(duì)代碼有備注,大家可以看一下
旋轉(zhuǎn)箭頭時(shí),rotate,默認(rèn)以畫(huà)布左上角為支點(diǎn),箭頭生成的圖片與畫(huà)布大小一樣,我們要以畫(huà)布的中心對(duì)箭頭進(jìn)行旋轉(zhuǎn),所以我們需要先將箭頭 移到 中心位置,translate 后,與剛才不同的是 支點(diǎn) 移動(dòng)到中心位置,但是整體位置已經(jīng)偏移,所以在 rotate 后 我們需要將 圖片自身 的位置 定義為 負(fù)值,讓圖片回到自己最初的位置

8,指針轉(zhuǎn)動(dòng)到指定位置


說(shuō)明:因?yàn)橐粡垐D無(wú)法截取完整代碼,所以分兩張圖。
因?yàn)槭钱?dāng)用戶觸發(fā)時(shí)開(kāi)啟定時(shí)器,所以為了防止用戶在連續(xù)開(kāi)啟定時(shí)器,所以,首先定義一個(gè)開(kāi)關(guān),當(dāng)用戶點(diǎn)擊時(shí) 判斷 是否在進(jìn)行,如果在 進(jìn)行,就 return 出來(lái),如果沒(méi)有進(jìn)行,定義值,現(xiàn)在開(kāi)始進(jìn)行,當(dāng)運(yùn)動(dòng)結(jié)束后,改變值,記錄運(yùn)動(dòng)已經(jīng)結(jié)束。
因?yàn)楫?dāng)將方法是在用戶觸發(fā)時(shí)調(diào)用,所以當(dāng)用戶調(diào)用時(shí)需要將中獎(jiǎng)id傳參進(jìn)去,循環(huán)數(shù)據(jù),將每個(gè)數(shù)據(jù)索引存儲(chǔ)起來(lái),進(jìn)行判斷,如果當(dāng)前索引下數(shù)據(jù)的 id 等于 傳參進(jìn)來(lái)的 id 則將該中獎(jiǎng)索引記錄下來(lái),然后獲取到數(shù)據(jù)該索引下的內(nèi)容,將之前存儲(chǔ)的 起始角度(start)和 結(jié)束角度 (end ) 取出來(lái),通過(guò)隨機(jī)數(shù),找到其區(qū)域內(nèi)的角度 為 目標(biāo)角度(t)。
n 為當(dāng)前進(jìn)行的角度 ,默認(rèn)快速旋轉(zhuǎn)3圈后,開(kāi)始慢慢減速,所以當(dāng) n > 360*3 ( 3 圈分界線 ) 的角度后,開(kāi)始慢慢減速,(可能大家有疑問(wèn),為什么要給 t 加上 360 ,是因?yàn)槿绻?t 等于 10度 或者特別小的度數(shù)的時(shí)候,由慢變慢的過(guò)程不明顯,所以又加了360,讓它有個(gè)慢慢減速的過(guò)程)。當(dāng)進(jìn)行的角度 減去 分割線后 大于 等于 目標(biāo)角度(t)后,證明已經(jīng)到達(dá)角度終點(diǎn),停止定時(shí)器,將進(jìn)行角度 歸 0。

9,繪制獎(jiǎng)品信息,展示數(shù)據(jù)提供的文字信息,進(jìn)行封裝

this.gd.textBaseline = ' top ' ; ?字體位置,如下圖所示:

this.gd.textAlign = ' center ' ; ? 如下圖所示:

this.gd.translate(this.config.cx,this.config.cy); 將字體默認(rèn)定義在畫(huà)布中心,本來(lái)的算用 rotate 來(lái)處理文字的角度,但是用了之后,文字位置角度是正確了,可是文字本身角度也進(jìn)行了調(diào)整,所以沒(méi)辦法,只好通過(guò)文字自身的 x , y 來(lái)調(diào)整所處的正確位置。
var x = Math.sin(d2a(i*360/l)) * (this.config.r - 50); ? 因?yàn)榻嵌群托本€的位置我們是已知的(因?yàn)槲覀兌x所有文字據(jù)中心的位置都是相等的 相當(dāng)于半徑 ,所以我們知道斜線位置,角度就是 360 除以 份數(shù)后 乘以 i 得出來(lái)的值),然后通過(guò)下圖公式去求出 x 值,y 值也是同樣方法。(學(xué)好數(shù)理化,走遍天下都不怕,驕傲臉 ^_^ )

this.gd.fillText(str,x,-y); 將設(shè)置好的值傳進(jìn)去,就可以定義好文字的位置了,因?yàn)?y 值 的 0 位置在畫(huà)布中心,所以當(dāng)位置在上面時(shí)自然就成了負(fù)值 (大家不要用 x軸y軸的想法去想哦)。
10,循環(huán)數(shù)據(jù),給數(shù)據(jù)中的不同文字定義不同位置

說(shuō)明:循環(huán)數(shù)據(jù),使用之前封裝好的方法將數(shù)據(jù)下的每一份的文字定義位置。
11,封裝成功,開(kāi)始調(diào)用

說(shuō)明:這個(gè)是自己弄的假數(shù)據(jù) ,大家可根據(jù)后臺(tái)提供的不同數(shù)據(jù)來(lái)對(duì)代碼進(jìn)行,適當(dāng)?shù)恼{(diào)整
寫(xiě)到這,轉(zhuǎn)盤抽獎(jiǎng)就差不多完了,第一次總結(jié)自己寫(xiě)的小demo,有不對(duì)的地方歡迎大家多多指教,在寫(xiě)的時(shí)候 是按照 自己封裝好后的代碼順序 一塊一塊進(jìn)行的,大家看著可能有些亂,可以去 github 上下載代碼進(jìn)行參考,看起來(lái)會(huì)更直觀一點(diǎn)。
地址:https://github.com/juanjuanGit/rotary-draw.git