您的位置:首頁>遊戲>正文

淺談canvas繪畫王者榮耀

作者:車大棒

原文連接:http://www.cnblogs.com/chedabang/archive/2017/12/09/8011321.html

背景:

一日晚上下班的我靜靜的靠在角落上聽著歌, 這時"滴!滴!"手機上傳來一陣qq消息。 原來我人在問王者榮耀的雷達圖在頁面上如何做出來的, 有人回答用canvas繪畫。 那麼問題來了, 已經好久沒有使用canvas繪畫了東西。

SO, 就想自己畫一個canvas雷達圖, 順便重新回顧一下canvas的知識點。

王者榮耀雷達圖的基本構成。

聊天記錄當中的雷達圖不是特別清楚, 所以我這邊截圖了自己的一個戰績雷達圖。

是不是有被我的戰績嚇到了, 害不害怕!

好了扯遠了, 讓我們回到正題上來。

通過截圖上面的雷達圖基本主體是一個正六邊形,

每個頂點則配有相應的文字說明。

然後就是中間紅色區域部分則由對角線上的點, 連成一圈填充構成。 因此這裡我們稱它為資料填充區

所以這個雷達圖我們分為三步來完成。

①正六邊形

②資料填充區

③繪製文本

推薦下我的web前端學習群:189394454, 不定期分享乾貨, 還有我整理的一份精品教程、歡迎初學者和在進階中的小夥伴。

正六變形的座標點解析

在繪畫這個正六邊形的時候, 先讓我們對於這個正六邊形進行簡單的數學分析。

這裡先用畫板畫一個正六變形, 然後進行切割並切角。

是吧, 借用以前高中還是初中的數學, 正六邊形的內角和720°, 那麼每一個對角就是120°。 在已知對角線的長度。 那麼通過sin60°,

cos60°一類的, 那個可以求出各個三角形的邊長。

可是問題來了, 這裡我們要計算的是各個座標點。 而canvas的坐標軸是從左上角算(0, 0)原點的單象限坐標軸。 假設六邊形的中心點是(250, 250)、對角線的長度是100*2, 那麼按照三角函數推斷:

bottom-center座標:(250, 250 + 100)

bottom-left座標:(250 - 100*sin(60°), 250+100*cos(60°))

top-left座標:(250 - 100*sin(60°), 250-100*cos(60°))

top-center座標:(250, 250 - 100)

top-right座標:(250 + 100*sin(60°), 250-100*cos(60°))

bottom-right的座標:(250 + 100*sin(60°), 250+100*cos(60°))

座標是出來了, 但是一個點一個點去繪畫是不是有點太low了!

腫麼辦?

啦啦啦啦!

那麼就到了我們找規律的時間來了!

但是在找規律的同時, 為毛中心點的X軸和別人不一樣, 為毛一會加一會減。

所以當思考各座標點參數的規律的時候, 讓先回顧以前的函數角度圖表

看完這個函數參照圖之後, 讓我再次修改一下6個點的書寫方式。

bottom-center座標:(250 + 100*sin(0°), 250 + 100*cos(0°))

bottom-left座標:(250 + 100*sin(300°), 250+100*cos(300°))

top-left座標:(250 + 100*sin(240°), 250-100*cos(240°))

top-center座標:(250 +100*sin(180°), 250 + 100*cos(180°))

top-right座標:(250 + 100*sin(120°), 250-100*cos(120°))

bottom-right的座標:(250 + 100*sin60°), 250+100*cos(60°))

這個時候再看組座標資料點,

是不是感覺有點意思!

那麼這個時候我們便可以通過一個for迴圈, 用一個陣列把這6個座標點給記錄下來。

var pointArr = [];for (var i = 0; i < 6; i++) { pointArr[i] = {}; pointArr[i].x = 250 + 100 * Math.sin(60 * i); pointArr[i].y = 250 + 100* Math.cos(60 * i); }1.1 繪畫正六邊形

前面既然, 將正六邊形的座標點通過一個for迴圈解析出來。 那麼就是代碼繪畫正六邊形了:

不規則的紅色半透明的六邊形。 其實就是就可以看做中心點, 到各個邊角點之間線段為一區間這。 之後就是將這個區間分成若干份, 你占這個這個區間多少份, 滿份就是邊角點, 零份就是原點。

觀察前面的雷達圖當中, B等級大概佔據某個等級的50%左右。 而B前面還有等級A、S。

所以當S等級時候, 可以看作區間 / 1。

B等級看作區間 / 2, 那麼A就是 區間 / 1.5.

以此類推就可以得出剩下 C 就是區間 / 2.5、D:區間/ 3

這裡我就不用for迴圈書寫了, 直接偷懶手寫一個物件。

