2017-06-16 63 views
1

我試圖在D3中創建一個可以使用滑塊調整比例的繪圖。看起來它略有失敗;當我重新調整比例時,網格線會產生僞影。d3.js中的動態縮放在網格線中有奇怪的痕跡

任何想法我做錯了什麼?我使用onChange方法調用ysc.domain([0,this.value]);來重新調整y軸的滑塊,並重新繪製包括座標軸和網格線的圖形。

<!DOCTYPE html> 
 
<html> 
 
<head> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.8.0/d3.min.js"></script> 
 
<script type='text/javascript'> 
 
document.addEventListener("DOMContentLoaded", function(event) { 
 

 

 
var margin = {top: 10, right: 20, bottom: 20, left: 30}, 
 
width = 600, 
 
height = 180; 
 

 

 
// add the graph canvas to the body of the webpage 
 
var svg = d3.select("div#plot1").append("svg") 
 
.attr("width", width) 
 
.attr("height", height); 
 

 
var axis = svg.append("g") 
 
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 
 

 
var xsc= d3.scaleLinear().domain([0,1]).range([0, width-margin.left-margin.right]), 
 
    ysc= d3.scaleLinear().domain([0,1]).range([height-margin.top-margin.bottom,0]); 
 
    
 
var N = 500; 
 
axis.append("path"); 
 
var line = d3.line() 
 
.x(function(d,i) { return xsc(i/N); }) 
 
.y(function(d,i) { return ysc(d); }); 
 
var axis_drawn = d3.axisLeft( ysc); 
 
axis.call(axis_drawn); 
 
function drawGridLines() 
 
    { 
 
var grid=axis.selectAll("line.horizontalGrid"); 
 
grid.data(ysc.ticks()).enter() 
 
    .append("line") 
 
    .attr('class','horizontalGrid') 
 
    .attr('x1',margin .right) 
 
    .attr('x2',width) 
 
    .attr('fill','none') 
 
    .attr("shape-rendering" , "crispEdges") 
 
    .attr("stroke" , "black") 
 
    .attr("stroke-width" , "1px") 
 
    .attr('opacity','0.2') 
 
    .merge(grid) 
 
    .attr('y1', ysc) 
 
    .attr('y2', ysc); 
 

 
grid.exit().remove(); 
 
    }  
 
function drawGraph() 
 
{ 
 
var data = []; 
 
var ylim = ysc.domain(); 
 
for (var i = 0; i < N; ++i) 
 
{ 
 
    data.push((Math.cos(i*8*Math.PI/N) + 1)/2.0); 
 
} 
 

 
var waveform = axis.selectAll("path"); 
 
waveform.datum(data) 
 
.attr("fill","none") 
 
.attr("stroke","steelblue") 
 
.attr("d", line); 
 

 

 
axis.call(axis_drawn); 
 
drawGridLines(); 
 
} 
 
drawGraph(); 
 
function showRange(x) { 
 
    d3.select('#rangeLabel').text(x); 
 
} 
 
showRange(1); 
 
d3.select('#range') 
 
    .on('change', function(d) { 
 
    ysc.domain([0,this.value]); 
 
    drawGraph(); 
 
    showRange(this.value); 
 
}); 
 

 

 
}); // DOMContentLoaded event 
 
</script> 
 
<style type='text/css'> 
 
svg { 
 
    display: block; 
 
    margin-left: auto; 
 
    margin-right: auto; 
 
} 
 
.grid line { 
 
    stroke: lightgrey; 
 
    stroke-opacity: 0.7; 
 
    shape-rendering: crispEdges; 
 
} 
 

 
.grid path { 
 
    stroke-width: 0; 
 
} 
 
.hidden { 
 
    display: none; 
 
} 
 
div#interactive-container { 
 
    vertical-align: top; 
 
    display: inline-block; 
 
    position: relative; 
 
    left: 0; 
 
} 
 
form#interactive-controls { 
 
    border: 1px solid black; 
 
    display: inline-block; 
 
} 
 
div#plot2 { 
 
    display: inline-block; 
 
    position: relative; 
 
    left: 0; 
 
} 
 
</style> 
 
</head><body> 
 
<div id="plot1"> 
 
</div> 
 
<div id='bottom-panel'> 
 
<div id='interactive-container' class='hidden'> 
 
<form id='interactive-controls'> 
 
<input type="range" id='range' value='1', min='1', max='10', step='1'><span id='rangeLabel'></span> 
 
</form> 
 
</div> 
 
</div> 
 
</body> 
 
</html>

回答

2

的文檔不會在這個非常清楚,我有之前停留在此。

selection.data().enter()... 
selection.exit()... 

是不一樣的:

selection.data(); 
selection.enter()... 
selection.exit()... 

我們看到的.data()進入()鏈接往往只是因爲所有的元素將被輸入並沒有退出:

什麼如果我們沒有現有的元素,比如空頁面?然後 我們正在將數據加入空白選擇,並且所有數據都以 結束。

