第二次调用D3可重用线图只绘制轴而不是线 [英] The second call to D3 reusable line chart only draws axes but not lines

查看:163
本文介绍了第二次调用D3可重用线图只绘制轴而不是线的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图绘制一个折线图,其中绘制两条线,一条线显示在今天的日期之前,另一条线显示在今天的日期之后,因为我使用D3的剪辑路径,用户可以点击按钮更新图表。这部分工作,但是当我尝试使该代码可重用,因为我想要第二个图表下面的第一个,当我传递第二个容器到同一个图表函数(即#graph1为图表1和#graph2为图表2)绘制轴,但不是第二个图形的线(第一个仍然工作),有人可能指向我的方向正确吗?谢谢。
下面是我的代码:

 功能图(选择){
selection.each ){

var svg = d3.select(this).selectAll(svg)。data([data]);

//更新外部尺寸b $ b svg.attr(width,width)
.attr(height,height);

//更新内部尺寸
var g = svg .select(g)
.attr(transform,translate(+ margin.left +,+ margin.top +));

if








$ b $。 xScale(parsedate(today)))
.attr(height,height- margin.top - margin.bottom);

svg.select(#clip-after rect)
.transition()。duration(400)
.attr(x,xScale(parsedate(today)))
.attr(width,width-xScale )))
.attr(height,height- margin.top - margin.bottom);

svg.selectAll(path.line-after)
.data(data).transition()
.duration(400).attr(d,line_after (数据));

svg.selectAll(path.line-before)
.data(data).transition()
.duration(400).attr(d,line_before (数据));


} else {
g.append(clipPath)
.attr(id,clip-before)
.append (rect)
.attr(width,xScale(parsedate(today)))
.attr(height,height- margin.top - margin.bottom);

g.append(clipPath)
.attr(id,clip-after)
.append(rect)
.attr (x,xScale(parsedate(today)))
.attr(width,width-xScale(parsedate(today)))
.attr(height,height- margin.top - margin.bottom);

g.selectAll(。line)
.data([after])
.enter()。append(path)
。 attr(class,function(d){returnline-+ d;})
.attr(clip-path,function(d){returnurl );})
.attr(d,line_after(data));

g.selectAll(。line)
.data([before])
.enter()。append(path)
。 attr(class,function(d){returnline-+ d;})
.attr(clip-path,function(d){returnurl );})
.attr(d,line_before(data));


}





//添加X轴
g.append (g)
.attr(class,x axis);


svg.select(。x.axis)
.attr(transform,translate(0,300) .call(xAxis);

g.selectAll(。x.axis text)//选择xaxis的所有文本元素
.attr(transform,function(d){
returntranslate(+ this.getBBox()。height * -2 +,+ this.getBBox()。height +)rotate(-45);
});

//添加Y轴
g.append(g)
.attr(class,y axis);

svg.select(。y.axis)。transition()。duration(400).call(yAxis);


}); // end choice.each
} // end chart