// 繪製資料區域var letterData = { 'S': 1, 'A': 1.5, 'B': 2, 'C': 2.5, 'D': 3 }ctx.save();ctx.beginPath();for (var i = 0; i < 6; i++) { lineArr[i].yEnd = rCenter + curR * Math.cos(rAngle * i) / (letterData[rData[i][1]]); lineArr[i].xEnd = rCenter + curR * Math.sin(rAngle * i) / (letterData[rData[i][1]]); ctx.lineTo(lineArr[i].xEnd, lineArr[i].yEnd); console.log(lineArr);}ctx.closePath();ctx.stroke();ctx.fillStyle = 'rgba(255, 0, 0, 0.5)';ctx.fill();

2.2 對資料填充區域繪畫小圓點和邊長

當我們回歸到前面的截圖發現, 需要單獨把資料填充區域的的各個點位置給加強, 並把邊角用更深的線條的描繪出來。

ctx.lineWidth = 2; //設置資料填充區域的線條顏色ctx.strokeStyle = '#dd3f26'; //設置填充區域的顏色var point = 3; //設置資料填充區域的小圓點大小for (var i = 0; i < 6; i++) { ctx.beginPath(); ctx.arc(lineArr[i].xEnd, lineArr[i].yEnd, point, 0, Math.PI * 2); ctx.fillStyle = 'rgba(255, 0, 0, 0.8)'; ctx.fill(); console.log(lineArr); } ctx.restore();

3.1 繪製文本

王者榮耀雷達文本是需要繪製兩點,

①用黑色16px字體繪製各點描述點

②用紅色30px字體繪製各點能力級別

但是估計看到繪製文本, 估計有的小夥伴就會說。 不是有陣列的存儲各個邊角的座標, 直接一個for迴圈依次根據各個點繪畫出來不就OK了。

// 繪製文本 var rData = [ ['生存', 'S'], ['經濟', 'S'], ['輸出', 'S'], ['KDA', 'B'], ['打野', 'B'], ['推進', 'S'] ] ctx.save(); ctx.font = '16px Microsoft Yahei'; //設置字體 ctx.fillStyle = '#000'; // 顏色 for (var i = 0; i < 6; i++) { var y = rCenter + curR * Math.cos(rAngle * i); var x = rCenter + curR * Math.sin(rAngle * i); ctx.fillText(rData[i][0], x, y); } ctx.restore();

流覽器最終顯示的視覺效果:

是不是覺得很驚喜, 這裡輸出、經濟位置勉強還行, 但是剩下的文字位置就偏差了許多了。 所以在繪製文字的時候, 還得針對文字的座標位置進行相應的調整。

3.2 繪製文本--描述

既然直接調用座標的位置會出問題, 那麼讓根據上文中的圖片文字的規則簡單分析。

①如果X軸 == 中心點,那麼就判斷Y軸。比中心點大文字下移一點,反之文字上移一點。

②如果X軸 < 中心點,那麼文字X軸位置就左移動一點,反正右移動距離。

// 繪製文本 ctx.save(); var fontSize = 16; ctx.font = fontSize + 'px Microsoft Yahei'; ctx.textBaseline="middle"; //設置基線參考點 ctx.textAlign="center"; // 文本居中 ctx.fillStyle = '#000'; for (var i = 0; i < 6; i++) { var y = rCenter + curR * Math.cos(rAngle * i); var x = rCenter + curR * Math.sin(rAngle * i); console.log(Math.sin(rAngle * i)) var s_width = ctx.measureText(rData[i][0]).width; //獲取當前繪畫的字體寬度 if ( x == rCenter) { if (y > rCenter ) { ctx.fillText(rData[i][0], x - s_width/2, y + fontSize); } else { ctx.fillText(rData[i][0], x - s_width/2, y - fontSize); } } else if ( x > rCenter) { console.log(rData[i][0]); ctx.fillText(rData[i][0], x + s_width*1.5, y); } else { ctx.fillText(rData[i][0], x - s_width*1.5, y); }

這裡多了好幾個不常用的屬性,下面就是介紹這些屬性的特點:

ctx.textBaseline: 設置或返回在繪製文本時使用的當前文本基線

說到基線,各位童鞋想一想咱們以前英文練習本,上面有著一條條線條

瞬間回憶到當年被罰抄英語單詞的歲月,一把辛酸淚呀。

網頁設計字體也有一個基線的存在,因此canvas的基線點就是直接從座標點劃出一條橫線基線。

這裡從網路上截圖一張,通過設置基線參考位置,看看文本所在位置的改變。

推薦下我的web前端學習群:189394454,不定期分享乾貨,還有我整理的一份精品教程、歡迎初學者和在進階中的小夥伴。

