【帶着canvas去流浪(6)】繪製雷達圖

圖片描述

示例代碼託管在: http://www.github.com/dashnowords/blogs

博客園地址:《大史住在大前端》原創博文目錄html

華爲雲社區地址:【你要的前端打怪升級指南】前端

[TOC]git

一. 任務說明

使用原生canvasAPI繪製雷達圖。(截圖以及數據來自於百度Echarts官方示例庫【查看示例連接】)。github

圖片描述

二. 重點提示

雷達圖繪製的看起來並不複雜,無非就是一些路徑點的連線,其中的難點都在於一些細節。canvas

  1. 座標轉換

    爲了不在繪製過程當中不斷根據夾角來計算某個數據點的座標,咱們可讓座標系先移動到繪圖中心,而後在繪製過程當中逐步旋轉並使用context.lineTo(x,y)來連線便可,這樣作的好處是很明顯的。好比在繪製背景六邊形的時候,每次旋轉後,路徑點壓根就不須要移動,直接在循環中每次都調用context.lineTo( )方法連線至同一個數據點便可,看起來位移沒有變,實際上隨着座標系的旋轉,連線繞過的是多邊形的軌跡。瀏覽器

  2. 文字的對齊

    爲了讓文字保持正常的方向,咱們須要將座標系的旋轉恢復到初始狀態再進行繪製。繪製的過程當中能夠根據繪製點和中心連線相對於x軸的角度來動態修改其繪製時的相對點(left,right,center),不然就會出現下圖的結果,也就是文字區域的中心到圖形中心的距離的確是一致的,但這並非咱們想要的效果。echarts

圖片描述

  1. canvas座標系

    請時刻記得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

相關文章
相關標籤/搜索