D3.js實(shí)現(xiàn)雷達(dá)圖的方法詳解
前言
再簡(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)注腳本之家。
- d3.js實(shí)現(xiàn)簡(jiǎn)單的網(wǎng)絡(luò)拓?fù)鋱D實(shí)例代碼
- D3.js實(shí)現(xiàn)拓?fù)鋱D的示例代碼
- D3.js實(shí)現(xiàn)折線圖的方法詳解
- D3.js實(shí)現(xiàn)柱狀圖的方法詳解
- JavaScript可視化圖表庫(kù)D3.js API中文參考
- D3.js實(shí)現(xiàn)餅狀圖的方法詳解
- 基于d3.js實(shí)現(xiàn)實(shí)時(shí)刷新的折線圖
- D3.js實(shí)現(xiàn)散點(diǎn)圖和氣泡圖的方法詳解
- d3.js實(shí)現(xiàn)立體柱圖的方法詳解
- 利用d3.js力導(dǎo)布局繪制資源拓?fù)鋱D實(shí)例教程
相關(guān)文章
JS中使用 after 偽類清除浮動(dòng)實(shí)例
這篇文章主要介紹了使用 after 偽類清除浮動(dòng)實(shí)例,非常不錯(cuò),具有參考借鑒價(jià)值,需要的的朋友參考下2017-03-03javascript正則表達(dá)配置擴(kuò)展名并實(shí)現(xiàn)驗(yàn)證
這篇文章主要介紹了javascript正則表達(dá)配置擴(kuò)展名并實(shí)現(xiàn)驗(yàn)證,文章圍繞主題展開(kāi)相關(guān)資料,具有以得參考價(jià)值,需要的小伙伴可以參考一下2022-02-02javascript上傳圖片前預(yù)覽圖片兼容大多數(shù)瀏覽器
上傳圖片前預(yù)覽圖片這種效果應(yīng)用比較廣泛,實(shí)現(xiàn)的方也大同小異,下面為大家介紹下,在javascript中是如何實(shí)現(xiàn)的,感興趣的朋友可以參考下2013-10-10JS實(shí)現(xiàn)網(wǎng)頁(yè)Div層Clone拖拽效果
這篇文章主要介紹了JS實(shí)現(xiàn)網(wǎng)頁(yè)Div層Clone拖拽效果,涉及JavaScript響應(yīng)鼠標(biāo)事件動(dòng)態(tài)改變頁(yè)面元素位置屬性及層級(jí)屬性的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-09-09JavaScript實(shí)現(xiàn)的原生態(tài)Tab標(biāo)簽頁(yè)功能【兼容IE6】
這篇文章主要介紹了JavaScript實(shí)現(xiàn)的原生態(tài)Tab標(biāo)簽頁(yè)功能,可兼容IE6及谷歌等瀏覽器,涉及javascript事件響應(yīng)及頁(yè)面元素動(dòng)態(tài)操作相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-09-09asp錯(cuò)誤 '80040e21' 多步 OLE DB&nbs
今天在寫asp入庫(kù)操作的時(shí)候提示Microsoft OLE DB Provider for ODBC Drivers 錯(cuò)誤 80040e21 多步 OLE DB 操作產(chǎn)生錯(cuò)誤,請(qǐng)檢查每個(gè) OLE DB 狀態(tài)值,經(jīng)測(cè)試時(shí)函數(shù)定義文件沒(méi)有加載導(dǎo)致類型不對(duì),所以無(wú)法入庫(kù)2023-05-05JavaScript實(shí)現(xiàn)網(wǎng)頁(yè)帶動(dòng)畫返回頂部的方法詳解
這篇文章主要為大家詳細(xì)介紹了如何利用JavaScript實(shí)現(xiàn)網(wǎng)頁(yè)帶動(dòng)畫返回頂部的效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-08-08JavaScript 斐波那契數(shù)列 倒序輸出 輸出100以內(nèi)的質(zhì)數(shù)代碼實(shí)例
這篇文章主要介紹了JavaScript 斐波那契數(shù)列 倒序輸出 輸出100以內(nèi)的質(zhì)數(shù)代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09Bootstrap輪播插件中圖片變形的終極解決方案 使用jqthumb.js
這篇文章主要介紹了Bootstrap輪播插件中圖片變形的終極解決方案,使用jqthumb.js,感興趣的小伙伴們可以參考一下2016-07-07