ctx.textAlign: 這個文本水準居中,不過和CSS當中的居中不一樣的是,他是從座標點劃出一條分隔號分割文本的。

ctx.measureText : 返回包含指定文本寬度的物件。

通俗一點的就是說,就是獲取你繪製文本的寬度。假設一排文字內容為'Hello World', size為16px大小文本。在這裡高度都是16px穩定不變,這樣canvas畫其他元素對這個位置只需要Y軸移動這個文本的'size'大小就可以避免覆蓋到上面。

但是如果要X軸去移動位置,你根本不知道'Hello World'這串文本的長度。那麼這個時候就需要ctx.measureText這個方法,獲取當前你繪製文本的寬度。

3.2 繪製文本--能力級別

既然前面已經介紹了描述的繪畫方法,那麼依葫蘆畫瓢。讓我們一併開始繪製能力級別的文本。

// 繪製文本 ctx.save(); var fontSize = 16; var maxfontSize = 30; ctx.font = fontSize + 'px Microsoft Yahei'; ctx.textBaseline="middle"; ctx.textAlign="center"; for (var i = 0; i < 6; i++) { var y = rCenter + curR * Math.cos(rAngle * i); var x = rCenter + curR * Math.sin(rAngle * i); console.log(Math.sin(rAngle * i)) var s_width = ctx.measureText(rData[i][0]).width; if ( x == rCenter) { if (y > rCenter ) { ctx.fillText(rData[i][0], x - s_width/2, y + fontSize); } else { ctx.fillText(rData[i][0], x - s_width/2, y - fontSize); } } else if ( x > rCenter) { console.log(rData[i][0]); ctx.fillText(rData[i][0], x + s_width*1.5, y); } else { ctx.fillText(rData[i][0], x - s_width*1.5, y); } } ctx.restore(); ctx.save();// 繪製等級 ctx.font = '30px Microsoft Yahei bold'; ctx.fillStyle = '#d7431f'; ctx.textBaseline="middle"; ctx.textAlign="center"; for (var i = 0; i < 6; i++) { var y = rCenter + curR * Math.cos(rAngle * i); var x = rCenter + curR * Math.sin(rAngle * i); var M_width = ctx.measureText(rData[i][1]).width; if ( x == rCenter) { if (y > rCenter ) { ctx.fillText(rData[i][1], x + M_width/2, y + fontSize); } else { ctx.fillText(rData[i][1], x + M_width/2, y - fontSize); } } else if ( x > rCenter) { console.log(rData[i][0]); ctx.fillText(rData[i][1], x + M_width, y); } else { ctx.fillText(rData[i][1], x - M_width, y); } } ctx.restore(); ctx.save();

頁面最終效果:

結尾

好了!以上就是鄙人對於canvas繪畫一點簡單理解與複習了,其中也回顧了一些canvas基本屬性點。後續如何用canvas玩出各種花樣就看各位看官自己了!

小貼士:

在使用ctx.measureText這個方法的時候需要注意一下。這個方法在寬度參考物件也跟當前繪畫環境的font-size有關聯的。

打個比方說,在繪製描述的文本的時候。font-size設置是16px,那麼ctx.measureText('輸出').width 是32。

那麼在繪製能力等級的時候,font-size設置是32,那麼ctx.measureText('輸出').width 就不再是32了而是64或者。

貼士2:

這裡順便幫做設計朋友推廣他的一個微信H5視頻案例,全程水墨畫武俠風,畫工炒雞棒棒。


另外前面loadi ng動畫寶劍出鞘css3部分,利用極少transform3d代碼完成。感興趣的童鞋可以微信掃一掃,看一下運動軌跡就心中估計就能猜出運行的的css3代碼了。

關注公眾號→‘學習web前端’跟萬人一起學前端!

①如果X軸 == 中心點,那麼就判斷Y軸。比中心點大文字下移一點,反之文字上移一點。

②如果X軸 < 中心點,那麼文字X軸位置就左移動一點,反正右移動距離。

// 繪製文本 ctx.save(); var fontSize = 16; ctx.font = fontSize + 'px Microsoft Yahei'; ctx.textBaseline="middle"; //設置基線參考點 ctx.textAlign="center"; // 文本居中 ctx.fillStyle = '#000'; for (var i = 0; i < 6; i++) { var y = rCenter + curR * Math.cos(rAngle * i); var x = rCenter + curR * Math.sin(rAngle * i); console.log(Math.sin(rAngle * i)) var s_width = ctx.measureText(rData[i][0]).width; //獲取當前繪畫的字體寬度 if ( x == rCenter) { if (y > rCenter ) { ctx.fillText(rData[i][0], x - s_width/2, y + fontSize); } else { ctx.fillText(rData[i][0], x - s_width/2, y - fontSize); } } else if ( x > rCenter) { console.log(rData[i][0]); ctx.fillText(rData[i][0], x + s_width*1.5, y); } else { ctx.fillText(rData[i][0], x - s_width*1.5, y); }

