2016-03-03 36 views
0

我是d3的新手,但對HighCharts api非常熟悉。d3 - 帶自定義DOM元素的多重覆蓋圖表

我見過很多在同一頁上的多個D3圖表的例子;但似乎無法找到一個圖表疊加/坐在另一個圖表之上的示例。這可能嗎?

使用HighCharts,您可以在plotOptions配置對象中定義多個圖表類型。有沒有類似的D3?或者,你怎麼能用d3做到這一點?

我希望在條形圖上有一個線條圖。根據這些數據,會有不同的「階段」,所以一些酒吧可能不活動/空閒。

此外,我需要顯示一個指示器來顯示「舞臺」當前的位置;並確保這是全部響應。

例(粗略樣機):

desired chart/s mockup

研究D3和尋找類似的例子之後,我想,也許D3是不是我們的最佳選擇;也許一個自定義的CSS/JS/HTML解決方案(在角度應用程序內)會更好。

任何建議或指針將不勝感激。

+2

你絕對可以做到這一點。可以說,d3正是實現你所說的「自定義CSS/JS/HTML」解決方案的方式。與HighCharts不同,d3不是將配置傳遞到預定義圖表對象的框架。使用d3,您可以從頭開始構建圖表。這很像你會用jQuery來做,除了d3更適合渲染數據驅動的圖形。因此,如果您可以瞭解如何使用d3渲染趨勢線,以及如何使用d3渲染條形圖,那麼您可以通過將每個渲染到單個SVG中的每個容器中,將它們放入其自己的''容器中。 – meetamit

+0

c3.js是一個基於d3的圖表庫,可以覆蓋某些圖表。例如,請參閱http://c3js.org/samples/chart_combination.html。 – mgraham

+0

謝謝大家。將花一些時間在這個;我們可能會簡化這個以及版本1. –

回答

2

這裏有一個快速實物模型從這個excellent bar chart例子開始:

<!DOCTYPE html> 
 
<meta charset="utf-8"> 
 
<style> 
 

 
.point rect { 
 
    fill: steelblue; 
 
} 
 

 
.point circle { 
 
    fill: orange; 
 
} 
 

 
.point rect:hover { 
 
    fill: brown; 
 
} 
 

 
.axis { 
 
    font: 10px sans-serif; 
 
} 
 

 
.axis path, 
 
.axis line { 
 
    fill: none; 
 
    stroke: #000; 
 
    shape-rendering: crispEdges; 
 
} 
 

 
.x.axis path { 
 
    display: none; 
 
} 
 

 
.line { 
 
    fill: none; 
 
    stroke: orange 
 
} 
 

 
</style> 
 
<body> 
 
<script src="//d3js.org/d3.v3.min.js"></script> 
 
<script> 
 

 
var margin = {top: 75, right: 20, bottom: 30, left: 40}, 
 
    width = 600 - margin.left - margin.right, 
 
    height = 500 - margin.top - margin.bottom; 
 

 
var x = d3.scale.ordinal() 
 
    .rangeRoundBands([0, width], .1); 
 

 
var y = d3.scale.linear() 
 
    .range([height, 0]); 
 

 
