简体   繁体   中英

d3js zoom into plot

I have made a plot with d3js and because the plot function goes outside the plot I wanted to clip it. At the same time I also thought about zooming. Therefor I started implementing the following example into my code; http://bl.ocks.org/mbostock/3892919

Unfortunately however, it seems that my implementation is incorrect. My x- and y-axis labels seem to disappear + the background has turned black + the actual plot is missing until you try to zoom :S.

Solution how to fix, ie, correctly implement this is asked. Further I am also open for improvements of my code.

Jsfiddle of the version with zoom: http://jsfiddle.net/n3Lndkum/12/ Jsfiddle of the last working version: http://jsfiddle.net/n3Lndkum/9/

Embedded version:

 var margin = {top: 20, right: 20, bottom: 50, left: 50}, width = 250 - margin.left - margin.right, height = 250 - margin.top - margin.bottom, padding = 50; var x = d3.scale.linear() .range([0, width]) .domain([0, 10]); var y = d3.scale.linear() .range([height, 0]) .domain([0, 10]); var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(5) .innerTickSize(-6) .outerTickSize(0) .tickPadding(7); var yAxis = d3.svg.axis() .scale(y) .orient("left") .ticks(5) .innerTickSize(-6) .outerTickSize(0) .tickPadding(7); var zoom = d3.behavior.zoom() .x(x) .y(y) .scaleExtent([0.1, 32]) .on("zoom", zoomed); var data = []; for (var k = -100; k < 101; k++) { data.push({x: k/10, y: 0.5*k*k/100}); } var line = d3.svg.line() .x(function(d) { return x(dx); }) .y(function(d) { return y(dy); }) .interpolate("linear"); 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 + ")") .call(zoom); svg.append("rect") .attr("width", width) .attr("height", height); // Add x axis svg.append("g") .attr("class","x axis") .attr("transform","translate(0," + height + ")") .call(xAxis); // Add y axis svg.append("g") .attr("class","y axis") .call(yAxis); /* append additional X axis */ svg.append("g") .attr("class", "axis") .attr("transform", "translate(" + [0, 0] + ")") .call(xAxis.innerTickSize(6).tickPadding(-20).tickFormat("")); /* append additional y axis */ svg.append("g") .attr("class","y axis") .attr("transform", "translate(" + [width, 0] + ")") .call(yAxis.innerTickSize(6).tickPadding(-20).tickFormat("")); // Add x axis label svg.append("text") .attr("transform", "translate(" + (width / 2) + "," + (height + margin.bottom) + ")") .style("font-size","15") .style("text-anchor", "middle") .text("x axis"); // Add y axis label svg.append("text") .attr("transform", "rotate(-90)") .attr("y",0 - margin.left) .attr("x",0 - (height / 2)) .attr("dy", "1em") .style("font-size","15") .style("text-anchor", "middle") .text("y axis"); // Add x grid svg.append("g") .attr("class","grid") .attr("transform","translate(0," + height + ")") .call(xAxis .tickSize(-height,-height,0) .tickFormat("") ); // Add y grid svg.append("g") .attr("class","grid") .call(yAxis .tickSize(-width,-width,0) .tickFormat("") ); function zoomed() { svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); // Add data svg.append("path") .attr("class","line") .attr("d",line(data)); } 
 body { font: 10px sans-serif; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .line { fill: none; stroke: steelblue; stroke-width: 1.5px; } .grid .tick { stroke: lightgrey; opacity: 0.7; } .grid path { stroke-width: 0; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script> 

Few things here:

1.) You aren't applying a clip-path to the line. This is why it overruns the grid.

2.) Your grid is black because you've appended a rect to it (so it can receive the pan/zoom mouse events) but you aren't setting it's fill to something (it's black by default). Note, you can't set it's fill to none since then it won't get the mouse events.

3.) You shouldn't share the axis definition like you are doing:

.call(xAxis.innerTickSize(6).tickPadding(-20).tickFormat(""));

