示例代碼託管在: http://www.github.com/dashnowords/blogs博客園地址:《大史住在大前端》原創博文目錄html
華爲雲社區地址:【你要的前端打怪升級指南】前端
[TOC]git
使用原生canvasAPI
繪製雷達圖。(截圖以及數據來自於百度Echarts官方示例庫【查看示例連接】)。github
雷達圖
繪製的看起來並不複雜,無非就是一些路徑點的連線,其中的難點都在於一些細節。canvas
爲了不在繪製過程當中不斷根據夾角來計算某個數據點的座標,咱們可讓座標系先移動到繪圖中心,而後在繪製過程當中逐步旋轉並使用context.lineTo(x,y)
來連線便可,這樣作的好處是很明顯的。好比在繪製背景六邊形的時候,每次旋轉後,路徑點壓根就不須要移動,直接在循環中每次都調用context.lineTo( )
方法連線至同一個數據點便可,看起來位移沒有變,實際上隨着座標系的旋轉,連線繞過的是多邊形的軌跡。瀏覽器
爲了讓文字保持正常的方向,咱們須要將座標系的旋轉恢復到初始狀態再進行繪製。繪製的過程當中能夠根據繪製點和中心連線相對於x軸的角度來動態修改其繪製時的相對點(left
,right
,center
),不然就會出現下圖的結果,也就是文字區域的中心到圖形中心的距離的確是一致的,但這並非咱們想要的效果。echarts
請時刻記得canvas座標系的初始方向是x軸向右,y軸向下,和普通笛卡爾座標系是不同的,尤爲是在旋轉角度和座標計算的時候,很容易出現和預期角度不相符的結果。spa
//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)); } }
瀏覽器中可查看效果:rest
百度Echarts官方示例庫中有這樣一個雷達圖的示例,展現了在雷達圖上表現時間維度的示例,感興趣的讀者能夠自行查看。code