解决方案>

  / *** PredictGraph代码开始* /(function(){d3.PredictGraph = function(){//选项//(用下面的getter和setter操作)var margin = {top :width = getWidth($(window).width()),height = 450,xValue = function(d){return d.date;},yValue = function(d){return d.actualvalue;},yValue2 = function(d){return d.predictedvalue;},xScale = d3.time.scale()。range([0,width]),yScale = d3.scale .linear()。range([height,0]),xAxis = xAxis = d3.svg.axis()。scale(xScale).orient(bottom)。ticks(10).tickFormat(d3.time.format (%Y-%m-%d)),//这是轴的日期格式,yAxis = d3.svg.axis()。scale(yScale).orient(left)。ticks (10),line = d3.svg.line()。x(X).y(Y),line_before = d3.svg.line().x(function(d){return xScale(d.date);} ).y(function(d){return yScale(d.actualvalue); }),line_after = d3.svg.line().x(function(d){return xScale(d.date);}).y(function(d){return yScale(d.predictedvalue);})today = moment()._ d,// date object formatDate = d3.time.format(%Y-%m-%d),todayFormatted = formatDate(moment()._ d),alreadyClicked = false;函数(选择){selection.each(function){console.log(this); var max_actual = d3.max(data,function(d){return d.actualvalue;}); // var max_predicted = d3.max(data,function(d){return d.predictedvalue;}); // after var max = Math.max(max_actual,max_predicted); // total var min_actual = d3.min {return d.actualvalue;}); // after var min_predicted = d3.min(data,function(d){return d.predictedvalue;}); // after var min = Math.min(min_actual,min_predicted); var parsedate = d3.time.format(%Y-%m-%d)。parse; //将日期从一个格式化的字符串转换为一个日期对象data.forEach(function(d){d.date = parsedate range([0,width-margin.left(d));}}; //更新x-scale xScale.domain(d3.extent(data,function(d){return d.date; -  margin.right]); //更新y标度yScale.domain([min,max])。range([height- margin.top  -  margin.bottom,0]); //选择svg元素,如果存在var svg = d3.select(this).selectAll(svg)。data([data]); //否则,创建骨架图。 var gEnter = svg.enter()。append(svg)。append(g); //更新外部尺寸。 svg.attr(width,width).attr(height,height); //更新内部尺寸。 var g = svg.select(g)。attr(transform,translate(+ margin.left +,+ margin.top +)); console.log(svg.select(#clip-before+ this.id)); var currNode = this; if(svg.select(#clip-before+ this.id)[0] [0]!= null){svg.select(#clip-before+ this.id +rect).transition .duration(400).attr(width,xScale(parsedate(today))).attr(height,height- margin.top  -  margin.bottom); svg.select(#clip-after+ this.id +rect).transition()。duration(400).attr(x,xScale(parsedate(today))).attr(width,width -xScale(parsedate(today))).attr(height,height- margin.top  -  margin.bottom); svg.selectAll(path.line-after).data(data).transition().duration(400).attr(d,line_after(data)); svg.selectAll(path.line-before).data(data).transition().duration(400).attr(d,line_before(data)); svg.select(line.today).transition()。duration(400).attr({'x1':xScale(parsedate(today)),'y1':0,'x2':xScale )),'y2':height-margin.top  -  margin.bottom}).style(stroke,#FF7F66).style(stroke-dasharray,(3,3)).style fill,none); } else {g.append(clipPath).attr(id,clip-before+ this.id).append(rect).attr(width,xScale(parsedate(today))) .attr(height,height-margin.top  -  margin.bottom); g.append(clipPath).attr(id,clip-after+ this.id).append(rect).attr(x,xScale(parsedate(today))).attr width,width-xScale(parsedate(today))).attr(height,height- margin.top  -  margin.bottom); g.selectAll(。line).data([after]).enter()。append(path).attr(class,function(d){returnline-+ d; ).attr(clip-path,function(d){returnurl(#clip  - + d + currNode.id +);}).attr(d,line_after(data)); g.selectAll(。line).data([before]).enter()。append(path).attr(class,function(d){returnline-+ d; ).attr(clip-path,function(d){returnurl(#clip  - + d + currNode.id +);}).attr(d,line_before(data)); g.append(line).attr(class,today).attr({'x1':xScale(parsedate(today)),'y1':0,'x2':xScale )),'y2':height-margin.top  -  margin.bottom}).style(stroke,#FF7F66).style(stroke-dasharray,(3,3)).style fill,none); } //添加X轴g.append(g).attr(class,x axis); //.call(xAxis); svg.select(。x.axis).attr(transform,translate(0,300))。transition()。duration(400).call(xAxis); g.selectAll(。x.axis text)//选择xax的所有文本元素.attr(transform,function(d){returntranslate(+ this.getBBox()。height * -2 +,+ this.getBBox()。height +)rotate(-45);}); //添加Y轴g.append(g).attr(class,y axis); //.call(yAxis)svg.select(。y.axis)。transition()。duration(400).call(yAxis); alreadyClicked = true; }); } //路径生成器的x访问器; xScale o xValue。 function X(d){return xScale(formatDate.parse(d.date)); } //路径生成器的y访问器; yScale o yValue。函数Y(d){return yScale(d.actualvalue); } function Y2(d){return yScale(d.predictedvalue); } chart.margin = function(_){if(!arguments.length)return margin; margin = _;返回图表; }; chart.width = function(_){if(!arguments.length)return width; width = _;返回图表; }; chart.height = function(_){if(!arguments.length)return height; height = _;返回图表; }; chart.x = function(_){if(!arguments.length)return xValue; xValue = _;返回图表; }; chart.y = function(_){if(!arguments.length)return yValue; yValue = _;返回图表; };函数getWidth(width){if(width> 1500 || width< 990){return 800; } else if(width>> 1300&& width> 990){return 700; } else {return 650; }} return chart; };})(); / * PredictGraph代码结束* // * app.js代码开始* / var chart = d3.PredictGraph(); //全局变量var formatDate = d3.time.format(%Y-%m-%d); var today = formatDate(moment()._ d)|| 2016-01-01; var first_day = formatDate(getFirstDay())|| 2015-01-01; var numMonths =three; // one,three,six,twelve var last_day = formatDate(getLastDay(numMonths))|| 2016-04-01; var product =product1; var loaded = false; //每次用户点击月份按钮或选择下拉菜单项//每次输入参数:firstDay(今天减去12个月),lastDay(从numMonths检索到getLastDay的传递值)// productSelected(基于下拉菜单选择的产品菜单值)function updateResults(firstDay,lastDay,productSelected){//获取数据...将从Django最终而不是json文件//d3.json(data / data.json,function(data){var data = [{date:2015-01-01,productname:product1,actualvalue:512,predictedvalue:05},{date:2015-02-01,产品名:product1,实际值:311,预测值:426},{日期:2015-03-01 :725},{date:2015-04-01,productname:product1,actualvalue:396,predictedvalue:887},{date:2015-05-01 ,productname:product1,product1,actualvalue:70,predicted valueue:299},{date:2015-06-01预测值:40},{date:2015-07-01,productname:product1,actualvalue:157,predictedvalue:504},{date: 01,productname:product1,actualvalue:335,forecastvalue:372},{date:2015-09-01,productname:product1,actualvalue:426 ,预测值:894},{日期:2015年10月1日,产品名称: 11,productname:product1,actualvalue:701,prediction value:569},{date:2015-12-01 :674,predictedvalue:387},{date:2016-01-01,productname:product1,actualvalue:986,forecastvalue:65} product:product1,actual value:426,predictedvalue:49},{date:2016-03-01实际值:130,预测值:63},{date:2016-04-01,productname:product1,actualvalue:674,predictedvalue:889} :2016-05-01,productname:product1,actualvalue:192,predictedvalue:983},{date:2016-06-01,productname:product1 ,actualvalue:6预测值:186},{date:2016-07-01,productname:product1,actualvalue:476,日期:2016-09-01,产品名称:产品名称:产品名称在product1,actualvalue:121,predictedvalue:792},{date:2016-10-01,productname:product1,actualvalue:745,predictionvalue:712} {date:2016-11-01,productname:product1,actualvalue:755,predictedvalue:46},{date:2016-12-01,productname :product1,actualvalue:361,predictionvalue:810},{date:2015-01-01,productname:product2,actualvalue:675,prediction value },{date:2015-02-01,productname:product2,actualvalue:690,predictedvalue:388},{date:2015-03-01,产品名:product2,实际价值:994,预测值:871},{日期:2015-04-01 :656},{date:2015-05-01,productname:product2,actualvalue:901,predictedvalue:216},{date:2015-06-01 ,productname:product2,actualvalue:491},{date:2015-07-01,productname:product2预测值:360},{date:2015-08-01,productname:product2,actualvalue:299,predictedvalue:487},{date: 01,productname:product2,actualvalue:80,forecastvalue:267},{date:2015-10-01,productname:product2,actualvalue:760 ,预测值:830},{date:2015-11-01,productname:product2,actualvalue:122,prediction value:273} 12,productname:product2,actualvalue:859,predictedvalue:258},{date:2016-01-01 :807,predictedvalue:729},{date:2016-02-01,productname:product2,actualvalue:82,predictedvalue:939},{date: 2016-03-01,productname:product2,actualvalue:72,predictedvalue:348},{date:2016-04-01,productname:product2实际价值:544,forecastvalue:429},{date:2016-05-01,productname:product2,actualvalue:512,predictedvalue:562} :2016-06-01,productname:product2,actualvalue:510,predictedvalue:800},{date:2016-07-01,productname:product2 ,actualvalue:283,predictedvalue:847},{date:2016-08-01,productname:product2,actualvalue:172,predictedvalue:343}日期:2016年10月1日,产品名称:产品名称: product2,actualvalue:863,predictedvalue:184},{date:2016-11-01,productname:product2,actualvalue:125,prediction value:623} {date:2016-12-01,productname:product2,actualvalue:400,predictedvalue:920}]; //获取第一天和最后一天之间的数据以及所选的产品//绘制一行作为测试var test_data = data.filter(function(el){return el.date< lastDay& el.date> ; firstDay&& el.productname == productSelected}); d3.select(#graph1)。datum(test_data).call(chart); //}); } function updateResults2(firstDay,lastDay,productSelected){//获取数据...将从Django最终而不是json文件//d3.json(data / data.json,function(data){var data = {date:2015-01-01,productname:product1,actualvalue:512,predictedvalue:805},{date:2015-02-01,productname :product1,actualvalue:311,predictionvalue:426},{date:2015-03-01,productname:product1,actualvalue:305,predicted value:725 },{date:2015-04-01,productname:product1,actualvalue:396,predictedvalue:887}产品名:product1,actualvalue:70,predictedvalue:299},{date:2015-06-01 :40},{date:2015-07-01,productname:product1,actualvalue:157,predictedvalue:504},{date:2015-08-01 ,productname:product1,actualvalue:335,predicted value:372},{date:2015-09-01预测值:899},{date:2015-10-01,productname:product1,actualvalue:983,predictedvalue:842},{date: 01,productname:product1,actualvalue:701,forecastvalue:569},{date:2015-12-01,productname:product1,actualvalue:674 ,预测值:387},{date:2016-01-01,productname:product1,actualvalue:986,forecastvalue:65} 02,productname:product1,actualvalue:426预测值:49} :130,predictedvalue:63},{date:2016-04-01,productname:product1,actualvalue:674,predictedvalue:889},{date: product:product1,actualvalue:192,predictedvalue:983},{date:2016-06-01,productname:product1实际值:476,预测值:181},{日期:实际值:6,预测值:186} :2016-08-01,productname:product1,actualvalue:493,predictedvalue:544},{date:2016-09-01,productname:product1 ,实际价值:745,预测值:712},{日期:2016-10-01日期:2016-12-01,产品名称:日期:2016-11-01,productname:product1,actualvalue:755,predictedvalue:46} 预测值:810},{日期:2015-01-01,产品名称:product2,实际值:675,预测值:722} {date:2015-02-01,productname:product2,actualvalue:690,predictedvalue:388},{date:2015-03-01,productname :product2,actualvalue:994,predictedvalue:871},{date:2015-04-01,productname:product2,actualvalue:678,prediction value },{date:2015-05-01,productname:product2,actualvalue:901,predictedvalue:216}产品名:product2,实际价值:304,预测值:491},{日期:2015-07-01 :360},{date:2015-08-01,productname:product2,actualvalue:299,predictedvalue:48​​7},{date:2015-09-01 ,productname:product2,product2,actual value:80,forecastvalue:267},{date:2015-10-01预测值:830},{date:2015-11-01,productname:product2,actualvalue:122,predictedvalue:273} 01,productname:product2,actualvalue:859,forecastvalue:258},{date:2016-01-01,productname:product2,actualvalue:807 ,estimatedvalue:729},{date:2016-02-01,productname:product2,actualvalue:82,predictedvalue:939},{date: 03,productname:product2,actualvalue:72预测值:348},{date:2016-04-01 :544,predictedvalue:429},{date:2016-05-01,productname:product2,actualvalue:512,predictedvalue:562},{date: product:product2,actual value:510,predictedvalue:800},{date:2016-07-01实际价值:283,预测值:847},{date:2016-08-01,productname:product2,actualvalue:172,predictedvalue:343} :2016-09-01,productname:product2,actualvalue:315,predictedvalue:633},{date:2016-10-01,productname:product2 ,实际值:863,预测值:184},{date:2016-11-01,productname:product2,actualvalue:125,prediction value:623}日期:2016-12-01,productname:product2,actualvalue:400,predictedvalue:920} //获取第一天和最后一天之间的数据以及所选的产品//绘制一行作为测试var test_data = data.filter(function(el){return el.date< lastDay& el.date> ; firstDay&&product.productname == productSelected}); d3.select(#graph2)。datum(test_data).call(chart); //}); } $(document).ready(function(){$(#button30)。click(function(){$(。buttons1)。removeClass(selected-button); $(#button30) 。addClass(selected-button); numMonths = $(#button30)。data('months'); first_day = formatDate(getFirstDay()); last_day = formatDate(getLastDay(numMonths)); updateResults(first_day, lastCode(button-button); $(#button90)。addClass(button);选择按钮); numMonths = $(#button90)。data('months'); first_day = formatDate(getFirstDay()); last_day = formatDate(getLastDay(numMonths)); updateResults(first_day,last_day,product) ;}); $(#button180)。click(function(){$(。buttons1)。removeClass(selected-button); $(#button180)。addClass ); numMonths = $(#button180)。data('months'); first_day = formatDate(getFirstDay()); last_day = formatDate(getLastDay(numMonths)); updateResults(first_day,last_day,product);}); $(#button360)。click(function(){$(。buttons1)。removeClass(selected-button); $(#button360)。addClass(selected-button); numMonths = $(#button360)。data('months'); first_day = formatDate(getFirstDay()); last_day = formatDate(getLastDay(numMonths)); updateResults(first_day,last_day,product); }); updateResults(first_day,last_day,product); $(#button30-2)。click(function(){$(。buttons2)removeClass(selected-button); $(#button30-2)。addClass ); numMonths = $(#button30-2)。data('months'); first_day = formatDate(getFirstDay()); last_day = formatDate(getLastDay(numMonths)); updateResults2(first_day,last_day,product);} ); $(#button90-2)。click(function(){$(。buttons2)。removeClass(selected-button); ); numMonths = $(#button90-2)。data('months'); first_day = formatDate(getFirstDay()); last_day = formatDate(getLastDay(numMonths)); updateResults2(first_day,last_day,product);} ); $(#button180-2)。click(function(){$(。buttons2)removeClass(selected-button); $(#button180-2)addClass ); numMonths = $(#button180-2)。data('months'); first_day = formatDate(getFirstDay()); last_day = formatDate(getLastDay(numMonths)); updateResults2(first_day,last_day,product);} ); $(#button360-2)。addClass(selected-button)removeClass( ); numMonths = $(#button360-2)。data('months'); //或.data()first_day = formatDate(getFirstDay()); last_day = formatDate(getLastDay(numMonths));设置最后一天updateResults2(first_day,last_day,product);}); updateResults2(第一天,last_day,产品); }); //函数设置最后一天的日期...基本上今天+ 1,3,6或12个月,默认为3 function getLastDay(months){switch(months){caseone:return moment 1,'months')._ d;打破; casesix:return moment()。add(6,'months')._ d;打破; case十二:return moment()。add(12,'months')._ d;打破; default:return moment()。add(3,'months')._ d; }; } //函数设置第一天的日期...基本上今天 -  12函数getFirstDay(){return moment()。subtract(12,'months')。 }  

  .container {margin-top:50px;} html ,body {height:100%; font-family:'Open Sans',sans-serif;}。wrap {min-heigh:100%;}。prediction {height:700px;}。footer {width:100%;位置:固定; bottom:0px; padding:0px; text-align:center;}。footer p {margin-left:auto; margin-right:auto;} .selected-button {background-color:black; border-color:black; color :white;} .axis path,.axis line {fill:none; stroke:#000; shape-rendering:crispEdges; } .x.axis path {} .line {fill:none;中风:钢蓝stroke-width:1.5px; } .line-before {stroke:#2185C5; fill:none; } .line-after {stroke:#FFB779; fill:none; }  

 < script src =https:// ajax .googleapis.com / ajax / libs / jquery / 2.1.1 / jquery.min.js>< / script>< script src =https://cdnjs.cloudflare.com/ajax/libs/d3/ 3.4.11 / d3.min.js>< / script>< script src =http://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js >< / script> < script src =http://jhjanicki.github.io/reusable-line-chart/js/bootstrap.min.js>< / script>< script src =http://jhjanicki.github .io / reusable-line-chart / js / moment.js>< / script> <! -  D3  - >< script src =http://d3js.org/d3.v3.jscharset =utf-8>< / script> < ;!  - 下划线 - >< script src =https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js>< / script> ;< div class =containerid =main>< section class =predictionid =prediction1>< div class =row>< div class =col-lg -12>< div class =buttons>< button type =buttonclass =btn btn-default buttons1data-months =oneid =button30> 1个月< / button>< button type =buttonclass =btn btn-default selected-button buttons1data-months =threeid =button90> 3个月< / button>< button type =buttonclass =btn btn-default buttons1data-months =sixid =button180> 6个月< / button>< button type =buttonclass =btn btn-default buttons1data-months =twelveid =button360> 12个月< / button>< / div>< div id =graph1>< / div>< / div>< / div> <! -  end row  - >< div class =row>< div class =col-lg-12>< div class =buttons>< button type = buttonclass =btn btn-default buttons2data-months =oneid =button30-2> 1个月< / button>< button type =buttonclass =btn btn-default selected-button buttons2data-months =threeid =button90-2 3个月< / button>< button type =buttonclass =btn btn-default buttons2data-months =sixid =button180-2 6个月< / button>< button type =buttonclass =btn btn-default buttons2data-months =twelveid =button360-2 12个月< / button>< / div>< div id =graph2>< / div>< / div>< / div& <! -  end row  - > < / section> < / div> <! -  end container  - >  



我们通过您的 http://jhjanicki.github.io/reusable-line-chart/和看到app.js行号110 updateResults(first_day,last_day,product);此行将调用第一个图,并在行号206处创建 alreadyClicked 值为 true ,当您调用 updateResults2(first_day,last_day,product); 在行号151处,第二个图形的alreadyClicked值设置为 true else部分不会被执行,这一次它会尝试动画,但实际上没有这样的元素存在。我们必须为每个svg保持单独的剪辑路径,那么只有我们可以实现。尝试分析代码并理解。希望这是您要找的。如果不是问我:D


I am trying to make a line chart where two lines are drawn, one line is displayed before today's date and the other is displayed after today's date, for that I am using D3's clip path, and the users can click on buttons to update the chart. That part works but when I try to make that code reusable since I wanted a second chart below the first, and when I pass a second container to the same chart function (i.e. #graph1 for chart 1 and #graph2 for chart 2) it only draws the axes but not the lines for the second graph (the first one still works), could someone point me in the right direction? Thank you. Below is my code:

function chart(selection) {
       selection.each(function(data) {

      var svg = d3.select(this).selectAll("svg").data([data]);

      // Update the outer dimensions.
      svg.attr("width", width)
          .attr("height", height);

      // Update the inner dimensions.
      var g = svg.select("g")
          .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

      if(alreadyClicked==true){

         svg.select("#clip-before rect")
         .transition().duration(400)
         .attr("width", xScale(parsedate(today)))
         .attr("height", height- margin.top - margin.bottom);

         svg.select("#clip-after rect")
         .transition().duration(400)
         .attr("x", xScale(parsedate(today)))
         .attr("width", width-xScale(parsedate(today)))
         .attr("height", height- margin.top - margin.bottom);

         svg.selectAll("path.line-after")
         .data(data).transition()
         .duration(400).attr("d", line_after(data));

         svg.selectAll("path.line-before")
         .data(data).transition()
         .duration(400).attr("d", line_before(data));


      }else{
        g.append("clipPath")
              .attr("id", "clip-before")
              .append("rect")
              .attr("width", xScale(parsedate(today)))
              .attr("height", height- margin.top - margin.bottom);

         g.append("clipPath")
              .attr("id", "clip-after")
              .append("rect")
              .attr("x", xScale(parsedate(today)))
              .attr("width", width-xScale(parsedate(today)))
              .attr("height", height- margin.top - margin.bottom);

        g.selectAll(".line")
              .data(["after"])
              .enter().append("path")
              .attr("class", function(d) { return "line-" + d; })
              .attr("clip-path", function(d) { return "url(#clip-" + d + ")"; })
              .attr("d", line_after(data));

         g.selectAll(".line")
              .data(["before"])
              .enter().append("path")
              .attr("class", function(d) { return "line-" + d; })
              .attr("clip-path", function(d) { return "url(#clip-" + d + ")"; })
              .attr("d", line_before(data));


      }





    // Add the X Axis
      g.append("g")
            .attr("class", "x axis");


      svg.select(".x.axis")
            .attr("transform", "translate(0,300)").transition().duration(400).call(xAxis);

      g.selectAll(".x.axis text")  // select all the text elements for the xaxis
          .attr("transform", function(d) {
             return "translate(" + this.getBBox().height*-2 + "," + this.getBBox().height + ")rotate(-45)";
         });

      // Add the Y Axis
      g.append("g")
            .attr("class", "y axis"); 

      svg.select(".y.axis").transition().duration(400).call(yAxis);


  }); //end selection.each
}//end chart

解决方案

/**
* PredictGraph code start
*/
(function () {
	d3.PredictGraph = function() {
  	  // OPTIONS 
	  // (manipulated with getters and setters below)
	  var margin = {top: 50, right: 20, bottom: 100, left: 60},
		  width = getWidth($(window).width()),
		  height = 450,
		  xValue = function(d) { return d.date; },
		  yValue = function(d) { return d.actualvalue; },
		  yValue2= function(d) { return d.predictedvalue;},  
		  xScale = d3.time.scale().range([0, width]),
		  yScale = d3.scale.linear().range([height, 0]),
		  xAxis = xAxis = d3.svg.axis().scale(xScale)
			.orient("bottom").ticks(10).tickFormat(d3.time.format("%Y-%m-%d")), //this is where the date is formatted for the axis,
		  yAxis = d3.svg.axis().scale(yScale)
			.orient("left").ticks(10),
		  line = d3.svg.line().x(X).y(Y),
		  line_before= d3.svg.line()
				.x(function(d) { return xScale(d.date);})
				.y(function(d) { return yScale(d.actualvalue);
							}),
		  line_after= d3.svg.line()
				.x(function(d) { return xScale(d.date);})
				.y(function(d) { return yScale(d.predictedvalue);})
		  today= moment()._d, //date object
		  formatDate = d3.time.format("%Y-%m-%d"),
		  todayFormatted = formatDate(moment()._d),
		  alreadyClicked=false;
	  function chart(selection) {
		selection.each(function(data) {
		console.log(this);
		var max_actual = d3.max(data, function(d) { return d.actualvalue;} ); //before
		var max_predicted = d3.max(data, function(d) { return d.predictedvalue;} ); //after
		var max = Math.max(max_actual, max_predicted); //overall
		var min_actual = d3.min(data, function(d) { return d.actualvalue;} ); //before
		var min_predicted = d3.min(data, function(d) { return d.predictedvalue;} ); //after
		var min = Math.min(min_actual, min_predicted);			  
		var parsedate = d3.time.format("%Y-%m-%d").parse;		
		// to convert date from a formated string into a date object
			data.forEach(function(d) {d.date = parsedate(d.date);});  			
		  // Update the x-scale.
		  xScale.domain(d3.extent(data, function(d) { return d.date; })).range([0, width- margin.left - margin.right]);
		  // Update the y-scale.
		  yScale.domain([min,max]).range([height- margin.top - margin.bottom,0]);
		  // Select the svg element, if it exists.
		  var svg = d3.select(this).selectAll("svg").data([data]);
		//Otherwise, create the skeletal chart.
		  var gEnter = svg.enter().append("svg").append("g");
		  // Update the outer dimensions.
		  svg.attr("width", width).attr("height", height);
		  // Update the inner dimensions.
		  var g = svg.select("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");		
		 console.log(svg.select("#clip-before"+this.id));
          var currNode=this;				  
		  if(svg.select("#clip-before"+this.id)[0][0]!=null){		  	
		     svg.select("#clip-before"+this.id+" rect")
		     .transition().duration(400)
		     .attr("width", xScale(parsedate(today)))
			 .attr("height", height- margin.top - margin.bottom);
			 
			 svg.select("#clip-after"+this.id+" rect")
		     .transition().duration(400)
		     .attr("x", xScale(parsedate(today)))
		     .attr("width", width-xScale(parsedate(today)))
			 .attr("height", height- margin.top - margin.bottom);
		  	
		  	 svg.selectAll("path.line-after")
		  	 .data(data).transition()
             .duration(400).attr("d", line_after(data));
             
             svg.selectAll("path.line-before")
		  	 .data(data).transition()
             .duration(400).attr("d", line_before(data));
             
             svg.select("line.today")
		     .transition().duration(400)
		     .attr({'x1': xScale(parsedate(today)),'y1': 0,'x2': xScale(parsedate(today)),'y2': height- margin.top - margin.bottom})
				.style("stroke", "#FF7F66")
				.style("stroke-dasharray", ("3, 3")) 
				.style("fill", "none");	             
		  }else{
		  	g.append("clipPath")
				  .attr("id", "clip-before"+this.id)
				  .append("rect")
				  .attr("width", xScale(parsedate(today)))
				  .attr("height", height- margin.top - margin.bottom);
				  
			 g.append("clipPath")
				  .attr("id", "clip-after"+this.id)
				  .append("rect")
				  .attr("x", xScale(parsedate(today)))
				  .attr("width", width-xScale(parsedate(today)))
				  .attr("height", height- margin.top - margin.bottom);
				  
		  	g.selectAll(".line")
				  .data(["after"])
				  .enter().append("path")
				  .attr("class", function(d) { return "line-" + d; })
				  .attr("clip-path", function(d) { return "url(#clip-"+d+currNode.id+")"; })
				  .attr("d", line_after(data));
				  
			 g.selectAll(".line")
				  .data(["before"])
				  .enter().append("path")
				  .attr("class", function(d) { return "line-" + d; })
				  .attr("clip-path", function(d) { return "url(#clip-"+d+currNode.id+")"; })
				  .attr("d", line_before(data));
				  
			g.append("line")
				.attr("class","today")
				.attr({
					'x1': xScale(parsedate(today)),
					'y1': 0,
					'x2': xScale(parsedate(today)),
					'y2': height- margin.top - margin.bottom
				})
				.style("stroke", "#FF7F66")
				.style("stroke-dasharray", ("3, 3")) 
				.style("fill", "none");			
		  }
		// Add the X Axis
		  g.append("g")
				.attr("class", "x axis");
				//.call(xAxis);
				
		  svg.select(".x.axis")
 				.attr("transform", "translate(0,300)").transition().duration(400).call(xAxis);
 		        
		  g.selectAll(".x.axis text")  // select all the text elements for the xaxis
			  .attr("transform", function(d) {
				 return "translate(" + this.getBBox().height*-2 + "," + this.getBBox().height + ")rotate(-45)";
			 });

		  // Add the Y Axis
		  g.append("g")
 				.attr("class", "y axis"); //.call(yAxis)
			
		  svg.select(".y.axis").transition().duration(400).call(yAxis);		
	       alreadyClicked = true;
		});
	  }

	  // The x-accessor for the path generator; xScale ∘ xValue.
	  function X(d) {
		return xScale(formatDate.parse(d.date));
	  }

	  // The y-accessor for the path generator; yScale ∘ yValue.
	  function Y(d) {
		return yScale(d.actualvalue);
	  }
  
	  function Y2(d){
		return yScale(d.predictedvalue);
	  }

	  chart.margin = function(_) {
		if (!arguments.length) return margin;
		margin = _;
		return chart;
	  };

	  chart.width = function(_) {
		if (!arguments.length) return width;
		width = _;
		return chart;
	  };

	  chart.height = function(_) {
		if (!arguments.length) return height;
		height = _;
		return chart;
	  };

	  chart.x = function(_) {
		if (!arguments.length) return xValue;
		xValue = _;
		return chart;
	  };

	  chart.y = function(_) {
		if (!arguments.length) return yValue;
		yValue = _;
		return chart;
	  };
	  function getWidth(width){
	
			if (width > 1500 || width < 990) {
				return 800;
			}else if ( width > 1300 && width > 990){
				return 700;
			}else{
				return 650;
			}
		}  
	   return chart;
	};
})();

/*PredictGraph code end*/


/*app.js code start*/

		var chart = d3.PredictGraph();	
		//Global Variables
		
		var formatDate = d3.time.format("%Y-%m-%d");
		var today = formatDate(moment()._d) || "2016-01-01";
		var first_day = formatDate(getFirstDay()) || "2015-01-01";
		var numMonths = "three";  //one, three, six, twelve
		var last_day = formatDate(getLastDay(numMonths)) || "2016-04-01";
		var product = "product1";
		var loaded = false;
		// called everytime a user clicks on a month button or select a dropdown menu item
		// input params: the firstDay (today minus 12 months), lastDay (pass value retrieved from numMonths into getLastDay)
		// 				 productSelected (selected product based on dropdown menu value)
		function updateResults(firstDay, lastDay, productSelected){
		
			//get data... will get from Django eventually instead of json file
			//d3.json("data/data.json",function(data){	
          var data=[{"date":"2015-01-01","productname":"product1","actualvalue":512,"predictedvalue":05},{"date":"2015-02-01","productname":"product1","actualvalue":311,"predictedvalue":426},{"date":"2015-03-01","productname":"product1","actualvalue":305,"predictedvalue":725},{"date":"2015-04-01","productname":"product1","actualvalue":396,"predictedvalue":887},{"date":"2015-05-01","productname":"product1","actualvalue":70,"predictedvalue":299},{"date":"2015-06-01","productname":"product1","actualvalue":32,"predictedvalue":40},{"date":"2015-07-01","productname":"product1","actualvalue":157,"predictedvalue":504},{"date":"2015-08-01","productname":"product1","actualvalue":335,"predictedvalue":372},{"date":"2015-09-01","productname":"product1","actualvalue":426,"predictedvalue":899},{"date":"2015-10-01","productname":"product1","actualvalue":983,"predictedvalue":842},{"date":"2015-11-01","productname":"product1","actualvalue":701,"predictedvalue":569},{"date":"2015-12-01","productname":"product1","actualvalue":674,"predictedvalue":387},{"date":"2016-01-01","productname":"product1","actualvalue":986,"predictedvalue":65},{"date":"2016-02-01","productname":"product1","actualvalue":426,"predictedvalue":49},{"date":"2016-03-01","productname":"product1","actualvalue":130,"predictedvalue":63},{"date":"2016-04-01","productname":"product1","actualvalue":674,"predictedvalue":889},{"date":"2016-05-01","productname":"product1","actualvalue":192,"predictedvalue":983},{"date":"2016-06-01","productname":"product1","actualvalue":6,"predictedvalue":186},{"date":"2016-07-01","productname":"product1","actualvalue":476,"predictedvalue":181},{"date":"2016-08-01","productname":"product1","actualvalue":493,"predictedvalue":544},{"date":"2016-09-01","productname":"product1","actualvalue":121,"predictedvalue":792},{"date":"2016-10-01","productname":"product1","actualvalue":745,"predictedvalue":712},{"date":"2016-11-01","productname":"product1","actualvalue":755,"predictedvalue":46},{"date":"2016-12-01","productname":"product1","actualvalue":361,"predictedvalue":810},{"date":"2015-01-01","productname":"product2","actualvalue":675,"predictedvalue":722},{"date":"2015-02-01","productname":"product2","actualvalue":690,"predictedvalue":388},{"date":"2015-03-01","productname":"product2","actualvalue":994,"predictedvalue":871},{"date":"2015-04-01","productname":"product2","actualvalue":678,"predictedvalue":656},{"date":"2015-05-01","productname":"product2","actualvalue":901,"predictedvalue":216},{"date":"2015-06-01","productname":"product2","actualvalue":304,"predictedvalue":491},{"date":"2015-07-01","productname":"product2","actualvalue":223,"predictedvalue":360},{"date":"2015-08-01","productname":"product2","actualvalue":299,"predictedvalue":487},{"date":"2015-09-01","productname":"product2","actualvalue":80,"predictedvalue":267},{"date":"2015-10-01","productname":"product2","actualvalue":760,"predictedvalue":830},{"date":"2015-11-01","productname":"product2","actualvalue":122,"predictedvalue":273},{"date":"2015-12-01","productname":"product2","actualvalue":859,"predictedvalue":258},{"date":"2016-01-01","productname":"product2","actualvalue":807,"predictedvalue":729},{"date":"2016-02-01","productname":"product2","actualvalue":82,"predictedvalue":939},{"date":"2016-03-01","productname":"product2","actualvalue":72,"predictedvalue":348},{"date":"2016-04-01","productname":"product2","actualvalue":544,"predictedvalue":429},{"date":"2016-05-01","productname":"product2","actualvalue":512,"predictedvalue":562},{"date":"2016-06-01","productname":"product2","actualvalue":510,"predictedvalue":800},{"date":"2016-07-01","productname":"product2","actualvalue":283,"predictedvalue":847},{"date":"2016-08-01","productname":"product2","actualvalue":172,"predictedvalue":343},{"date":"2016-09-01","productname":"product2","actualvalue":315,"predictedvalue":633},{"date":"2016-10-01","productname":"product2","actualvalue":863,"predictedvalue":184},{"date":"2016-11-01","productname":"product2","actualvalue":125,"predictedvalue":623},{"date":"2016-12-01","productname":"product2","actualvalue":400,"predictedvalue":920}];
				// get data between first and last day and for the selected product
				// draw a single line as a test
				var test_data = data.filter(function(el){
					return el.date < lastDay && el.date > firstDay && el.productname == productSelected
				});
			d3.select("#graph1").datum(test_data).call(chart);
			//});
		}
		
		function updateResults2(firstDay, lastDay, productSelected){ 
			//get data... will get from Django eventually instead of json file
			//d3.json("data/data.json",function(data){
		var data=[{"date":"2015-01-01","productname":"product1","actualvalue":512,"predictedvalue":805},{"date":"2015-02-01","productname":"product1","actualvalue":311,"predictedvalue":426},{"date":"2015-03-01","productname":"product1","actualvalue":305,"predictedvalue":725},{"date":"2015-04-01","productname":"product1","actualvalue":396,"predictedvalue":887},{"date":"2015-05-01","productname":"product1","actualvalue":70,"predictedvalue":299},{"date":"2015-06-01","productname":"product1","actualvalue":32,"predictedvalue":40},{"date":"2015-07-01","productname":"product1","actualvalue":157,"predictedvalue":504},{"date":"2015-08-01","productname":"product1","actualvalue":335,"predictedvalue":372},{"date":"2015-09-01","productname":"product1","actualvalue":426,"predictedvalue":899},{"date":"2015-10-01","productname":"product1","actualvalue":983,"predictedvalue":842},{"date":"2015-11-01","productname":"product1","actualvalue":701,"predictedvalue":569},{"date":"2015-12-01","productname":"product1","actualvalue":674,"predictedvalue":387},{"date":"2016-01-01","productname":"product1","actualvalue":986,"predictedvalue":65},{"date":"2016-02-01","productname":"product1","actualvalue":426,"predictedvalue":49},{"date":"2016-03-01","productname":"product1","actualvalue":130,"predictedvalue":63},{"date":"2016-04-01","productname":"product1","actualvalue":674,"predictedvalue":889},{"date":"2016-05-01","productname":"product1","actualvalue":192,"predictedvalue":983},{"date":"2016-06-01","productname":"product1","actualvalue":6,"predictedvalue":186},{"date":"2016-07-01","productname":"product1","actualvalue":476,"predictedvalue":181},{"date":"2016-08-01","productname":"product1","actualvalue":493,"predictedvalue":544},{"date":"2016-09-01","productname":"product1","actualvalue":121,"predictedvalue":792},{"date":"2016-10-01","productname":"product1","actualvalue":745,"predictedvalue":712},{"date":"2016-11-01","productname":"product1","actualvalue":755,"predictedvalue":46},{"date":"2016-12-01","productname":"product1","actualvalue":361,"predictedvalue":810},{"date":"2015-01-01","productname":"product2","actualvalue":675,"predictedvalue":722},{"date":"2015-02-01","productname":"product2","actualvalue":690,"predictedvalue":388},{"date":"2015-03-01","productname":"product2","actualvalue":994,"predictedvalue":871},{"date":"2015-04-01","productname":"product2","actualvalue":678,"predictedvalue":656},{"date":"2015-05-01","productname":"product2","actualvalue":901,"predictedvalue":216},{"date":"2015-06-01","productname":"product2","actualvalue":304,"predictedvalue":491},{"date":"2015-07-01","productname":"product2","actualvalue":223,"predictedvalue":360},{"date":"2015-08-01","productname":"product2","actualvalue":299,"predictedvalue":487},{"date":"2015-09-01","productname":"product2","actualvalue":80,"predictedvalue":267},{"date":"2015-10-01","productname":"product2","actualvalue":760,"predictedvalue":830},{"date":"2015-11-01","productname":"product2","actualvalue":122,"predictedvalue":273},{"date":"2015-12-01","productname":"product2","actualvalue":859,"predictedvalue":258},{"date":"2016-01-01","productname":"product2","actualvalue":807,"predictedvalue":729},{"date":"2016-02-01","productname":"product2","actualvalue":82,"predictedvalue":939},{"date":"2016-03-01","productname":"product2","actualvalue":72,"predictedvalue":348},{"date":"2016-04-01","productname":"product2","actualvalue":544,"predictedvalue":429},{"date":"2016-05-01","productname":"product2","actualvalue":512,"predictedvalue":562},{"date":"2016-06-01","productname":"product2","actualvalue":510,"predictedvalue":800},{"date":"2016-07-01","productname":"product2","actualvalue":283,"predictedvalue":847},{"date":"2016-08-01","productname":"product2","actualvalue":172,"predictedvalue":343},{"date":"2016-09-01","productname":"product2","actualvalue":315,"predictedvalue":633},{"date":"2016-10-01","productname":"product2","actualvalue":863,"predictedvalue":184},{"date":"2016-11-01","productname":"product2","actualvalue":125,"predictedvalue":623},{"date":"2016-12-01","productname":"product2","actualvalue":400,"predictedvalue":920}];
				
				// get data between first and last day and for the selected product
				// draw a single line as a test
				var test_data = data.filter(function(el){
					return el.date < lastDay && el.date > firstDay && el.productname == productSelected
				});
				d3.select("#graph2").datum(test_data).call(chart);		
			//});
		}
		$(document).ready(function(){		
			$("#button30").click( function(){
				$(".buttons1").removeClass("selected-button");
				$("#button30").addClass("selected-button");
				numMonths= $("#button30").data('months'); 
				first_day=formatDate(getFirstDay());
				last_day=formatDate(getLastDay(numMonths)); 
				updateResults(first_day,last_day,product);
			});
			
			$("#button90").click( function(){
				$(".buttons1").removeClass("selected-button");
				$("#button90").addClass("selected-button");
				numMonths= $("#button90").data('months');
				first_day=formatDate(getFirstDay());
				last_day=formatDate(getLastDay(numMonths)); 
				updateResults(first_day,last_day,product);
			});
			
			$("#button180").click( function(){
				$(".buttons1").removeClass("selected-button");
				$("#button180").addClass("selected-button");
				numMonths= $("#button180").data('months');
				first_day=formatDate(getFirstDay());
				last_day=formatDate(getLastDay(numMonths)); 
				updateResults(first_day,last_day,product);
			});
			
			$("#button360").click( function(){
				$(".buttons1").removeClass("selected-button");
				$("#button360").addClass("selected-button");
				numMonths= $("#button360").data('months');
				first_day=formatDate(getFirstDay());
				last_day=formatDate(getLastDay(numMonths)); 
				updateResults(first_day,last_day,product);
			});
			
			updateResults(first_day,last_day,product);
			$("#button30-2").click( function(){
				$(".buttons2").removeClass("selected-button");
				$("#button30-2").addClass("selected-button");
				numMonths= $("#button30-2").data('months');
				first_day=formatDate(getFirstDay());
				last_day=formatDate(getLastDay(numMonths)); 
				updateResults2(first_day,last_day,product);
			});
			
			$("#button90-2").click( function(){
				$(".buttons2").removeClass("selected-button");
				$("#button90-2").addClass("selected-button");
				numMonths= $("#button90-2").data('months');
				first_day=formatDate(getFirstDay());
				last_day=formatDate(getLastDay(numMonths));
				updateResults2(first_day,last_day,product);
			});
			
			$("#button180-2").click( function(){
				$(".buttons2").removeClass("selected-button");
				$("#button180-2").addClass("selected-button");
				numMonths= $("#button180-2").data('months'); 
				first_day=formatDate(getFirstDay());
				last_day=formatDate(getLastDay(numMonths));  
				updateResults2(first_day,last_day,product);
			});
			
			$("#button360-2").click( function(){
				$(".buttons2").removeClass("selected-button");
				$("#button360-2").addClass("selected-button");
				numMonths= $("#button360-2").data('months'); //or .data()
				first_day=formatDate(getFirstDay());
				last_day=formatDate(getLastDay(numMonths));  // to set last day
				updateResults2(first_day,last_day,product);
			});
			
			updateResults2(first_day,last_day,product);

   		});
			// function to set last day date... basically today + 1, 3, 6, or 12 months, default is 3
			function getLastDay(months){ 

				switch(months){
					case "one": 
				
						return moment().add(1, 'months')._d; 
					break;
					case"six": 
				
						return moment().add(6, 'months')._d; 
					break;
					case"twelve":  
					
						return moment().add(12, 'months')._d; 
					break; 
					default: 
						return moment().add(3, 'months')._d; 
				};

			}
			
			// function to set first day date... basically today - 12
			function getFirstDay(){
				return moment().subtract(12,'months')._d;
			}

.container{
  margin-top:50px;}
html, body {
  height: 100%; font-family: 'Open Sans', sans-serif;}
.wrap{
  min-heigh:100%;
}
.prediction{
  height:700px;
}
.footer{
  width:100%;
  position:fixed;
  bottom:0px;padding:0px;text-align:center;}
.footer p{margin-left:auto;margin-right:auto;}
    .selected-button{background-color: black;border-color: black;color:white;}
  
	.axis path,
	.axis line {
		fill: none;
		stroke: #000;
		shape-rendering: crispEdges;
	}
	.x.axis path {}	.line {
		fill: none;
		stroke: steelblue;
		stroke-width: 1.5px;
	}

	.line-before {
		stroke:#2185C5;
		fill: none;
	}
	.line-after{
		stroke:#FFB779;
		fill: none;
	}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.3/jquery.easing.min.js"></script>
    <script src="http://jhjanicki.github.io/reusable-line-chart/js/bootstrap.min.js"></script>
<script src="http://jhjanicki.github.io/reusable-line-chart/js/moment.js"></script>
    <!-- D3 --><script src="http://d3js.org/d3.v3.js" charset="utf-8"></script>
   	<!-- underscore --><script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<div class="container" id="main">
<section class="prediction" id="prediction1">
<div class="row">
<div class="col-lg-12">
<div class="buttons">
<button type="button" class="btn btn-default buttons1" data-months="one" id="button30"> 1 Month</button>
<button type="button" class="btn btn-default selected-button buttons1" data-months="three" id="button90"> 3 Months</button>
<button type="button" class="btn btn-default buttons1" data-months="six" id="button180"> 6 Months</button>
<button type="button" class="btn btn-default buttons1" data-months="twelve" id="button360"> 12 Months</button>
</div>
<div id="graph1">
</div>
</div>
</div> <!-- end row -->
<div class="row">
<div class="col-lg-12">
<div class="buttons">
<button type="button" class="btn btn-default buttons2" data-months="one" id="button30-2"> 1 Month</button>
<button type="button" class="btn btn-default selected-button buttons2" data-months="three" id="button90-2"> 3 Months</button>
<button type="button" class="btn btn-default buttons2" data-months="six" id="button180-2"> 6 Months</button>
<button type="button" class="btn btn-default buttons2" data-months="twelve" id="button360-2"> 12 Months</button>
</div>
<div id="graph2"></div>
</div></div> <!-- end row -->
  </section>	
	</div> <!-- end container -->

I've gone through your http://jhjanicki.github.io/reusable-line-chart/ and seen app.js line number 110 updateResults(first_day,last_day,product); this line will invoke for first graph and makes alreadyClicked value to true at line number 206, and when you invoke updateResults2(first_day,last_day,product); at line number 151 for second graph 'alreadyClicked' value is set to true earlier now else part will not be executed and this time it'll try to animate, but actually there is no such element exists. We have to maintain separate clip-path for each svg, then only we can achieve that. Try to analyze the code and understand. Hope this is what you are looking for. If not ask me :D

这篇关于第二次调用D3可重用线图只绘制轴而不是线的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