The pan and zoom functionality is going to need to redraw the axis(es) and grid(s), keep a reference to each one you create.

4.) In your zoom event, don't re-append a new line. Select the existing one and update it's data:

d3.select(".line")
  .attr("d", line(data));

Putting this all together:

 <!DOCTYPE html> <meta charset="utf-8"> <title>Zoom + Pan</title> <style> svg { font: 10px sans-serif; shape-rendering: crispEdges; } rect { fill: transparent; } .axis path, .axis line { fill: none; stroke: #000; shape-rendering: crispEdges; } .line { fill: none; stroke: steelblue; stroke-width: 1.5px; clip-path: url(#clip); } .grid .tick { stroke: lightgrey; opacity: 0.7; } .grid path { stroke-width: 0; } </style> <body> <script src="//d3js.org/d3.v3.min.js"></script> <script> var margin = { top: 20, right: 20, bottom: 30, left: 40 }, width = 600 - margin.left - margin.right, height = 500 - margin.top - margin.bottom; var x = d3.scale.linear() .domain([0, 10]) .range([0, width]); var y = d3.scale.linear() .domain([0, 10]) .range([height, 0]); var zoom = d3.behavior.zoom() .x(x) .y(y) .scaleExtent([1, 32]) .on("zoom", zoomed); 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 + ")") .call(zoom); var xAxis1 = d3.svg.axis() .scale(x) .orient("bottom") .ticks(5) .innerTickSize(-6) .outerTickSize(0) .tickPadding(7); var yAxis1 = d3.svg.axis() .scale(y) .orient("left") .ticks(5) .innerTickSize(-6) .outerTickSize(0) .tickPadding(7); var xAxis2 = d3.svg.axis() .scale(x) .orient("top") .ticks(5) .innerTickSize(6) .tickPadding(-20) .tickFormat(""); var yAxis2 = d3.svg.axis() .scale(y) .orient("left") .ticks(5) .innerTickSize(6) .tickPadding(-20) .tickFormat(""); var xGrid = d3.svg.axis() .scale(x) .orient("bottom") .tickSize(-height, -height, 0) .tickFormat(""); var yGrid = d3.svg.axis() .scale(y) .orient("left") .ticks(5) .tickSize(-width, -width, 0) .tickFormat(""); // Add x grid svg.append("g") .attr("class", "x grid") .attr("transform", "translate(0," + height + ")") .call(xGrid); // Add y grid svg.append("g") .attr("class", "y grid") .call(yGrid); svg.append("g") .attr("class", "x1 axis") .attr("transform", "translate(0," + height + ")") .call(xAxis1); svg.append("g") .attr("class", "y1 axis") .call(yAxis1); /* append additional X axis */ svg.append("g") .attr("class", "x2 axis") .attr("transform", "translate(" + [0, 0] + ")") .call(xAxis2); /* append additional y axis */ svg.append("g") .attr("class", "y2 axis") .attr("transform", "translate(" + [width, 0] + ")") .call(yAxis2); svg.append("defs").append("clipPath") .attr("id", "clip") .append("rect") .attr("width", width) .attr("height", height); svg.append("rect") .attr("width", width) .attr("height", height); var data = []; for (var k = -100; k < 101; k++) { data.push({ x: k / 10, y: 0.5 * k * k / 100 }); } var line = d3.svg.line() .x(function(d) { return x(dx); }) .y(function(d) { return y(dy); }) .interpolate("linear"); svg.append("path") .attr("class", "line") .attr("d", line(data)) function zoomed() { svg.select(".x1.axis").call(xAxis1); svg.select(".y1.axis").call(yAxis1); svg.select(".x2.axis").call(xAxis2); svg.select(".y2.axis").call(yAxis2); svg.select(".x.grid").call(xGrid); svg.select(".y.grid").call(yGrid); d3.select(".line") .attr("d", line(data)); } </script> 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM