日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

D3.js学习笔记七:多系列折线图与图例

發(fā)布時(shí)間:2023/12/20 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 D3.js学习笔记七:多系列折线图与图例 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


http://www.daliane.com/d3_js_xue_xi_bi_ji_qi_duo_xi_lie_zhe_xian_tu_yu_tu_li/


要解決的問題

現(xiàn)在這個(gè)統(tǒng)計(jì)圖還要解決幾個(gè)問題:支持多個(gè)系列、為多系列加入圖例。

現(xiàn)在的數(shù)據(jù)是單條折線,如果有多條折線,那么需要為它指定不同的名稱和顏色,為它們指定圖例,指定圖例以后,我們通過圖例來控制折線的顯示和隱藏。

通過多維數(shù)組產(chǎn)生折線

首先調(diào)整產(chǎn)生數(shù)據(jù)系列的函數(shù),使它產(chǎn)生不定長度的隨機(jī)數(shù),每一個(gè)系列為一個(gè)數(shù)組,并指定折線名稱。

//產(chǎn)生隨機(jī)數(shù)據(jù)
function getData()
{
var lineNum=Math.round(Math.random()*10)%3+1;
var dataNum=Math.round(Math.round(Math.random()*10))+5;
oldData=dataset;
dataset=[];
xMarks=[];
lineNames=[];for(i=0;i<dataNum;i++)
{
xMarks.push("標(biāo)簽"+i);
}
for(i=0;i<lineNum;i++)
{
var tempArr=[];
for(j=1;j<dataNum;j++)
{
tempArr.push(Math.round(Math.random()*h));
}
dataset.push(tempArr);
lineNames.push("系列"+i);
}
}

我們希望能夠自由的添加折線,最好的辦法就是將折線封裝起來,做成一個(gè)折線類,每次添加刪除就調(diào)用它的相關(guān)方法就行了,定義折線類,它有4個(gè)方法,init是第一次產(chǎn)生折線時(shí)候調(diào)用,它初始化內(nèi)部對(duì)象,movieBegin在數(shù)據(jù)更換之前調(diào)用,將圖表置于動(dòng)畫開始狀態(tài),reDraw開始數(shù)據(jù)動(dòng)畫,remove將折線從畫布清除。

//定義折線類
function CrystalLineObject()
{
this.group=null;
this.path=null;
this.oldData=[];this.init=function(id)
{
var arr=dataset[id];
this.group=svg.append("g");var line = d3.svg.line()
.x(function(d,i){return xScale(i);})
.y(function(d){return yScale(d);});

//添加折線
this.path=this.group.append("path")
.attr("d",line(arr))
.style("fill","none")
.style("stroke-width",1)
.style("stroke",lineColor[id])
.style("stroke-opacity",0.9);

//添加系列的小圓點(diǎn)
this.group.selectAll("circle")
.data(arr)
.enter()
.append("circle")
.attr("cx", function(d,i) {
return xScale(i);
})
.attr("cy", function(d) {
return yScale(d);
})
.attr("r",5)
.attr("fill",lineColor[id]);
this.oldData=arr;
};

//動(dòng)畫初始化方法
this.movieBegin=function(id)
{
var arr=dataset[i];
//補(bǔ)足/刪除路徑
var olddata=this.oldData;
var line= d3.svg.line()
.x(function(d,i){if(i>=olddata.length) return w-padding; else return xScale(i);})
.y(function(d,i){if(i>=olddata.length) return h-foot_height; else return yScale(olddata[i]);});

//路徑初始化
this.path.attr("d",line(arr));

//截?cái)嗯f數(shù)據(jù)
var tempData=olddata.slice(0,arr.length);
var circle=this.group.selectAll("circle").data(tempData);

//刪除多余的圓點(diǎn)
circle.exit().remove();

//圓點(diǎn)初始化,添加圓點(diǎn),多出來的到右側(cè)底部
this.group.selectAll("circle")
.data(arr)
.enter()
.append("circle")
.attr("cx", function(d,i){
if(i>=olddata.length) return w-padding; else return xScale(i);
})
.attr("cy",function(d,i){
if(i>=olddata.length) return h-foot_height; else return yScale(d);
})
.attr("r",5)
.attr("fill",lineColor[id]);

this.oldData=arr;
};

//重繪加動(dòng)畫效果
this.reDraw=function(id,_duration)
{
var arr=dataset[i];
var line = d3.svg.line()
.x(function(d,i){return xScale(i);})
.y(function(d){return yScale(d);});

//路徑動(dòng)畫
this.path.transition().duration(_duration).attr("d",line(arr));

//圓點(diǎn)動(dòng)畫
this.group.selectAll("circle")
.transition()
.duration(_duration)
.attr("cx", function(d,i) {
return xScale(i);
})
.attr("cy", function(d) {
return yScale(d);
})
};

//從畫布刪除折線
this.remove=function()
{
this.group.remove();
};
}

我們修改了drawChart()函數(shù),使得它針對(duì)不定數(shù)量的折線作出處理,如果少了,就加上,否則刪除多余的線條。

for(i=0;i<dataset.length;i++)
{
if(i<currentLineNum)
{
//對(duì)已有的線條做動(dòng)畫
lineObject=lines[i];
lineObject.movieBegin(i);
}
else
{
//如果現(xiàn)有線條不夠,就加上一些
var newLine=new CrystalLineObject();
newLine.init(i);
lines.push(newLine);
}
}//刪除多余的線條,如果有的話
if(dataset.length<currentLineNum)
{
for(i=dataset.length;i<currentLineNum;i++)
{
lineObject=lines[i];
lineObject.remove();
}
lines.splice(dataset.length,currentLineNum-dataset.length);
}

為系列添加圖例

我們添加一個(gè)圖例元素到畫布,并且將圖例的增刪改做成了一個(gè)函數(shù),代碼如下:

//添加圖例
function addLegend()
{
var textGroup=legend.selectAll("text")
.data(lineNames);textGroup.exit().remove();legend.selectAll("text")
.data(lineNames)
.enter()
.append("text")
.text(function(d){return d;})
.attr("class","legend")
.attr("x", function(d,i) {return i*100;})
.attr("y",0)
.attr("fill",function(d,i){ return lineColor[i];});

var rectGroup=legend.selectAll("rect")
.data(lineNames);

rectGroup.exit().remove();

legend.selectAll("rect")
.data(lineNames)
.enter()
.append("rect")
.attr("x", function(d,i) {return i*100-20;})
.attr("y",-10)
.attr("width",12)
.attr("height",12)
.attr("fill",function(d,i){ return lineColor[i];});

legend.attr("transform","translate("+((w-lineNames.length*100)/2)+","+(h-10)+")");
}

這個(gè)是常規(guī)的功能,代碼雖然多,但是不難看懂,現(xiàn)在我們的折線圖如下圖所示。

察看新的動(dòng)畫演示效果:

? <!DOCTYPE html>
? <html>
? <head>
? <meta?charset="utf-8">
? <title>畫一個(gè)折線圖</title>
? <script?type="text/javascript"?src="js/d3.js"></script>
? </head>
? <style?type="text/css">
? body{
? height: 100%;
? }
? .title{font-family:Arial,微軟雅黑;font-size:18px;text-anchor:middle;}
? .subTitle{font-family:Arial,宋體;font-size:12px;text-anchor:middle;fill:#666}
? ?
? .axis path,
? .axis line {
? fill: none;
? stroke: black;
? shape-rendering: crispEdges;
? }
? .axis text {
? font-family: sans-serif;
? font-size: 11px;
? fill:#999;
? }
? ?
? .inner_line path,
? .inner_line line {
? fill: none;
? stroke:#E7E7E7;
? shape-rendering: crispEdges;
? }
? ?
? .legend{font-size: 12px; font-family:Arial, Helvetica, sans-serif}
? ?
? </style>
? <body>
? <script?type="text/javascript">
? var dataset=[];
? var lines=[]; //保存折線圖對(duì)象
? var xMarks=[];
? var lineNames=[]; //保存系列名稱
? var lineColor=["#F00","#09F","#0F0"];
? var w=600;
? var h=400;
? var padding=40;
? var currentLineNum=0;
? ?
? //用一個(gè)變量存儲(chǔ)標(biāo)題和副標(biāo)題的高度,如果沒有標(biāo)題什么的,就為0
? var head_height=padding;
? var title="收支平衡統(tǒng)計(jì)圖";
? var subTitle="2013年1月 至 2013年6月";
? ?
? //用一個(gè)變量計(jì)算底部的高度,如果不是多系列,就為0
? var foot_height=padding;
? ?
? //模擬數(shù)據(jù)
? getData();
? ?
? //判斷是否多維數(shù)組,如果不是,則轉(zhuǎn)為多維數(shù)組,這些處理是為了處理外部傳遞的參數(shù)設(shè)置的,現(xiàn)在數(shù)據(jù)標(biāo)準(zhǔn),沒什么用
? if(!(dataset[0] instanceof Array))
? {
? var tempArr=[];
? tempArr.push(dataset);
? dataset=tempArr;
? }
? ?
? //保存數(shù)組長度,也就是系列的個(gè)數(shù)
? currentLineNum=dataset.length;
? ?
? //圖例的預(yù)留位置
? foot_height+=25;
? ?
? //定義畫布
? var svg=d3.select("body")
? .append("svg")
? .attr("width",w)
? .attr("height",h);
? ?
? //添加背景
? 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");
? ?
? //添加標(biāo)題
? if(title!="")
? {
? svg.append("g")
? .append("text")
? .text(title)
? .attr("class","title")
? .attr("x",w/2)
? .attr("y",head_height);
? ?
? head_height+=30;
? }
? ?
? //添加副標(biāo)題
? if(subTitle!="")
? {
? svg.append("g")
? .append("text")
? .text(subTitle)
? .attr("class","subTitle")
? .attr("x",w/2)
? .attr("y",head_height);
? ?
? head_height+=20;
? }
? ?
? maxdata=getMaxdata(dataset);
? ?
? //橫坐標(biāo)軸比例尺
? var xScale = d3.scale.linear()
? .domain([0,dataset[0].length-1])
? .range([padding,w-padding]);
? ?
? //縱坐標(biāo)軸比例尺
? var yScale = d3.scale.linear()
? .domain([0,maxdata])
? .range([h-foot_height,head_height]);
? ?
? //定義橫軸網(wǎng)格線
? var xInner = d3.svg.axis()
? .scale(xScale)
? .tickSize(-(h-head_height-foot_height),0,0)
? .tickFormat("")
? .orient("bottom")
? .ticks(dataset[0].length);
? ?
? //添加橫軸網(wǎng)格線
? var xInnerBar=svg.append("g")
? .attr("class","inner_line")
? .attr("transform", "translate(0," + (h - padding) + ")")
? .call(xInner);
? ?
? //定義縱軸網(wǎng)格線
? var yInner = d3.svg.axis()
? .scale(yScale)
? .tickSize(-(w-padding*2),0,0)
? .tickFormat("")
? .orient("left")
? .ticks(10);
? ?
? //添加縱軸網(wǎng)格線
? var yInnerBar=svg.append("g")
? .attr("class", "inner_line")
? .attr("transform", "translate("+padding+",0)")
? .call(yInner);
? ?
? //定義橫軸
? var xAxis = d3.svg.axis()
? .scale(xScale)
? .orient("bottom")
? .ticks(dataset[0].length);
? ?
? //添加橫坐標(biāo)軸
? var xBar=svg.append("g")
? .attr("class","axis")
? .attr("transform", "translate(0," + (h - foot_height) + ")")
? .call(xAxis);
? ?
? //通過編號(hào)獲取對(duì)應(yīng)的橫軸標(biāo)簽
? xBar.selectAll("text")
? .text(function(d){return xMarks[d];});
? ?
? //定義縱軸
? var yAxis = d3.svg.axis()
? .scale(yScale)
? .orient("left")
? .ticks(10);
? ?
? //添加縱軸
? var yBar=svg.append("g")
? .attr("class", "axis")
? .attr("transform", "translate("+padding+",0)")
? .call(yAxis);
? ?
? //添加圖例
? var legend=svg.append("g");
? ?
? addLegend();
? ?
? //添加折線
? lines=[];
? for(i=0;i<currentLineNum;i++)
? {
? var newLine=new CrystalLineObject();
? newLine.init(i);
? lines.push(newLine);
? }
? ?
? //重新作圖
? function drawChart()
? {
? var _duration=1000;
? ?
? getData();
? ?
? addLegend();
? ?
? //設(shè)置線條動(dòng)畫起始位置
? var lineObject=new CrystalLineObject();
? ?
? for(i=0;i<dataset.length;i++)
? {
? if(i<currentLineNum)
? {
? //對(duì)已有的線條做動(dòng)畫
? lineObject=lines[i];
? lineObject.movieBegin(i);
? }
? else
? {
? //如果現(xiàn)有線條不夠,就加上一些
? var newLine=new CrystalLineObject();
? newLine.init(i);
? lines.push(newLine);
? }
? }
? ?
? //刪除多余的線條,如果有的話
? if(dataset.length<currentLineNum)
? {
? for(i=dataset.length;i<currentLineNum;i++)
? {
? lineObject=lines[i];
? lineObject.remove();
? }
? lines.splice(dataset.length,currentLineNum-dataset.length);
? }
? ?
? maxdata=getMaxdata(dataset);
? newLength=dataset[0].length;
? ?
? //橫軸數(shù)據(jù)動(dòng)畫
? xScale.domain([0,newLength-1]);
? xAxis.scale(xScale).ticks(newLength);
? xBar.transition().duration(_duration).call(xAxis);
? xBar.selectAll("text").text(function(d){return xMarks[d];});
? xInner.scale(xScale).ticks(newLength);
? xInnerBar.transition().duration(_duration).call(xInner);
? ?
? //縱軸數(shù)據(jù)動(dòng)畫
? yScale.domain([0,maxdata]);
? yBar.transition().duration(_duration).call(yAxis);
? yInnerBar.transition().duration(_duration).call(yInner);
? ?
? //開始線條動(dòng)畫
? for(i=0;i<lines.length;i++)
? {
? lineObject=lines[i];
? lineObject.reDraw(i,_duration);
? }
? ?
? currentLineNum=dataset.length;
? dataLength=newLength;
? }
? ?
? //定義折線類
? function CrystalLineObject()
? {
? this.group=null;
? this.path=null;
? this.oldData=[];
? ?
? this.init=function(id)
? {
? var arr=dataset[id];
? this.group=svg.append("g");
? ?
? var line = d3.svg.line()
? .x(function(d,i){return xScale(i);})
? .y(function(d){return yScale(d);});
? ?
? //添加折線
? this.path=this.group.append("path")
? .attr("d",line(arr))
? .style("fill","none")
? .style("stroke-width",1)
? .style("stroke",lineColor[id])
? .style("stroke-opacity",0.9);
? ?
? //添加系列的小圓點(diǎn)
? this.group.selectAll("circle")
? .data(arr)
? .enter()
? .append("circle")
? .attr("cx", function(d,i) {
? return xScale(i);
? })
? .attr("cy", function(d) {
? return yScale(d);
? })
? .attr("r",5)
? .attr("fill",lineColor[id]);
? this.oldData=arr;
? };
? ?
? //動(dòng)畫初始化方法
? this.movieBegin=function(id)
? {
? var arr=dataset[i];
? //補(bǔ)足/刪除路徑
? var olddata=this.oldData;
? var line= d3.svg.line()
? .x(function(d,i){if(i>=olddata.length) return w-padding; else return xScale(i);})
? .y(function(d,i){if(i>=olddata.length) return h-foot_height; else return yScale(olddata[i]);});
? ?
? //路徑初始化
? this.path.attr("d",line(arr));
? ?
? //截?cái)嗯f數(shù)據(jù)
? var tempData=olddata.slice(0,arr.length);
? var circle=this.group.selectAll("circle").data(tempData);
? ?
? //刪除多余的圓點(diǎn)
? circle.exit().remove();
? ?
? //圓點(diǎn)初始化,添加圓點(diǎn),多出來的到右側(cè)底部
? this.group.selectAll("circle")
? .data(arr)
? .enter()
? .append("circle")
? .attr("cx", function(d,i){
? if(i>=olddata.length) return w-padding; else return xScale(i);
? })
? .attr("cy",function(d,i){
? if(i>=olddata.length) return h-foot_height; else return yScale(d);
? })
? .attr("r",5)
? .attr("fill",lineColor[id]);
? ?
? this.oldData=arr;
? };
? ?
? //重繪加動(dòng)畫效果
? this.reDraw=function(id,_duration)
? {
? var arr=dataset[i];
? var line = d3.svg.line()
? .x(function(d,i){return xScale(i);})
? .y(function(d){return yScale(d);});
? ?
? //路徑動(dòng)畫
? this.path.transition().duration(_duration).attr("d",line(arr));
? ?
? //圓點(diǎn)動(dòng)畫
? this.group.selectAll("circle")
? .transition()
? .duration(_duration)
? .attr("cx", function(d,i) {
? return xScale(i);
? })
? .attr("cy", function(d) {
? return yScale(d);
? })
? };
? ?
? //從畫布刪除折線
? this.remove=function()
? {
? this.group.remove();
? };
? }
? ?
? //添加圖例
? function addLegend()
? {
? var textGroup=legend.selectAll("text")
? .data(lineNames);
? ?
? textGroup.exit().remove();
? ?
? legend.selectAll("text")
? .data(lineNames)
? .enter()
? .append("text")
? .text(function(d){return d;})
? .attr("class","legend")
? .attr("x", function(d,i) {return i*100;})
? .attr("y",0)
? .attr("fill",function(d,i){ return lineColor[i];});
? ?
? var rectGroup=legend.selectAll("rect")
? .data(lineNames);
? ?
? rectGroup.exit().remove();
? ?
? legend.selectAll("rect")
? .data(lineNames)
? .enter()
? .append("rect")
? .attr("x", function(d,i) {return i*100-20;})
? .attr("y",-10)
? .attr("width",12)
? .attr("height",12)
? .attr("fill",function(d,i){ return lineColor[i];});
? ?
? legend.attr("transform","translate("+((w-lineNames.length*100)/2)+","+(h-10)+")");
? }
? ?
? //產(chǎn)生隨機(jī)數(shù)據(jù)
? function getData()
? {
? var lineNum=Math.round(Math.random()*10)%3+1;
? var dataNum=Math.round(Math.round(Math.random()*10))+5;
? oldData=dataset;
? dataset=[];
? xMarks=[];
? lineNames=[];
? ?
? for(i=0;i<dataNum;i++)
? {
? xMarks.push("標(biāo)簽"+i);
? }
? for(i=0;i<lineNum;i++)
? {
? var tempArr=[];
? for(j=1;j<dataNum;j++)
? {
? tempArr.push(Math.round(Math.random()*h));
? }
? dataset.push(tempArr);
? lineNames.push("系列"+i);
? }
? }
? ?
? //取得多維數(shù)組最大值
? function getMaxdata(arr)
? {
? maxdata=0;
? for(i=0;i<arr.length;i++)
? {
? maxdata=d3.max([maxdata,d3.max(arr[i])]);
? }
? return maxdata;
? }
? </script>
? <p?align="left">
? <button?onClick="javascript:drawChart();">刷新數(shù)據(jù)</button>
? </p>
? </body>
? </html>

,打開后右鍵查看源碼,點(diǎn)擊【刷新數(shù)據(jù)】可以看到新的動(dòng)畫效果,加入了多系列支持和圖例,看起來比較接近水晶易表的折線圖了


總結(jié)

以上是生活随笔為你收集整理的D3.js学习笔记七:多系列折线图与图例的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。