2013-06-27 51 views
1

我是JavaScript和D3的新手。堆疊面積圖在百分比刻度和絕對刻度之間切換的更好方法是什麼?

我試圖創造出一個絕對規模和百分比規模的數據之間切換堆疊面積圖。我意識到,這涉及到與

d3.layout.stack()玩耍。偏移量( 「零」)和 d3.layout.stack()。偏移量( 「展開」)。

我已經成功地得到的圖表做我想做的:

http://jsfiddle.net/dSJ4E/

...但我不是我的做法感到自豪,相信有一個更好的方法來做到這一點。有任何想法嗎?有沒有你可能知道的簡單例子?

我,因爲在建立我的if/else語句很不滿意我的代碼,我只是重新聲明的一切,我之前寫的話,只改變偏移變量。這看起來像一個笨重的方法。另外,我不認爲這會允許我設置轉換。

data = [{"type": "Group1", 
      "values": [ 
       {"x":0, "y": 2.5}, 
       {"x":1, "y": 2.4}, 
       {"x":2, "y": 0.3}]}, 
      {"type": "Group2", 
      "values": [ 
       {"x":0, "y": 1.5}, 
       {"x":1, "y": 1.3}, 
       {"x":2, "y": 1.1}]} 
      ]; 
var stackZero = d3.layout.stack() 
     .values(function(d){return d.values;}) 
     .offset("zero"); 

stackZero(data); 
var xScale = d3.scale.linear() 
     .domain([0,2]) 
     .range([0, width]); 

    var yScale = d3.scale.linear() 
     .range([height,0]) 
     .domain([0, d3.max(data, function(d){return d3.max(d.values, function(d){return d.y0 + d.y;});})]); 

    var area = d3.svg.area() 
     .x(function(d){return xScale(d.x);}) 
     .y0(function(d){return yScale(d.y0);}) 
     .y1(function(d){return yScale(d.y0 + d.y);});  

    var svg = d3.selectAll("body") 
     .append("svg") 
     .attr("width", width + margin.left + margin.right) 
     .attr("height", height + margin.top + margin.bottom) 
     .append("g") 
     .attr("transform", "translate(" + margin.left + "," + margin.top +")"); 

    svg.selectAll(".layers") 
     .data(data) 
     .enter() 
     .append("path") 
     .attr("class", "layer") 
     .attr("d", function(d){return area(d.values);}) 
     .style("fill", function (d,i){return colors(i)}); 

    var yAxis = d3.svg.axis() 
     .scale(yScale) 
     .orient("left"); 

    svg.append("g") 
     .attr("class", "y axis") 
     .attr("transform", "translate(0,0)") 
     .call(yAxis); 

    d3.select("p") //now we start to interact with the chart 
     .on("click", function() { 

      console.log("entering variable is " + stackType); 

      svg.selectAll("path").data([]).exit().remove(); 
      svg.selectAll(".y.axis").data([]).exit().remove(); 
if(stackType){ //enter true, or expanded data 

      var stackExpand = d3.layout.stack() 
       .values(function(d){return d.values;}) 
       .offset("expand"); 

      stackExpand(data); 

      console.log(data); 

      var yScale = d3.scale.linear() 
       .range([height,0]) 
       .domain([0, d3.max(data, function(d){return d3.max(d.values, function(d){return d.y0 + d.y;});})]); 

      var area = d3.svg.area() 
       .x(function(d){return xScale(d.x);}) 
       .y0(function(d){return yScale(d.y0);}) 
       .y1(function(d){return yScale(d.y0 + d.y);}); 

      svg.selectAll(".layers") 
       //.data(stackZero(data)) 
       .data(stackExpand(data)) 
       .enter() 
       .append("path") 
       .attr("class", "layer") 
       .attr("d", function(d){return area(d.values);}) 
       .style("fill", function (d,i){return colors(i)}); 

      formatter = d3.format(".0%"); 

      var yAxis = d3.svg.axis() 
       .scale(yScale) 
       .orient("left") 
       .tickFormat(formatter); 

      svg.append("g") 
       .attr("class", "y axis") 
       .attr("transform", "translate(0,0)") 
       .call(yAxis); 


      stackType = false; 
      console.log("exiting variable is " + stackType); 

      } else { //enter false, or zero data 

      data = [{"type": "Group1", 
      "values": [ 
       {"x":0, "y": 2.5}, 
       {"x":1, "y": 2.4}, 
       {"x":2, "y": 0.3}]}, 
      {"type": "Group2", 
      "values": [ 
       {"x":0, "y": 1.5}, 
       {"x":1, "y": 1.3}, 
       {"x":2, "y": 1.1}]} 
      ]; 

      var stackZero = d3.layout.stack() 
       .values(function(d){return d.values;}) 
       .offset("zero"); 

      stackZero(data); 

      console.log(data); 

      var yScale = d3.scale.linear() 
       .range([height,0]) 
       .domain([0, d3.max(data, function(d){return d3.max(d.values, function(d){return d.y0 + d.y;});})]); 

      var area = d3.svg.area() 
       .x(function(d){return xScale(d.x);}) 
       .y0(function(d){return yScale(d.y0);}) 
       .y1(function(d){return yScale(d.y0 + d.y);}); 

      svg.selectAll(".layers") 
       .data(stackZero(data)) 
       //.data(stackExpand(data)) 
       .enter() 
       .append("path") 
       .attr("class", "layer") 
       .attr("d", function(d){return area(d.values);}) 
       .style("fill", function (d,i){return colors(i)}); 

      stackType = true; 
      console.log("exiting variable is" + stackType); 

      var yAxis = d3.svg.axis() 
       .scale(yScale) 
       .orient("left"); 

      svg.append("g") 
       .attr("class", "y axis") 
       .attr("transform", "translate(0,0)") 
       .call(yAxis); 

      }; 

}); 
    </script> 