var xAxis = d3.svg.axis() 
 
    .scale(x) 
 
    .orient("bottom"); 
 

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

 
var svg = d3.select("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 + ")"); 
 

 

 
    var data = "abcdefghijklmnopqrstuvwxyz".split("").map(function(d){ 
 
    return { 
 
     letter: d, 
 
     bar: Math.random() * 10, 
 
     line: Math.random() * 10 
 
    }; 
 
    }) 
 
    
 
    x.domain(data.map(function(d) { return d.letter; })); 
 
    y.domain([0, d3.max(data, function(d) { return d3.max([d.bar, d.line]); })]); 
 

 
    svg.append("g") 
 
     .attr("class", "x axis") 
 
     .attr("transform", "translate(0," + height + ")") 
 
     .call(xAxis); 
 

 
    svg.append("g") 
 
     .attr("class", "y axis") 
 
     .call(yAxis) 
 
    .append("text"); 
 

 
    var points = svg.selectAll(".point") 
 
     .data(data) 
 
    .enter().append("g") 
 
     .attr("class", "point"); 
 
    
 
    points.append('rect') 
 
     .attr("x", function(d) { return x(d.letter); }) 
 
     .attr("width", x.rangeBand()) 
 
     .attr("y", function(d) { return y(d.bar); }) 
 
     .attr("height", function(d) { return height - y(d.bar); }); 
 
     
 
    points.append('circle') 
 
     .attr("r", 5) 
 
     .attr("cx", function(d){ return x(d.letter) + x.rangeBand()/2; }) 
 
     .attr("cy", function(d){ return y(d.line)}); 
 
     
 
    var line = d3.svg.line() 
 
    .x(function(d) { return x(d.letter) + x.rangeBand()/2; }) 
 
    .y(function(d) { return y(d.line); }); 
 

 
    svg.append("path") 
 
    .attr("class", "line") 
 
    .datum(data) 
 
    .attr("d", line); 
 
    
 
    var indicator = svg.append("g") 
 
    .attr("r", 5) 
 
    .attr("transform", "translate(" + (x("q") + x.rangeBand()/2) + "," + -20 + ")"); 
 
    
 
    indicator.append("circle") 
 
    .attr("r", 40) 
 
    .style("fill", "red"); 
 
    
 
    indicator.append("text") 
 
    .text("!") 
 
    .style("fill", "white") 
 
    .style("text-anchor", "middle") 
 
    .style("alignment-baseline", "middle") 
 
    .style("font-size", 70); 
 
    
 
    indicator.append("line") 
 
    .attr("y1", 20) 
 
    .attr("y2", height + 20) 
 
    .attr("x1", 0) 
 
    .attr("x2", 0) 
 
    .style("stroke", "red") 
 
    .style("stroke-width", "4px"); 
 

 
</script>


新的解決方案基於評論

鑑於你的輸入數據,這裏有一個新的例子。我在這裏遇到了一些問題,所以請在任何混淆的位上提問。我試圖把它註釋掉:

<!DOCTYPE html> 
 
<meta charset="utf-8"> 
 
<style> 
 
    rect { 
 
    fill: steelblue; 
 
    } 
 
    
 
circle { 
 
    fill: orange; 
 
    } 
 
    
 
    rect:hover { 
 
    fill: brown; 
 
    } 
 
    
 
    .axis { 
 
    font: 10px sans-serif; 
 
    } 
 
    
 
    .axis path, 
 
    .axis line { 
 
    fill: none; 
 
    stroke: #000; 
 
    shape-rendering: crispEdges; 
 
    } 
 
    
 
    .x.axis path { 
 
    display: none; 
 
    } 
 
    
 
    .line { 
 
    fill: none; 
 
    stroke: orange 
 
    } 
 