這裡多了好幾個不常用的屬性,下面就是介紹這些屬性的特點:

ctx.textBaseline: 設置或返回在繪製文本時使用的當前文本基線

說到基線,各位童鞋想一想咱們以前英文練習本,上面有著一條條線條

瞬間回憶到當年被罰抄英語單詞的歲月,一把辛酸淚呀。

網頁設計字體也有一個基線的存在,因此canvas的基線點就是直接從座標點劃出一條橫線基線。

這裡從網路上截圖一張,通過設置基線參考位置,看看文本所在位置的改變。

推薦下我的web前端學習群:189394454,不定期分享乾貨,還有我整理的一份精品教程、歡迎初學者和在進階中的小夥伴。

ctx.textAlign: 這個文本水準居中,不過和CSS當中的居中不一樣的是,他是從座標點劃出一條分隔號分割文本的。

ctx.measureText : 返回包含指定文本寬度的物件。

通俗一點的就是說,就是獲取你繪製文本的寬度。假設一排文字內容為'Hello World', size為16px大小文本。在這裡高度都是16px穩定不變,這樣canvas畫其他元素對這個位置只需要Y軸移動這個文本的'size'大小就可以避免覆蓋到上面。

但是如果要X軸去移動位置,你根本不知道'Hello World'這串文本的長度。那麼這個時候就需要ctx.measureText這個方法,獲取當前你繪製文本的寬度。

3.2 繪製文本--能力級別

既然前面已經介紹了描述的繪畫方法,那麼依葫蘆畫瓢。讓我們一併開始繪製能力級別的文本。

// 繪製文本 ctx.save(); var fontSize = 16; var maxfontSize = 30; ctx.font = fontSize + 'px Microsoft Yahei'; ctx.textBaseline="middle"; ctx.textAlign="center"; for (var i = 0; i < 6; i++) { var y = rCenter + curR * Math.cos(rAngle * i); var x = rCenter + curR * Math.sin(rAngle * i); console.log(Math.sin(rAngle * i)) var s_width = ctx.measureText(rData[i][0]).width; if ( x == rCenter) { if (y > rCenter ) { ctx.fillText(rData[i][0], x - s_width/2, y + fontSize); } else { ctx.fillText(rData[i][0], x - s_width/2, y - fontSize); } } else if ( x > rCenter) { console.log(rData[i][0]); ctx.fillText(rData[i][0], x + s_width*1.5, y); } else { ctx.fillText(rData[i][0], x - s_width*1.5, y); } } ctx.restore(); ctx.save();// 繪製等級 ctx.font = '30px Microsoft Yahei bold'; ctx.fillStyle = '#d7431f'; ctx.textBaseline="middle"; ctx.textAlign="center"; for (var i = 0; i < 6; i++) { var y = rCenter + curR * Math.cos(rAngle * i); var x = rCenter + curR * Math.sin(rAngle * i); var M_width = ctx.measureText(rData[i][1]).width; if ( x == rCenter) { if (y > rCenter ) { ctx.fillText(rData[i][1], x + M_width/2, y + fontSize); } else { ctx.fillText(rData[i][1], x + M_width/2, y - fontSize); } } else if ( x > rCenter) { console.log(rData[i][0]); ctx.fillText(rData[i][1], x + M_width, y); } else { ctx.fillText(rData[i][1], x - M_width, y); } } ctx.restore(); ctx.save();

頁面最終效果:

結尾

好了!以上就是鄙人對於canvas繪畫一點簡單理解與複習了,其中也回顧了一些canvas基本屬性點。後續如何用canvas玩出各種花樣就看各位看官自己了!

小貼士:

在使用ctx.measureText這個方法的時候需要注意一下。這個方法在寬度參考物件也跟當前繪畫環境的font-size有關聯的。

打個比方說,在繪製描述的文本的時候。font-size設置是16px,那麼ctx.measureText('輸出').width 是32。

那麼在繪製能力等級的時候,font-size設置是32,那麼ctx.measureText('輸出').width 就不再是32了而是64或者。

貼士2:

這裡順便幫做設計朋友推廣他的一個微信H5視頻案例,全程水墨畫武俠風,畫工炒雞棒棒。


另外前面loadi ng動畫寶劍出鞘css3部分,利用極少transform3d代碼完成。感興趣的童鞋可以微信掃一掃,看一下運動軌跡就心中估計就能猜出運行的的css3代碼了。

關注公眾號→‘學習web前端’跟萬人一起學前端!

Next Article
喜欢就按个赞吧!!!
点击关闭提示