新年伊始,學習不止,
順帶恭喜下自己博客點擊量突破 20w~啦~~ 不容易啊~~
--------------------------------------------------------------------------
王者榮耀的英雄介紹資料里,都有個雷達圖,以直觀的形式可以分析這個英雄的優缺點,如下圖所示:圖片來自網路,

為了避嫌,免得引發口水戰,我隱去了具體的英雄,只談雷達圖的繪制,
先看效果,

HTML
準備一個 canvas,
<canvas id="mycan" width="800" height="600"></canvas>
JavaScript
一些基礎作業:獲取canvas,獲取 ctx,
let mycan = document.getElementById("mycan");
let ctx = mycan.getContext("2d");
坐標點的獲取
雷達圖的拐角,其實就是三角函式得出來的坐標,

用 JavaScript 寫出來就是:θ 是弧度,
X = Math.cos( θ )* R
Y = Math.sin( θ )* R
每個點的坐標,可以借用回圈得出,如下所示,變數 bian,雷達圖的邊的個數,dotsArray 是專門用來存盤坐標點的陣列,
for(let i = 0 ; i <= bian-1 ; i++){
let angleHD = Math.PI*2 / bian * i - Math.PI/2 ;
let x = r * Math.cos( angleHD );
let y = r * Math.sin( angleHD );
dotsArray.push({x:x,y:y});
}
基礎練習:基本的多邊形
雷達圖的關鍵就是要會繪制基本的多邊形,由上面的推理,可以得知,一個基本的多邊形的繪制如下所示:
let mycan = document.getElementById("mycan");
let ctx = mycan.getContext("2d");
let bian = 5 ; // 5 邊形
let r = 150 ; // 坐標點與中心的距離
let dotsArray = []; // 坐標點陣列
for(let i = 0 ; i <= bian-1 ; i++){
// 角度 - PI/2 ,是讓起點坐標在 12點 位置,
let angleHD = Math.PI*2 / bian * i - Math.PI/2 ;
let x = r * Math.cos( angleHD );
let y = r * Math.sin( angleHD );
dotsArray.push({x:x,y:y});
}
console.info( dotsArray );
// 開始繪制路徑
ctx.beginPath();
ctx.save();
// 位移原點到canvas中心,這樣,繪制坐標的時候,就不用考慮位移情況,
ctx.translate( mycan.width/2 ,mycan.height/2);
ctx.moveTo( dotsArray[0].x, dotsArray[0].y );
for(let i=1; i<=dotsArray.length-1 ; i++){
ctx.lineTo( dotsArray[i].x, dotsArray[i].y);
}
ctx.closePath();
ctx.stroke();
ctx.restore();

中級練習:面向物件的方式繪制多邊形
以上代碼為基礎,現在改進下,用面向物件方式繪制多邊形,
polygon.js
/*
* 多邊形類:
* 引數:
* x,y 中心點坐標
* r 距離中心點的距離
* bian 邊數,至少大于3
* dotsArray 存盤各個點的坐標陣列
* */
class Polygon{
constructor(props) {
this.x = 0 ;
this.y = 0 ;
this.r = 150 ;
this.bian = 3 ;
this.dotsArray = [];
this.fillStyle = "#000";
this.strokeStyle = "#f30";
Object.assign(this, props);
return this;
}
createPath(ctx){
let {x,y, bian,r} = this;
ctx.beginPath();
ctx.save();
for(let i = 0 ; i <= bian-1; i++ ){
let angleHD = Math.PI*2 / bian * i - Math.PI/2 ;
let dotX = r*Math.cos( angleHD );
let dotY = r*Math.sin( angleHD );
this.dotsArray.push({x:dotX, y:dotY});
}
ctx.moveTo( this.dotsArray[0].x , this.dotsArray[0].y);
for(let i=1 ; i <= this.dotsArray.length-1 ; i++){
ctx.lineTo( this.dotsArray[i].x , this.dotsArray[i].y);
}
ctx.closePath(); // 封閉路徑
ctx.restore();
return this;
}
render(ctx){
let {x,y,strokeStyle, fillStyle } = this ;
ctx.save();
ctx.translate( x , y );
ctx.strokeStyle = strokeStyle;
this.createPath(ctx);
ctx.stroke();
ctx.restore();
return this ;
}
}
let mycan = document.getElementById("mycan");
let ctx = mycan.getContext("2d");
let myPolygon = new Polygon({
x : mycan.width/2,
y : mycan.height/2,
bian : 5,
r : 200
});
myPolygon.render(ctx);