</style> 
 

 
<body> 
 
    <script src="//d3js.org/d3.v3.min.js"></script> 
 
    <script> 
 
    var margin = { 
 
     top: 75, 
 
     right: 20, 
 
     bottom: 30, 
 
     left: 40 
 
     }, 
 
     width = 600 - margin.left - margin.right, 
 
     height = 500 - margin.top - margin.bottom; 
 

 
    var x = d3.scale.ordinal() 
 
     .rangeRoundBands([0, width], .1); 
 

 
    var y = d3.scale.linear() 
 
     .range([height, 0]); 
 

 
    var xAxis = d3.svg.axis() 
 
     .scale(x) 
 
     .orient("bottom"); 
 

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

 
    var svg = d3.select("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 + ")"); 
 

 
    // here's your data 
 
    var data = 
 
    { 
 
     'point1': [{ 
 
     'value': 50 
 
     }, { 
 
     'value': 100 
 
     }, { 
 
     'value': 100 
 
     }, { 
 
     'value': 150 
 
     }], 
 
     'point2': [{ 
 
     'value': 25 
 
     }, { 
 
     'value': 40 
 
     }, { 
 
     'value': 60 
 
     }], 
 
     'point3': [{ 
 
     'value': 25 
 
     }] 
 
    }; 
 
    
 
    // d3ify your data 
 
    // d3 likes arrays of objects, you have an object of objects 
 
    // so first make it an array 
 
    var barData = d3.entries(data); 
 
    // set x domain 
 
    x.domain(barData.map(function(d){ return d.key })); 
 
    // create lineData 
 
    var lineData = []; 
 
    barData.forEach(function(d0, i){ 
 
     d0.mean = d3.mean(d0.value, function(d1){ return d1.value }); 
 
     d0.max = d3.max(d0.value, function(d1){ return d1.value}); 
 
     var N = d0.value.length, 
 
     // this is an inner scale 
 
     // that represents each bar 
 
     s = d3.scale.linear().range([ 
 
      x(d0.key) + (x.rangeBand()/N)/2, 
 
      x(d0.key) + x.rangeBand() 
 
     ]).domain([ 
 
      0, N 
 
     ]) 
 
     d0.value.forEach(function(d1, j){ 
 
     lineData.push({ 
 
      x: s(j), // this is the pixel position of x, it's jittered on the bar 
 
      y: d1.value // this is the user position of y 
 
     }) 
 
     }); 
 
    }); 
 

 
    // set y domain 
 
    y.domain([0, d3.max(barData, function(d) { 
 
     return d.max; 
 
    })]); 
 

 
    svg.append("g") 
 
     .attr("class", "x axis") 
 
     .attr("transform", "translate(0," + height + ")") 
 
     .call(xAxis); 
 

 
    svg.append("g") 
 
     .attr("class", "y axis") 
 
     .call(yAxis) 
 
     .append("text"); 
 

 
    // draw bars 
 
    var bars = svg.selectAll(".bar") 
 
     .data(barData) 
 
     .enter() 
 
     .append('rect') 
 
     .attr('class', 'bar') 
 
     .attr("x", function(d) { 
 
     return x(d.key); 
 
     }) 
 
     .attr("width", x.rangeBand()) 
 
     .attr("y", function(d) { 
 
     return y(d.mean); 
 
     }) 
 
     .attr("height", function(d) { 
 
     return height - y(d.mean); 
 
     }); 
 
    
 
    // add points 
 
    var points = svg.selectAll('point') 
 
     .data(lineData) 
 
     .enter() 
 
     .append('circle') 
 
     .attr('class', 'point') 
 
     .attr("r", 5) 
 
     .attr("cx", function(d) { 
 
     return d.x; // already pixel position 
 
     }) 
 
     .attr("cy", function(d) { 
 
     return y(d.y) 
 
     }); 
 

 
    var line = d3.svg.line() 
 
     .x(function(d) { 
 
     return d.x; // already pixel position 
 
     }) 
 
     .y(function(d) { 
 
     return y(d.y); 
 
     }); 
 

 
    svg.append("path") 
 
     .attr("class", "line") 
 
     .datum(lineData) 
 
     .attr("d", line); 
 

 
    var indicator = svg.append("g") 
 
     .attr("transform", "translate(" + (x("point2") + x.rangeBand()/2) + "," + -20 + ")"); 
 

 
    indicator.append("circle") 
 
     .attr("r", 40) 
 
     .style("fill", "red"); 
 

 
    indicator.append("text") 
 
     .text("!") 
 
     .style("fill", "white") 
 
     .style("text-anchor", "middle") 
 
     .style("alignment-baseline", "middle") 
 
     .style("font-size", 70); 
 

 
    indicator.append("line") 
 
     .attr("y1", 20) 
 
     .attr("y2", height + 20) 
 
     .attr("x1", 0) 
 
     .attr("x2", 0) 
 
     .style("stroke", "red") 
 
     .style("stroke-width", "4px"); 
 
    </script>

快樂d3ing!

+0

絕對太棒了,謝謝馬克!我並不期待一個完整的例子 - 這太棒了!我已經從中學到了很多東西。我正在調整這個解決方案並允許響應支持。我會向其他人發佈另一個答案。 –

+0

用這種方法,你怎麼能爲每個'點'有多個圈元素?顯然來自數據,一些「點」可能有3個圈,其他可能有7個例子。我想我可以計算它的協調方面,但是如何根據數據追加多個圈子? –

+0

我可能最終會看到類似這樣的數據。任何幫助或指導將v.appreciated - 真的抓住了我的頭。 ['value':50},{'value':100},{'value':100}], 'point2':[{'value':25},{'value ':40}], 'point3':[{'value':25}], 'point4':[{...},{...}] } –