d3.js中的动态重新缩放在网格线中具有奇怪的伪像

时间:2017-06-16 17:09:15

标签: javascript html d3.js

我正在尝试在D3中创建一个具有可以使用滑块调整的比例的绘图。好像它略有失败;当我重新调整比例时,网格线有伪影。

知道我做错了什么吗?我有一个带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>

1 个答案:

答案 0 :(得分:2)

文档在这方面并不十分清楚,我之前就已经坚持过了。

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

与:

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

我们经常看到.data()。enter()链接只是因为所有元素都会被输入而没有退出:

  

如果我们没有现有元素,例如空页?然后   我们将数据加入到空选择中,所有数据都会结束   输入

     

这种模式很常见,你经常会看到selectAll +数据+   输入+ 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>