2016-06-27 47 views
1

私は3軸を独立してズームする必要があるプロジェクトがあります。これを達成するためにスクロールバーを使用していますが、ズームが発生すると軸は再描画されません。D3.js軸でズームする

また、クリップパスが期待どおりに機能していないようです。

最終的には、グラフにパン機能も追加したいと思いますが、どこで開始するのかはわかりません。助けを事前に

http://jsfiddle.net/yo4mwLpj/

感謝。

// cases vs deaths of a disease over time 
var data = [ 
{"year": "1960", "cases":"887", "deaths": "199"}, 
{"year": "1965", "cases":"218", "deaths": "55"}, 
{"year": "1993", "cases":"37046", "deaths": "931"}, 
{"year": "1994", "cases":"38735", "deaths": "118"}, 
{"year": "1995", "cases":"19903", "deaths": "624"}, 
{"year": "1997", "cases":"4170", "deaths": "125"}, 
{"year": "1998", "cases":"10000", "deaths": "0"} 
]; 
data.forEach(function (d) { 
    d.year = d3.time.format("%Y").parse(d.year.toString()); 
    d.cases = +d.cases; 
    d.deaths = +d.deaths; 
}); 

var margin = { top: 30, right: 40, bottom: 30, left: 50 }, 
    width = 500 - margin.left - margin.right, 
    height = 270 - margin.top - margin.bottom; 

var xScale = d3.time.scale() 
    .domain(d3.extent(data, function (d) { return d.year; })) 
    .range([0, width]); 

var yScaleLeft = d3.scale.linear() 
    .domain([0, d3.max(data, function (d) { return d.cases; })]) 
    .range([height, 0]); 

var yScaleRight = d3.scale.linear() 
    .domain([0, d3.max(data, function (d) { return d.deaths; })]) 
    .range([height, 0]); 

var xAxis = d3.svg.axis() 
    .scale(xScale) 
    .orient("bottom").ticks(5); 

var yAxisLeft = d3.svg.axis() 
    .scale(yScaleLeft) 
    .orient("left").ticks(5); 

var yAxisRight = d3.svg.axis() 
    .scale(yScaleRight) 
    .orient("right").ticks(5); 

var lineCases = d3.svg.line() 
    .x(function (d) { return xScale(d.year); }) 
    .y(function (d) { return yScaleLeft(d.cases); }); 

var lineDeaths = d3.svg.line() 
    .x(function (d) { return xScale(d.year); }) 
    .y(function (d) { return yScaleRight(d.deaths); }); 

var svg = d3.select("#chart").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.append("g") 
    .attr("class", "x axis") 
    .attr("transform", "translate(0," + height + ")") 
    .call(xAxis); 

svg.append("g") 
    .attr("class", "y axis yleft") 
    .call(yAxisLeft); 

svg.append("g") 
    .attr("class", "y axis yright") 
    .call(yAxisRight) 
    .attr('transform', 'translate(' + width + ',0)'); 

svg.append("path") 
    .datum(data) 
    .attr("class", "line lineLeft") 
    .style("stroke", "red") 
    .attr("d", lineCases(data)) 
    .attr("clip", "url(#clip)"); 

svg.append("path") 
    .datum(data) 
    .attr("class", "line lineRight") 
    .attr("d", lineDeaths(data)) 
    .attr("clip", "url(#clip)"); 

svg.append("clipPath") 
    .attr("id", "clip") 
    .append("rect") 
    .attr("width", width) 
    .attr("height", height); 

$("#slider-x").slider({ 
    orientation: "horizontal", 
    range: "min", 
    min: 1000, 
    max: 10000, // make max be (maxDate-minDate).Days*1000, so you can zoom to one day 
    value: 1000, 
    slide: function(event, ui) { 
     zoomXWithSlider(ui.value/1000); 
    } 
}); 

$("#slider-y-left").slider({ 
    orientation: "vertical", 
    range: "min", 
    min: 1000, 
    max: 10000, 
    value: 1000, 
    slide: function(event, ui) { 
     zoomLeftWithSlider(ui.value/1000); 
    } 
}); 