這種模式很常見,你會經常看到selectAll + data + 輸入+ append方法依次調用,其中一個緊接在 之後。儘管這很常見,但請記住,這只是一個數據連接的特例。 (Mike's three little circles)。

因爲所有數據都以enter()方式結束,所以您的代碼在初始追加時起作用。直到蜱的數量超過10時纔出現問題,此時當蜱的數量應該減少時,退出選擇顯然不如預期的那樣工作。它是空的,並沒有刪除任何東西。使用此設置:

selection.data(); 
selection.enter()... 
selection.exit()... 

應該解決這個問題:

<!DOCTYPE html> 
 
<html> 
 
<head> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.8.0/d3.min.js"></script> 
 
<script type='text/javascript'> 
 
document.addEventListener("DOMContentLoaded", function(event) { 
 

 

 
var margin = {top: 10, right: 20, bottom: 20, left: 30}, 
 
width = 600, 
 
height = 180; 
 

 

 
// add the graph canvas to the body of the webpage 
 
var svg = d3.select("div#plot1").append("svg") 
 
.attr("width", width) 
 
.attr("height", height); 
 

 
var axis = svg.append("g") 
 
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); 
 

 
var xsc= d3.scaleLinear().domain([0,1]).range([0, width-margin.left-margin.right]), 
 
    ysc= d3.scaleLinear().domain([0,1]).range([height-margin.top-margin.bottom,0]); 
 
    
 
var N = 500; 
 
axis.append("path"); 
 
var line = d3.line() 
 
.x(function(d,i) { return xsc(i/N); }) 
 
.y(function(d,i) { return ysc(d); }); 
 
var axis_drawn = d3.axisLeft( ysc); 
 
axis.call(axis_drawn); 
 
function drawGridLines() 
 
    { 
 
var grid=axis.selectAll("line.horizontalGrid") 
 
    .data(ysc.ticks()); 
 
    
 
grid.enter() 
 
    .append("line") 
 
    .attr('class','horizontalGrid') 
 
    .attr('x1',margin .right) 
 
    .attr('x2',width) 
 
    .attr('fill','none') 
 
    .attr("shape-rendering" , "crispEdges") 
 
    .attr("stroke" , "black") 
 
    .attr("stroke-width" , "1px") 
 
    .attr('opacity','0.2') 
 
    .merge(grid) 
 
    .attr('y1', ysc) 
 
    .attr('y2', ysc); 
 

 
grid.exit().remove(); 
 
    }  
 
function drawGraph() 
 
{ 
 
var data = []; 
 
var ylim = ysc.domain(); 
 
for (var i = 0; i < N; ++i) 
 
{ 
 
    data.push((Math.cos(i*8*Math.PI/N) + 1)/2.0); 
 
} 
 

 
var waveform = axis.selectAll("path"); 
 
waveform.datum(data) 
 
.attr("fill","none") 
 
.attr("stroke","steelblue") 
 
.attr("d", line); 
 

 

 
axis.call(axis_drawn); 
 
drawGridLines(); 
 
} 
 
drawGraph(); 
 
function showRange(x) { 
 
    d3.select('#rangeLabel').text(x); 
 
} 
 
showRange(1); 
 
d3.select('#range') 
 
    .on('change', function(d) { 
 
    ysc.domain([0,this.value]); 
 
    drawGraph(); 
 
    showRange(this.value); 
 
}); 
 

 

 
}); // DOMContentLoaded event 
 
</script> 
 
<style type='text/css'> 
 
svg { 
 
    display: block; 
 
    margin-left: auto; 
 
    margin-right: auto; 
 
} 
 
.grid line { 
 
    stroke: lightgrey; 
 
    stroke-opacity: 0.7; 
 
    shape-rendering: crispEdges; 
 
} 
 

 
.grid path { 
 
    stroke-width: 0; 
 
} 
 
.hidden { 
 
    display: none; 
 
} 
 
div#interactive-container { 
 
    vertical-align: top; 
 
    display: inline-block; 
 
    position: relative; 
 
    left: 0; 
 
} 
 
form#interactive-controls { 
 
    border: 1px solid black; 
 
    display: inline-block; 
 
} 
 
div#plot2 { 
 
    display: inline-block; 
 
    position: relative; 
 
    left: 0; 
 
} 
 
</style> 
 
</head><body> 
 
<div id="plot1"> 
 
</div> 
 
<div id='bottom-panel'> 
 
<div id='interactive-container' class='hidden'> 
 
<form id='interactive-controls'> 
 
<input type="range" id='range' value='1', min='1', max='10', step='1'><span id='rangeLabel'></span> 
 
</form> 
 
</div> 
 
</div> 
 
</body> 
 
</html>

+0

的感謝!你能詳細說明爲什麼'selection.data().enter()...'與'selection.data()不同。 selection.enter()...'?我讀過三個小圈子,這對我沒有意義。所以'selection.data()'不會返回選擇本身? –

+0

哦,沒關係,我現在明白了:https://github.com/d3/d3-selection/blob/master/README.md#selection_data --selection.data([data [,key]]) - 將指定的數據數組與選定的元素進行連接,返回表示更新選擇的新選擇:成功綁定到數據的元素。 –

+0

什麼時候把東西放入'selection.data()....'vs'selection.merge(selection)...'? –