示例代碼托管在: "http://www.github.com/dashnowords/blogs" 博客園地址: "《大史住在大前端》原創博文目錄" 華為雲社區地址: "【你要的前端打怪升級指南】" [TOC] 一. 任務說明 使用原生 繪製雷達圖。(截圖以及數據來自於百度Echarts官方示例庫 ...
目錄
示例代碼托管在:http://www.github.com/dashnowords/blogs
博客園地址:《大史住在大前端》原創博文目錄
華為雲社區地址:【你要的前端打怪升級指南】
一. 任務說明
使用原生canvasAPI
繪製雷達圖。(截圖以及數據來自於百度Echarts官方示例庫【查看示例鏈接】)。
二. 重點提示
雷達圖
繪製的看起來並不複雜,無非就是一些路徑點的連線,其中的難點都在於一些細節。
坐標轉換
為了避免在繪製過程中不斷根據夾角來計算某個數據點的坐標,我們可以讓坐標系先移動到繪圖中心,然後在繪製過程中逐步旋轉並使用
context.lineTo(x,y)
來連線即可,這樣做的好處是很明顯的。比如在繪製背景六邊形的時候,每次旋轉後,路徑點壓根就不需要移動,直接在迴圈中每次都調用context.lineTo( )
方法連線至同一個數據點即可,看起來位移沒有變,實際上隨著坐標系的旋轉,連線繞過的是多邊形的軌跡。文字的對齊
為了讓文字保持正常的方向,我們需要將坐標系的旋轉恢復到初始狀態再進行繪製。繪製的過程中可以根據繪製點和中心連線相對於x軸的角度來動態修改其繪製時的相對點(
left
,right
,center
),否則就會出現下圖的結果,也就是文字區域的中心到圖形中心的距離的確是一致的,但這並不是我們想要的效果。
canvas坐標系
請時刻記得canvas坐標系的初始方向是x軸向右,y軸向下,和普通笛卡爾坐標系是不一樣的,尤其是在旋轉角度和坐標計算的時候,很容易出現和預期角度不相符的結果。
三. 示例代碼
//options數據來自於百度Echarts官方示例庫
start(options);
/**
* 繪製圖表
*/
function start(options) {
drawBg(options);
drawData(options);//繪製雷達圖
drawText(options);//繪製文字
}
function drawBg(options) {
let length = options.radar.indicator.length;
let angleStep = -2 * Math.PI / length;
context.strokeStyle="#b2b2b2";
context.lineWidth = 1;
//調整坐標系
//移動中心點
context.translate(500,300);
//將x軸旋轉至豎直向上
context.rotate(-90 * 2 * Math.PI / 360);
//每次以不同旋轉半徑繪製多個由大到小的圖形
for(let r = 200; r > 0 ; r -=40){
//移動至第一個繪圖點
context.save();
context.beginPath();
context.moveTo(r,0);
//轉動坐標系繪製所有點
for(let i = 0; i < length; i++){
context.rotate(angleStep);
context.lineTo(r,0);
}
context.closePath();
context.stroke();
//明暗色替換填充,此處從大到小切換顏色覆蓋式繪製即可
context.fillStyle = Math.round(r / 40) % 2 ? 'white':'#eaeaea';
context.fill();
context.restore();
}
}
/**
* 繪製數據
*/
function drawData(options) {
//解構賦值拿到數據關鍵點
let {radar:{indicator:indicators},series:[{data:data}]} = options;
let colors = ['#c43e3a','#364c5a'];
let length = indicators.length;
let angleStep = -2 * Math.PI / length;
for(let i = 0; i < data.length; i++){
context.save();
context.beginPath();
context.moveTo(200 * data[i].value[0] / indicators[0].max,0);
//遍歷每組數據
for(let j = 1; j < data[i].value.length; j++){
context.rotate(angleStep);
context.lineTo(200 * data[i].value[j] / indicators[j].max,0);
}
context.restore();
context.lineTo(200 * data[i].value[0] / indicators[0].max,0);
context.strokeStyle = colors[i];
context.lineWidth = 2;
context.stroke();
}
context.restore();
}
//繪製文字
function drawText(options) {
let {radar:{indicator:indicators}} = options;
let length = indicators.length;
let angleStep = 2 * Math.PI / length;
let r = 220;
context.fillStyle = 'black';
context.font = "14px bold 黑體";
context.textAlign = 'center';
context.rotate(90 * Math.PI * 2 / 360);
for(let i = 0; i < indicators.length; i++){
let curAngle = -90*2*Math.PI/360 - angleStep*i;
//根據方向調整文字的對齊點
let cos = Math.cos(curAngle);
if (Math.abs(cos) < 10e-4) {
context.textAlign = 'center';
}else if(cos > 0){
context.textAlign = 'left';
}else{
context.textAlign = 'right';
}
console.log(indicators[i].name, Math.cos(curAngle))
context.fillText(indicators[i].name, r * Math.cos(curAngle), r * Math.sin(curAngle));
}
}
瀏覽器中可查看效果:
百度Echarts官方示例庫中有這樣一個雷達圖的示例,展示了在雷達圖上表現時間維度的示例,感興趣的讀者可以自行查看。