2017-06-16 42 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)...'? –