我們從用面向對象的方式繪製圖表,到echarts實現k線圖,我們體驗到使用echarts等可視化庫的方便之處。
今天我們就分析一個實際案例。通過可視化成績查詢系統中的一個小模塊-折線圖,讓大家感受一下實際項目中如何進行選型和實現。
本篇我們將使用D3.js實現學生成績可查,圖表主要用來將所有測試過的技能類別,分數可視化,以此我們根據自身測試分數,做相應的查漏補缺,方便我們科學地提升自己專業能力,
因為canvs在數據展示方面,方便和操作難易程度上要弱於svg,所以我們選擇D3.
D3(或D3.js)是一個JavaScript庫,用於使用Web標準可視化數據。D3可幫助您使用SVG,Canvas和HTML將數據變為現實。D3將強大的可視化和交互技術與數據驅動的DOM操作方法相結合,為您提供現代瀏覽器的全部功能,並可自由地為您的數據設計正確的可視界面。
正如介紹所說,D3支持SVG與Canvas,我們看下兩者的差異:
Canvas
1)依賴解析度,
2)不支持事件處理器
3)弱的文本渲染能力
4)能夠以 .png 或 .jpg 格式保存結果圖像,
5)最適合圖像密集型的遊戲,其中的許多對象會被頻繁重繪。
SVG
1)不依賴解析度,線條顏色平滑,抗鋸齒
2)支持事件處理,便於用戶交互
3)最適合帶有大型渲染區域的應用程序(比如谷歌地圖)
4)複雜度高會減慢渲染速度(任何過度使用 DOM 的應用都不快)
5)不適合遊戲應用。
注意看SVG的第二點,數據交互這個是svg相比canvas的突出優勢,特別適合於可視化圖表製作,
svg可以用css,或者javascript操作,而且語法近似jQuery。用D3繪製SVG圖形,採用鏈式語法,大大縮短了我們的開發時間。
SVG元素有寬高屬性width,height表示繪製區域的寬度,高度。
SVG預定了七種元素:
1,矩形參數為6個:
2,圓形和橢圓形
橢圓與圓的參數差別就是將r分為rx,ry,分別表示橢圓的水平半徑,垂直半徑。
3,線條的參數有四個:
4,多邊形和折線
多邊形和折線只有points參數,寫法為x,y x,y區別在於多邊形會連接起點與終點。
<polygon points="100,20 20,90 60,160 140,160 180,90">
5.路徑
<path>標籤的功能豐富,可繪製很多圖形,線條,通過給定一系列點坐標來繪製,我們在D3中也會用到。
在SVG中可以使用<text>標籤繪製文字,屬性包括:
SVG中常見樣式有:
有了以上的SVG基礎知識我們就開始用D3繪製圖表吧。
首先我們先搭個架子給定一些樣式很簡單,注意引入了d3.v3.js:
<!DOCTYPE html> <html> ? <head> <meta charset="utf-8"> <title>D3繪製折線圖</title> <script src="https://cdn.bootcss.com/d3/3.2.2/d3.v3.js"></script> </head> <style type="text/css"> body { height: 100%; } ? .title { font-family: Arial, 微軟雅黑; font-size: 20px; text-anchor: middle; } ? .subTitle { font-family: Arial, 宋體; font-size: 14px; text-anchor: middle; fill: #2C3E50 } ? .axis path, .axis line { fill: none; stroke: black; shape-rendering: crispEdges; } ? .axis text { font-family: sans-serif; font-size: 12px; fill: #999; } </style> ? <body>
</body> <script> //準備構建圖表 </script> </html>
使用D3就像使用jQ一樣,參數我們用過一次就知道怎麼用了。
在<script>標籤內:
var dataset = [];//y軸數據由getData函數填充數據 var xMarks = [html, css, js, webpack, node, mongoDB];//x軸標籤的準備 var w = 700; var h = 500; var padding = 40; //用一個變數存儲標題和副標題的高度,如果沒有標題什麼的,就為0 var head_height = padding; var title = "學生成績"; var subTitle = "前端測試"; //用一個變數計算底部的高度,如果不是多系列,就為0 var foot_height = padding; //模擬數據執行函數 getData(); //定義畫布 var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h);
現在我們通過D3得到了SVG對象,並且設置了寬高屬性。
? //添加背景 svg.append("g") .append("rect") .attr("x", 0) .attr("y", 0) .attr("width", w) .attr("height", h) .style("fill", "#FFF") .style("stroke-width", 2) .style("stroke", "#E7E7E7");
? //添加標題 if (title != "") { svg.append("g") .append("text") .text(title) .attr("class", "title") .attr("x", w / 2) .attr("y", head_height); head_height += 30; } //添加副標題 if (subTitle != "") { svg.append("g") .append("text") .text(subTitle) .attr("class", "subTitle") .attr("x", w / 2) .attr("y", head_height); head_height += 20; }
用到我們全家變數padding,foot_height, head_height。
? //橫坐標軸比例尺 var xScale = d3.scale.linear() .domain([0, dataset.length - 1]) .range([padding, w - padding]); //縱坐標軸比例尺 var yScale = d3.scale.linear() .domain([0, d3.max(dataset)]) .range([h - foot_height, head_height]);
? //定義x軸 var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom").ticks(dataset.length); //添加x坐標軸並通過編號獲取對應的x軸考試種類 var xBar = svg.append("g") .attr("class", "axis") .attr("transform", "translate(0," + (h - padding) + ")") .call(xAxis) .selectAll("text") .text(function (d) { return xMarks[d]; });
? //定義y軸 var yAxis = d3.svg.axis() .scale(yScale) .orient("left").ticks(11); //添加y軸 var yBar = svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + padding + ",0)") .call(yAxis);
? //添加折線 var line = d3.svg.line() .x(function (d, i) { return xScale(i); }) .y(function (d) { return yScale(d); }); var path = svg.append("path") .attr("d", line(dataset)) .style("fill", "#2C3E50") .style("fill", "none") .style("stroke-width", 1) .style("stroke", "#2C3E50") .style("stroke-opacity", 0.9);
? //添加小圓點 svg.selectAll("circle") .data(dataset) .enter() .append("circle") .attr("cx", function (d, i) { return xScale(i); }) .attr("cy", function (d) { return yScale(d); }) .attr("r", 5) .attr("fill", "#E74C3C");
? //動態繪製 function drawChart() { getData(); yBar.transition().duration(1000).call(yAxis); //縱軸數據更新 yScale = d3.scale.linear() .domain([0, d3.max(dataset)]) .range([h - padding, head_height]); //重繪折線路徑 path.transition().duration(1000).attr("d", line(dataset)); //重繪圓點 svg.selectAll("circle") .data(dataset) .transition() .duration(1000) .attr("cx", function (d, i) { return xScale(i); }) .attr("cy", function (d) { return yScale(d); }) }
//模擬數據 function getData() { var dataNum = 6; dataset = []; for (i = 0; i < dataNum; i++) { dataset.push(Math.round(Math.random() * 100)); } }
? <p align="left"> <button onClick="javascript:drawChart();">刷新數據</button> </p>
1.我們採用了d3的v3版本,d3版本差異還是很大的。
2.其它圖表,比如像餅圖了,雷達圖了實現方式一樣的,
? a)準備數據,實際項目中通常數據來源於後台,通過數據交互獲取
? b)做圖表
? c)加交互
3.svg和canvas沒有附屬關係,也沒有好壞之分,只是適用的場景不同。
我們通過綜合案例中的一個折線圖,初步了解D3在綜合運用中用SVG的方式體現的圖表。
我們通過對比SVG與Canvas,使我們能夠在今後遇到的可視化業務中從容選擇。
d3的函數方法,我們第一次接觸可能會感覺到無從下手,一回生,二回熟嘛,多多練習就好了。
為了方便大家學習,這裡提供了個在線APID3的v3-v5的版本都在內,可自行查閱。
本篇我們只做了折線圖,常用的還有柱狀圖,扇形圖,雷達圖等,希望大家有時間,多多繪製不同圖形,提高自身能力。
下一篇我們將介紹常見的圖表庫,為大家提供更多的選擇,讓大家能夠根據不同的業務,進行最佳的技術選型。
推薦閱讀: