欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

D3.js實(shí)現(xiàn)雷達(dá)圖的方法詳解

 更新時(shí)間:2016年09月22日 08:41:02   投稿:daisy  
大家應(yīng)該都知道基本圖表一共有六種,分別是柱狀圖、折線圖、散點(diǎn)圖、氣泡圖、餅圖和雷達(dá)圖。前面五種圖形都已經(jīng)介紹過(guò)如何實(shí)現(xiàn)了,今天我們一起來(lái)看看最后的雷達(dá)圖。有需要的朋友們可以參考學(xué)習(xí)下。

前言

再簡(jiǎn)單介紹下D3.js,D3.js 是一個(gè)基于數(shù)據(jù)操作文檔JavaScript庫(kù)。D3幫助你給數(shù)據(jù)帶來(lái)活力通過(guò)使用HTML、SVG和CSS。D3重視Web標(biāo)準(zhǔn)為你提供現(xiàn)代瀏覽器的全部功能,而不是給你一個(gè)專有的框架。結(jié)合強(qiáng)大的可視化組件和數(shù)據(jù)驅(qū)動(dòng)方式Dom操作。這里也可以看到它是用SVG來(lái)呈現(xiàn)圖表的,所以使用D3.js是需要一定的SVG基礎(chǔ)的。

本文依然是先把簡(jiǎn)單的畫圖框架搭起來(lái),添加SVG畫布。這里和餅圖有點(diǎn)類似,為了方便后面的繪制,我們把組合這些元素的g元素移動(dòng)到畫布的中心:

<!DOCTYPE html>
<html lang="en">
 <head>
  <meta charset="UTF-8">
  <title>雷達(dá)圖</title>
  <style>
   .container {
    margin: 30px auto;
    width: 600px;
    height: 300px;
    border: 1px solid #000;
   }

  </style>
 </head>
 <body>
  <div class="container">
   <svg width="100%" height="100%"></svg>
  </div>
  <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
  <script>
   window.onload = function() {
    var width = 600, height = 300;
    // 創(chuàng)建一個(gè)分組用來(lái)組合要畫的圖表元素
    var main = d3.select('.container svg').append('g')
      .classed('main', true)
      .attr('transform', "translate(" + width/2 + ',' + height/2 + ')');

   };
   function getColor(idx) {
    var palette = [
     '#2ec7c9', '#b6a2de', '#5ab1ef', '#ffb980', '#d87a80',
     '#8d98b3', '#e5cf0d', '#97b552', '#95706d', '#dc69aa',
     '#07a2a4', '#9a7fd1', '#588dd5', '#f5994e', '#c05050',
     '#59678c', '#c9ab00', '#7eb00a', '#6f5553', '#c14089'
    ]
    return palette[idx % palette.length];
   }
  </script>
 </body>
</html>

這里為什么我會(huì)說(shuō)雷達(dá)圖和餅圖會(huì)有點(diǎn)類似呢?看一下下面這張圖。

可以看到,雷達(dá)圖的網(wǎng)軸(藍(lán)色部分)是由多個(gè)正多邊形所組成的,而正多邊形的繪制正好是可以利用圓半徑的特性來(lái)繪制的,所以從一開(kāi)始把繪制的原點(diǎn)移動(dòng)到畫布的中心是很方便后面的繪制工作的。

模擬數(shù)據(jù)

我們先模擬一些原始數(shù)據(jù)。

var data = {
 fieldNames: ['語(yǔ)文','數(shù)學(xué)','外語(yǔ)','物理','化學(xué)','生物','政治','歷史'],
 values: [
  [10,20,30,40,50,60,70,80]
 ]
};

計(jì)算網(wǎng)軸坐標(biāo)并繪制

在前面的其他圖表的實(shí)現(xiàn)中,都有比例尺或者布局這樣的東西來(lái)為我們轉(zhuǎn)化數(shù)據(jù)提供便利,雷達(dá)圖是否也存在這樣的工具函數(shù)呢?答案是沒(méi)有!沒(méi)有!沒(méi)有!重要的事情說(shuō)三遍!(-_-) 所以,我們只能開(kāi)動(dòng)自己的小腦瓜自己算了。

// 設(shè)定一些方便計(jì)算的常量
var radius = 100,
 // 指標(biāo)的個(gè)數(shù),即fieldNames的長(zhǎng)度
 total = 8,
 // 需要將網(wǎng)軸分成幾級(jí),即網(wǎng)軸上從小到大有多少個(gè)正多邊形
 level = 4,
 // 網(wǎng)軸的范圍,類似坐標(biāo)軸
 rangeMin = 0,
 rangeMax = 100,
 arc = 2 * Math.PI;
// 每項(xiàng)指標(biāo)所在的角度
var onePiece = arc/total;
// 計(jì)算網(wǎng)軸的正多邊形的坐標(biāo)
var polygons = {
 webs: [],
 webPoints: []
};
for(var k=level;k>0;k--) {
 var webs = '',
   webPoints = [];
 var r = radius/level * k;
 for(var i=0;i<total;i++) {
  var x = r * Math.sin(i * onePiece),
   y = r * Math.cos(i * onePiece);
  webs += x + ',' + y + ' ';
  webPoints.push({
   x: x,
   y: y
  });
 }
 polygons.webs.push(webs);
 polygons.webPoints.push(webPoints);
} 

計(jì)算網(wǎng)軸的坐標(biāo)就是計(jì)算一個(gè)個(gè)多邊形的各點(diǎn)坐標(biāo),為了后面添加polygon元素時(shí)方便繪制(points屬性的賦值),我們需要在求點(diǎn)坐標(biāo)的時(shí)候順便把它們拼成字符串。上述代碼的for循環(huán)中,外層循環(huán)代表一個(gè)多邊形,內(nèi)層循環(huán)代表多邊形上的點(diǎn),多邊形與多邊形之間差異僅僅在于它們的外圓的半徑不同,而同一多邊形的點(diǎn)與點(diǎn)之間的差異在于它們的角度不同。點(diǎn)的坐標(biāo)由半徑乘以角度的正弦或者余弦來(lái)求得。

得到了計(jì)算好的坐標(biāo)以后,我們就開(kāi)始添加網(wǎng)軸。

// 繪制網(wǎng)軸
var webs = main.append('g')
  .classed('webs', true);
webs.selectAll('polygon')
  .data(polygons.webs)
  .enter()
  .append('polygon')
  .attr('points', function(d) {
   return d;
  });

添加一個(gè)g元素用來(lái)組合所有代表網(wǎng)軸的元素,選擇其中的polygon元素并綁定polygons.webs數(shù)組,enter()搭配append()添加新的polygon元素,對(duì)points屬性進(jìn)行復(fù)制。完成這一系列在前面幾篇文章中已經(jīng)反復(fù)練習(xí)的操作以后,為了讓網(wǎng)軸更加的明顯,我們給它加一點(diǎn)樣式。

.webs polygon {
 fill: white;
 fill-opacity: 0.5;
 stroke: gray;
 stroke-dasharray: 10 5;
} 

我們得到了如下圖所示的網(wǎng)軸。

添加縱軸

接著我們把縱軸也添加上??v軸就是添加一根根的線條,連接中心點(diǎn)和最外層的多邊形上的點(diǎn),需要的數(shù)據(jù)可以從polygons.webPoints[0]中取。

// 添加縱軸
var lines = main.append('g')
  .classed('lines', true);
lines.selectAll('line')
  .data(polygons.webPoints[0])
  .enter()
  .append('line')
  .attr('x1', 0)
  .attr('y1', 0)
  .attr('x2', function(d) {
   return d.x;
  })
  .attr('y2', function(d) {
   return d.y;
  });

雷達(dá)圖的坐標(biāo)軸部分就完成了。

計(jì)算雷達(dá)圖區(qū)域并添加

雷達(dá)圖區(qū)域也是一個(gè)多邊形,只不過(guò)是一個(gè)不規(guī)則的多邊形。但是他的幾個(gè)點(diǎn)始終處在縱軸上,并且點(diǎn)在縱軸上的位置可以通過(guò)點(diǎn)所代表的值在縱軸范圍內(nèi)的占比計(jì)算出來(lái)的。

// 計(jì)算雷達(dá)圖表的坐標(biāo)
var areasData = [];
var values = data.values;
for(var i=0;i<values.length;i++) {
 var value = values[i],
   area = '',
   points = [];
 for(var k=0;k<total;k++) {
  var r = radius * (value[k] - rangeMin)/(rangeMax - rangeMin);
  var x = r * Math.sin(k * onePiece),
   y = r * Math.cos(k * onePiece);
  area += x + ',' + y + ' ';
  points.push({
   x: x,
   y: y
  })
 }
 areasData.push({
  polygon: area,
  points: points
 });
}