$("#slider-y-right").slider({ 
    orientation: "vertical", 
    range: "min", 
    min: 1000, 
    max: 10000, 
    value: 1000, 
    slide: function(event, ui) { 
     zoomRightWithSlider(ui.value/1000); 
    } 
}); 

function zoomXWithSlider(scale) { 
    // Note: works only on the <g> element and not on the <svg> element 
    // which is a common mistake 
    svg.selectAll("path.line").attr("transform", "scale("+scale+", 1)"); 
    svg.select(".x.axis").call(xAxis); 
} 

function zoomLeftWithSlider(scale) { 
    svg.select("path.line.lineLeft").attr("transform", "scale(1, "+scale+")"); 
    svg.select(".y.axis.yleft").call(yAxisLeft); 
} 

function zoomRightWithSlider(scale) { 
    svg.select("path.line.lineRight").attr("transform", "scale(1, "+scale+")"); 
    svg.select(".y.axis.yright").call(yAxisRight); 
} 
+0

多くの問題があります。私は今より完全な答えを書くことができます。まず、スライダの変更に反応して軸を再描画するときは、軸の基点となる尺度も変更する必要があります。そうでない場合は、同じものを再描画します(見ているとおり)。例えば、Xでは、軸を再描画する前に 'xScale.range([0、width * scale])'のようなことをするかもしれません。スライダーの値が変化したときに、新しいスケール値をキャプチャして、チャートのすべてまたはほとんどを再描画して、コードの繰り返しを避けるようなものをリファクタリングすることができます。 – meetamit

+0

パンに入ると、影響を受けた範囲の最初の要素(上記の「0」)も変更する必要があります。これが、リファクタリングの理由です。 – meetamit

+0

clipPathが機能しない理由は複数あります。まず、 'clipPath'は' 'セクションの子でなければなりません。 'clip'属性の代わりに、' .attr( "clip-path"、 "url(#clip)");)のように 'clip-path'を指定する必要があります。しかし、スライダーがドラッグされたときに ' 'を拡大すると、clipPathも暗黙的に拡大縮小されるという問題があります。つまり、一定のチャートの幅に基づいてクロップを続行しません。私が何を意味するか見るために、私は意図的にマスクの幅を 'width/2'に設定して、チャートの半分を切り取った。 xスライダをスライドさせたときの動きを見てみましょう。http://jsfiddle.net/tvvcbrob/ – meetamit

答えて

1

http://jsfiddle.net/8oo3ocfs/

var zoom = d3.behavior.zoom() //zoomYAxis 
    .x(xScale) 
    .y(yScaleLeft) 
    .on("zoom", function(){ 
     // don't let double-click or scroll wheel do anything 
     if (d3.event.sourceEvent == null || d3.event.sourceEvent.type == "wheel"){ 
     zoom.scale(previousScale); 
     zoom.translate(previousTranslate); 
     zoomRight.scale(previousScale); 
     zoomRight.translate(previousTranslate); 
     return; 
     } 

     // set previous scale for future use 
     previousScale = zoom.scale(); 
     previousTranslate = zoom.translate(); 

     //zoom.translate(panLimit()); 
     //zoomRight.translate(zoom.translate()); 

     // update the right side scale 
     zoomRight.scale(previousScale); 
     zoomRight.translate(previousTranslate); 

     // redraw lines 
     svg.select("path.line.lineLeft").attr("d", lineCases(data)); 
     svg.select("path.line.lineRight").attr("d", lineDeaths(data)); 

     // redraw axes 
     svg.select(".x.axis").call(xAxis); 
     svg.select(".y.axis.yleft").call(yAxisLeft); 
     svg.select(".y.axis.yright").call(yAxisRight); 
    }); 

私は最終的にこの作業を得ました。実際のプロジェクトでは機能するので、jsfiddleでクリップパスを使う必要はありません。さらに、私はパンの機能性を制限することはありませんでした。なぜなら、それは数学と非常に複雑になったからです。だから私はちょうどそれにリセットボタンを置く。