+0

你見過[這](http://nvd3.org/ghpages/stackedArea.html)?你可以按原樣使用它,或者查看源代碼以獲得靈感。 –

+0

是的,這些功能正是我感興趣的內容。不幸的是,代碼似乎比我目前的功能略勝一籌。 – jpkoning

回答

1

所以有很多事情可以做,以避免在代碼中重複自己。首先,您正在使用的很多D3功能都可以重複使用 - 它們是一般功能。這與javascript閉包的概念一起意味着您只需聲明一次並初始化不會更改的部分。

堆棧佈局不覆蓋Y和Y0值,因此得到切換工作,我改名爲你的初始數據raw_x和raw_y和適當調整的訪問者。

然後搞清楚yScale域和實際更新我裹成一個功能路徑的工作:

function drawChart() { 
    yScale.domain([0, d3.max(data, function (d) { 
     return d3.max(d.values, function (d) { 
      return d.y0 + d.y; 
     }); 
    })]); 

    // new data items need to get added 
    var areas = svg.selectAll(".layer") 
    .data(data, function(d) { return d.type; }); 

    areas.enter() 
     .append("path") 
     .attr("class", "layer") 
     .style("fill", function (d, i) { 
      return colors(i) 
    }); 

    // Added and updated items need to be updated 
    areas.attr("d", function (d) { 
     return area(d.values); 
    }) 

    // Old items need to be removed - we should not actually need this with the data as it is 
    areas.exit().remove(); 

    svg.selectAll("g.y.axis").call(yAxis); 
} 

這真的只是一個開始,你將要根據您的需求進行重組,而是採取了看一個updated fiddle

+0

太棒了。謝謝。 – jpkoning

+0

我發現我可以縮短'VAR區域= svg.selectAll數據(數據,函數(d){返回d.type;});( 「層」)。'通過去除'函數(d){返回d。類型; }'它仍然有效。你是否有這個理由? – jpkoning

+0

這是否意味着在未來的應用程序中,我應該避免對數據使用標籤「x」和「y」?畢竟,由於「x」和「y」已經被svg.stack.layout使用(你提到堆棧會覆蓋值),那麼包含這些標籤的數據似乎會增加不必要的混淆。 – jpkoning