計(jì)算完點(diǎn)的坐標(biāo)以后我們就可以添加雷達(dá)圖區(qū)域了。為了使雷達(dá)圖更可觀,我們除了添加多邊形表示雷達(dá)圖的區(qū)域以外,也把多邊形在各縱軸上的點(diǎn)標(biāo)記出來(lái)。

// 添加g分組包含所有雷達(dá)圖區(qū)域
var areas = main.append('g')
 .classed('areas', true);
// 添加g分組用來(lái)包含一個(gè)雷達(dá)圖區(qū)域下的多邊形以及圓點(diǎn) 
areas.selectAll('g')
 .data(areasData)
 .enter()
 .append('g')
 .attr('class',function(d, i) {
  return 'area' + (i+1);
 });
for(var i=0;i<areasData.length;i++) {
 // 依次循環(huán)每個(gè)雷達(dá)圖區(qū)域
 var area = areas.select('.area' + (i+1)),
   areaData = areasData[i];
 // 繪制雷達(dá)圖區(qū)域下的多邊形
 area.append('polygon')
   .attr('points', areaData.polygon)
   .attr('stroke', function(d, index) {
    return getColor(i);
   })
   .attr('fill', function(d, index) {
    return getColor(i);
   });
 // 繪制雷達(dá)圖區(qū)域下的點(diǎn) 
 var circles = area.append('g')
   .classed('circles', true);
 circles.selectAll('circle')
   .data(areaData.points)
   .enter()
   .append('circle')
   .attr('cx', function(d) {
    return d.x;
   })
   .attr('cy', function(d) {
    return d.y;
   })
   .attr('r', 3)
   .attr('stroke', function(d, index) {
    return getColor(i);
   }); 
}

這里為了體驗(yàn)層次關(guān)系,我用areas包含住所有雷達(dá)圖區(qū)域,又在里面用一個(gè)g分組表示一個(gè)雷達(dá)圖區(qū)域,在雷達(dá)圖區(qū)域里包含組成該區(qū)域的多邊形和圓點(diǎn)。這里因?yàn)槲覀償?shù)據(jù)用一個(gè)雷達(dá)圖區(qū)域就表示了,所以這個(gè)for循環(huán)只會(huì)循環(huán)一次。給繪制好的區(qū)域加上樣式。

.areas polygon {
 fill-opacity: 0.5;
 stroke-width: 3;
}
.areas circle {
 fill: white;
 stroke-width: 3;
}

于是得到了下圖這個(gè)樣子的圖表。

計(jì)算文字標(biāo)簽坐標(biāo)并添加

為了讓上面的圖表更完整一些,我們給它加上文字標(biāo)簽。文字標(biāo)簽標(biāo)注在網(wǎng)軸的外圍,所以可以以計(jì)算網(wǎng)軸多邊形點(diǎn)坐標(biāo)的同樣的原理計(jì)算文字標(biāo)簽的坐標(biāo)。

// 計(jì)算文字標(biāo)簽坐標(biāo)
var textPoints = [];
var textRadius = radius + 20;
for(var i=0;i<total;i++) {
 var x = textRadius * Math.sin(i * onePiece),
   y = textRadius * Math.cos(i * onePiece);
 textPoints.push({
  x: x,
  y: y
 });
} 

計(jì)算好坐標(biāo)以后再添加到畫布中。

// 繪制文字標(biāo)簽
var texts = main.append('g')
  .classed('texts', true);
texts.selectAll('text')
  .data(textPoints)
  .enter()
  .append('text')
  .attr('x', function(d) {
   return d.x;
  })
  .attr('y', function(d) {
   return d.y;
  })
  .text(function(d,i) {
   return data.fieldNames[i];
  }); 

最后的樣子是這樣的。

總結(jié)

以上就是利用D3.js實(shí)現(xiàn)雷達(dá)的全部?jī)?nèi)容,希望這篇文章對(duì)大家的學(xué)習(xí)和工作能有所幫助。如果有疑問(wèn)大家可以留言交流,感興趣的朋友們請(qǐng)大家繼續(xù)關(guān)注腳本之家。

相關(guān)文章

最新評(píng)論