高級練習:繪制雷達圖
把上面的類改進,拓展下,完成雷達圖的繪制,
/*
* 對變形類:
* 引數:
* x,y : 中心點坐標
* r : 距離中心點的距離
* datas 各個點的文字內容
* 資料 v : 0-100 的分數
* name: 點上的資料名稱
* false(默認) 或者
[
{ name:"智力", v:90},
{ name:"武力", v:80},
{ name:"策略", v:99},
{ name:"內政", v:90}
]
*
* bian 邊數,至少大于3
* dotsArray 存盤各個點的坐標陣列
* fillStyle , strokeStyle 顏色,以深色為主
* subStrokeStyle 內部線條顏色,以淺色為主,
* font 文字大小,字體
* radarXXColor 雷達資訊的相關色彩
*/
class Polygon{
constructor(props) {
this.x = 0 ;
this.y = 0 ;
this.r = 150 ;
this.datas = props.datas ;
this.bianNum = this.datas.length || 3 ;
this.dotsArray = [];
this.fillStyle = "#000";
this.strokeStyle = "#000";
this.subStrokeStyle = "#ccc";
this.font = "18px '微軟雅黑'";
this.radarFillColor = "rgba(255,100,100, 0.3)"
this.radarStrokeColor = "rgba(255,100,100, 1)"
Object.assign(this, props);
return this;
}
/*
* createBgPath : 繪制雷達圖的背景,
* */
createBgPath(ctx){
let {x,y,r,bianNum,subStrokeStyle,strokeStyle} = this;
let disR = r / 5 ;
let angle = Math.PI*2 / bianNum ;
ctx.save();
// 5 個同心多邊形
for(let i=0 ; i<=4 ; i++ ){
let dotsX , dotsY ;
ctx.beginPath();
ctx.strokeStyle = subStrokeStyle;
for( let j=0 ; j <= bianNum-1 ; j++ ){
dotsX = Math.cos( angle*j-Math.PI/2 )*disR*(i+1);
dotsY = Math.sin( angle*j-Math.PI/2 )*disR*(i+1);
if( j===0 ){
ctx.moveTo( dotsX, dotsY );
}else{
ctx.lineTo( dotsX, dotsY );
}
// 如果是最外面的邊,存盤邊的各個坐標,
if( i === 4 ){
this.dotsArray.push({x:dotsX,y:dotsY});
}
}
// 如果是最外面的邊,更改描邊色,
if( i===4 ){
ctx.strokeStyle = strokeStyle;
}
ctx.closePath();
ctx.stroke();
}
for(let i = 0 ; i<=this.dotsArray.length-1; i++){
ctx.save();
ctx.strokeStyle = subStrokeStyle ;
ctx.beginPath();
ctx.moveTo(0,0);
ctx.lineTo(this.dotsArray[i].x, this.dotsArray[i].y);
ctx.stroke();
ctx.restore();
}
ctx.restore();
return this;
}
/*
* createRadarText : 繪制雷達圖的文字
* */
createRadarText( ctx ){
let {x,y, datas ,font, dotsArray } = this;
ctx.save();
ctx.translate(x, y);
ctx.font = font ;
this.createBgPath(ctx); // 繪制雷達圖背景
for(let i=0 ; i <= dotsArray.length-1 ; i++){
let txt = datas[i].name ;
let dx = dotsArray[i].x;
let dy = dotsArray[i].y;
// 根據坐標,把文本的位置做適當調整,
if(dx<0){
ctx.textAlign = "end" ;
dx = dx - 5 ;
}else if(dx>0.01){
ctx.textAlign = "start" ;
dx = dx + 5 ;
}else{
ctx.textAlign = "center" ;
}
if( dy < 0 ){
ctx.textBaseline = "bottom";
dy = dy-5 ;
}else if(dy>0.01){
ctx.textAlign = "top" ;
dy = dy + 20 ;
}else{
ctx.textBaseline = "middle";
}
// 繪制文本,坐標在最大的多邊形的幾個點上
ctx.fillText( txt ,dx, dy );
}
ctx.restore();
return this;
}
/*
* 繪制雷達的點
* */
drawRadarDots(ctx,dotsArray){
let {x,y,radarStrokeColor} = this;
ctx.save();
ctx.translate(x, y);
ctx.fillStyle = radarStrokeColor;
for(let i = 0 ; i <= dotsArray.length-1 ; i++){
ctx.beginPath();
ctx.arc( dotsArray[i].x , dotsArray[i].y , 5, 0,Math.PI*2);
ctx.fill();
}
ctx.restore();
return this;
}
/*
* 繪制雷達圖
* */
drawRadar(ctx){
let {x,y,r, datas, bianNum, radarFillColor,radarStrokeColor} = this;
let radarDots = []; // 雷達點陣列
let angle = Math.PI*2 / bianNum ;
// 繪制雷達文字,含背景
this.createRadarText(ctx);
// 繪制雷達部分
ctx.save();
ctx.strokeStyle = radarStrokeColor;
ctx.fillStyle = radarFillColor ;
ctx.translate(x,y);
ctx.beginPath();
for( let i = 0 ; i<=datas.length-1 ; i++){
let dx = datas[i].v/100*r*Math.cos( angle*i - Math.PI/2 );
let dy = datas[i].v/100*r*Math.sin( angle*i - Math.PI/2 );
radarDots.push( {x:dx,y:dy} );
if( i===0 ){
ctx.moveTo(dx,dy);
}else{
ctx.lineTo(dx,dy);
}
}
ctx.closePath();
ctx.fill();
ctx.stroke();
ctx.restore();
// 繪制雷達點
this.drawRadarDots(ctx,radarDots);
console.info( radarDots );
}
}
let mycan = document.getElementById("mycan");
let ctx = mycan.getContext("2d");
let myData = [
{ name:"戰績", v:98},
{ name:"團戰", v:38},
{ name:"發育", v:100},
{ name:"輸出", v:95},
{ name:"推進", v:72},
{ name:"生存", v:80}
];
let myPolygon = new Polygon({
datas:myData,
x:mycan.width/2 ,
y:mycan.height/2 ,
r:200
});
myPolygon.drawRadar(ctx);
完工~
可能有不完美的地方,后面再改進~
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/259019.html
標籤:其他
上一篇:洛谷P1423 小玉在游泳
下一篇:牛客OI周賽13-普及組
