I am creating a d3 plot of some data and I would like to have zooming and panning capabilities. When I plot my data and zoom in or pan, the data can move outside the defined plot area without being clipped.
As you can see in the image, the orange dots appear on the green area when I would have expected them to be clipped.
Any ideas on what is wrong?
Demo at http://jsfiddle.net/ng6srz4o/
var dataX = [1,3,5,7,9];
var dataY = [0,2,4,6,8];
var margin, padding, outerWidth, outerHeight, innerWidth, innerHeight;
var xAxis, yAxis, xScale, yScale, svg, container, zoom, tooltip;
// assign the dimensions here, works the best
outerWidth = Math.ceil ($(window).width() * 0.90);
outerHeight = Math.ceil ($(window).height() * 0.55);
// distance of the outerRect from the innerRect
margin = { top: 16, right: 16, bottom: 32, left: 32 };
// distance of the actual data from the innerRect
padding = { top: 0, right: 32, bottom: 0, left: 32 };
innerWidth = outerWidth - margin.left - margin.right;
innerHeight = outerHeight - margin.top - margin.bottom;
xScale = d3.scale.linear()
.domain([ d3.min(dataX), d3.max(dataX) ])
.range([ padding.left, innerWidth - padding.right ]);
yScale = d3.scale.linear()
.domain([ d3.min(dataY), d3.max(dataY) ])
.range([ innerHeight - padding.bottom, padding.top ]);
zoom = d3.behavior.zoom()
.x(xScale).y(yScale)
.scaleExtent([1, 20])
.on ("zoom", onZoom);
svg = d3.select("#svgContainer")
.append("svg:svg")
.attr("width", outerWidth)
.attr("height", outerHeight)
.style("background", "green")
.append("svg:g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
drawAxis();
// white rect behind the graph
var rect = svg.append("svg:rect")
.attr("width", innerWidth)
.attr("height", innerHeight)
.style("fill", "gray");
container = svg.append("svg:g");
var graph = container.append("svg:g").selectAll("scatter-dots")
.data(dataY)
.enter().append("svg:circle")
.attr("cy", function (d) { return yScale(d); })
.attr("cx", function (d,i) { return xScale(dataX[i]); })
.attr("r", 10)
.style("fill", "orange");
function onZoom() {
var t = d3.event.translate,
s = d3.event.scale;
container.attr("transform", "translate(" + t + ")scale(" + s + ")");
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
}
function drawAxis()
{
xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom");
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + innerHeight + ")")
.style("fill", "blue")
.call(xAxis);
yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
svg.append("svg:g")
.attr("class", "y axis")
.style("fill", "red")
.call(yAxis);
}
Right now, you have all of your points contained in a g element, which won't clip any of the points that you're drawing.
If you want to clip the points, you can add an svg element to your main svg, which will clip anything contained within, like so:
var mainSVG = d3.select('body').append('svg')
.attr('width', outerWidth)
.attr('height', outerHeight);
var innerSVG = mainSVG.append('svg')
.attr('width', innerWidth)
.attr('height', innerHeight)
.selectAll('scatter-dots')
...
(updated) fiddle.
Related
I've been trying to do a Area graph with zoom, which works great unless i give a negative value to Y domain. And then it looks great if i don't try to zoom along the y axis
I've tried using the min value of of y for y0 and while that fixes the rendering (it looks god awful and it renders X where it has no value).
// set the dimensions and margins of the graph
let margin = {
top: 0,
right: 0,
bottom: 30,
left: 30
},
width = 440 - margin.left - margin.right,
height = 240 - margin.top - margin.bottom;
// append the svg object to the body of the page
let svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/dimitriiBirsan/RandomNumbersYear1970-2031/main/testing%20negative%20numbers.csv",
// When reading the csv, I must format variables:
function(d) {
return {
date: d3.timeParse("%m/%d/%Y")(d.date),
value: d.value
}
},
// Now I can use this dataset:
function(data) {
// Add X axis --> it is a date format
let x = d3.scaleTime()
.domain(d3.extent(data, function(d) {
return +d.date;
}))
.range([0, width]);
let xAxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(4));
// Add Y axis
let y = d3.scaleLinear()
.domain(d3.extent(data, function(d) {
return +d.value;
}))
.range([height, 0]);
let yAxis = svg.append("g")
.call(d3.axisLeft(y));
function make_x_gridlines(x) {
return d3.axisBottom(x)
}
function make_y_gridlines(y) {
return d3.axisLeft(y)
}
// Add a clipPath : everything out of this area won't be drawn
let clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("x", 0)
.attr("y", 0);
// Add the area
let line = svg.append('g')
.attr("clip-path", "url(#clip)")
line.append("path")
.datum(data)
.attr("class", "polyArea")
.attr("fill", "blue")
.attr("opacity", 0.7)
.attr("stroke", "none")
.attr("stroke-width", 1.5)
.attr("d", d3.area()
.x(function(d) {
return x(d.date)
})
.y0(y(0))
.y1(function(d) {
return y(d.value)
})
.curve(d3.curveStepAfter)
.defined((d, i) => (i != null))
);
let zoom = d3.zoom()
.scaleExtent([1, 50]) // This control how much you can unzoom (x0.5) and zoom (x20)
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", updateChart);
// This adds an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.attr('transform', 'translate(' + margin.top + ')')
.call(zoom);
function updateChart() {
// recover the new scale
let newX = d3.event.transform.rescaleX(x);
let newY = d3.event.transform.rescaleY(y);
// update axes with these new boundaries
xAxis.call(d3.axisBottom(newX).ticks(5))
yAxis.call(d3.axisLeft(newY))
// update location
svg
.select('.polyArea')
.attr("d", d3.area()
.x(function(d) {
return newX(d.date)
})
.y0(y(0))
.y1(function(d) {
return newY(d.value)
})
.curve(d3.curveStepAfter)
.defined((d, i) => (i != null)))
}
})
.grid line {
stroke: grey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width 0;
}
<main>
<div id="my_dataviz">
</div>
</main>
<script src="https://d3js.org/d3.v4.js"></script>
You forgot to use newY(0) instead of y(0) so now when you zoom, the zero line is where it should be, not where it was when you started
// set the dimensions and margins of the graph
let margin = {
top: 0,
right: 0,
bottom: 30,
left: 30
},
width = 440 - margin.left - margin.right,
height = 240 - margin.top - margin.bottom;
// append the svg object to the body of the page
let svg = d3.select("#my_dataviz")
.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 + ")");
//Read the data
d3.csv("https://raw.githubusercontent.com/dimitriiBirsan/RandomNumbersYear1970-2031/main/testing%20negative%20numbers.csv",
// When reading the csv, I must format variables:
function(d) {
return {
date: d3.timeParse("%m/%d/%Y")(d.date),
value: d.value
}
},
// Now I can use this dataset:
function(data) {
// Add X axis --> it is a date format
let x = d3.scaleTime()
.domain(d3.extent(data, function(d) {
return +d.date;
}))
.range([0, width]);
let xAxis = svg.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(4));
// Add Y axis
let y = d3.scaleLinear()
.domain(d3.extent(data, function(d) {
return +d.value;
}))
.range([height, 0]);
let yAxis = svg.append("g")
.call(d3.axisLeft(y));
function make_x_gridlines(x) {
return d3.axisBottom(x)
}
function make_y_gridlines(y) {
return d3.axisLeft(y)
}
// Add a clipPath : everything out of this area won't be drawn
let clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("x", 0)
.attr("y", 0);
// Add the area
let line = svg.append('g')
.attr("clip-path", "url(#clip)")
line.append("path")
.datum(data)
.attr("class", "polyArea")
.attr("fill", "blue")
.attr("opacity", 0.7)
.attr("stroke", "none")
.attr("stroke-width", 1.5)
.attr("d", d3.area()
.x(function(d) {
return x(d.date)
})
.y0(y(0))
.y1(function(d) {
return y(d.value)
})
.curve(d3.curveStepAfter)
.defined((d, i) => (i != null))
);
let zoom = d3.zoom()
.scaleExtent([1, 50]) // This control how much you can unzoom (x0.5) and zoom (x20)
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", updateChart);
// This adds an invisible rect on top of the chart area. This rect can recover pointer events: necessary to understand when the user zoom
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.attr('transform', 'translate(' + margin.top + ')')
.call(zoom);
function updateChart() {
// recover the new scale
let newX = d3.event.transform.rescaleX(x);
let newY = d3.event.transform.rescaleY(y);
// update axes with these new boundaries
xAxis.call(d3.axisBottom(newX).ticks(5))
yAxis.call(d3.axisLeft(newY))
// update location
svg
.select('.polyArea')
.attr("d", d3.area()
.x(function(d) {
return newX(d.date)
})
.y0(newY(0))
.y1(function(d) {
return newY(d.value)
})
.curve(d3.curveStepAfter)
.defined((d, i) => (i != null)))
}
})
.grid line {
stroke: grey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width 0;
}
<main>
<div id="my_dataviz">
</div>
</main>
<script src="https://d3js.org/d3.v4.js"></script>
I am new to D3.js. I am stuck of the following concepts:
I couldn't find examples where this is done in D3.js V4 and I am not sure how to navigate it.
To limit the zoom from going beyond zero I would like to use the minimum of the zoom as ZERO. I am not sure how to do this in scatter plot.
To avoid the zoomed points touching the y and z axis. I would like the points to fade or disappear when it touches the axis areas.
Here is my code
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 750 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var xMax = d3.max(graphdata, function(d) { return d["x"]; }),
yMax = d3.max(graphdata, function(d) { return d["y"]; });
var xScale = d3.scaleLinear()
.range([0, width])
.domain([0, xMax]).nice();
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, yMax]).nice();
var xAxis = d3.axisBottom()
.scale(xScale);
var yTicks = 5
var yAxis = d3.axisLeft()
.scale(yScale);
var svg = d3.select("#plotspace").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("id", "plot")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
// create a clipping region
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var gX = svg.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'x axis')
.call(xAxis);
var gY= svg.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'y axis')
.call(yAxis
);
var bubble = svg.selectAll('.bubble')
.data(graphdata)
.enter().append('path')
.attr('class', 'bubble')
.attr("d", d3.symbol().type(d3.symbolCircle).size(30))
.attr("transform", function(d) { return "translate(" + xScale(d["x"]) + "," + yScale(d["y"]) + ")"; })
.attr('r', 3.5 )
.attr('fill-opacity',0.7)
.style('fill','blue');
bubble.append('title')
.attr('x', 3.5 )
.text(keys[0]);
// Pan and zoom
var zoom = d3.zoom()
.scaleExtent([.5, 20])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.call(zoom);
function zoomed() {
var new_xScale = d3.event.transform.rescaleX(xScale);
var new_yScale = d3.event.transform.rescaleY(yScale);
gX.call(xAxis.scale(new_xScale));
gY.call(yAxis.scale(new_yScale));
bubble.data(graphdata)
.attr("transform", function(d) { return "translate(" + new_xScale(d["x"]) + "," + new_yScale(d["y"]) + ")"; })
}
Your first issue, negative numbers, is a result of allowing a zoom out from the initial zoom state. If the scales already hold all the data (since you dynamically create the scales), you should never have to zoom out from this zoom level. Zooming out from the initial zoom creates a plot area greater than the translate extent, this is causing negative values to appear in the scale. Try:
zoom.scaleExtent([1,4]);
That fixes the negative numbers, but you can still have overflow within those translate extents because you aren't using a clip path correctly.
You currently use one g called svg to plot points and draw axes, but you don't want to apply a clip area to this g, as the axes are outside of where you wish to draw the points. Instead, you could create a new g for the points only, and apply the plot area to that g with g.attr('clip-path','url(#id)');. Below I call that g plotArea and demonstrate these two changes:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 750 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var graphdata = d3.range(200).map(function(d) {
return {x: d3.randomLogNormal()(), y: d3.randomLogNormal()()}
})
var xMax = d3.max(graphdata, function(d) { return d["x"]; }),
yMax = d3.max(graphdata, function(d) { return d["y"]; });
var xScale = d3.scaleLinear()
.range([0, width])
.domain([0, xMax]).nice();
var yScale = d3.scaleLinear()
.range([height, 0])
.domain([0, yMax]);
var xAxis = d3.axisBottom()
.scale(xScale);
var yTicks = 5
var yAxis = d3.axisLeft()
.scale(yScale);
var svg = d3.select("#plotspace").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("id", "plot")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// create a clipping region
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var plotArea = svg.append("g") // we don't want to clip the axes.
.attr("clip-path","url(#clip)");
var gX = svg.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'x axis')
.call(xAxis);
var gY= svg.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'y axis')
.call(yAxis
);
var bubble = plotArea.selectAll('.bubble') // add to clipped area.
.data(graphdata)
.enter().append('path')
.attr('class', 'bubble')
.attr("d", d3.symbol().type(d3.symbolCircle).size(30))
.attr("transform", function(d) { return "translate(" + xScale(d["x"]) + "," + yScale(d["y"]) + ")"; })
.attr('r', 3.5 )
.attr('fill-opacity',0.7)
.style('fill','blue')
// Pan and zoom
var zoom = d3.zoom()
.scaleExtent([1, 20])
.translateExtent([[0, 0], [width, height]])
.extent([[0, 0], [width, height]])
.on("zoom", zoomed);
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.call(zoom);
function zoomed() {
var new_xScale = d3.event.transform.rescaleX(xScale);
var new_yScale = d3.event.transform.rescaleY(yScale);
gX.call(xAxis.scale(new_xScale));
gY.call(yAxis.scale(new_yScale));
bubble.data(graphdata)
.attr("transform", function(d) { return "translate(" + new_xScale(d["x"]) + "," + new_yScale(d["y"]) + ")"; })
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="plotspace"></div>
I'm messing around with d3 but a basic line seems to be inverted. Seems like the origin is at the top of the page (like the default orientation on a page). However, i assumed that the d3 points were relative to the svg graph.
How can I set the origin to be bottom left of graph? (Without transforming the data)
// make dataset
var dataset = [[1,1]];
for (var x = 0; x< 10000; x +=1) {
var y = x*x;
dataset.push([x, y])
}
// set graph dims
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");;
// add scale
var x = d3.scale.linear().domain([0, 200]).range([0, width]);
var y = d3.scale.linear().domain([0, 1000]).range([height, 0]);
// add x axis
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
// add y axis
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - margin.top - margin.bottom) + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
//add dots
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return d[0];
})
.attr("cy", function(d) {
return d[1];
})
.attr("r", 1);
Regarding the orientation: it has nothing to do with D3. D3 manipulates DOM elements, and normally (but not always) we use D3 to manipulate SVG elements. And the SVG specs say that the origin (0,0) is at the top left corner.
Regarding your problem: you correctly set the y scale to go from the bottom to the top, but you simply forgot to use it! Use the scale:
.attr("cx", function(d) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
Here is the demo:
// make dataset
var dataset = [[1,1]];
for (var x = 0; x< 100; x +=1) {
var y = x*x;
dataset.push([x, y])
}
// set graph dims
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Adds the svg canvas
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");;
// add scale
var x = d3.scale.linear().domain([0, 200]).range([0, width]);
var y = d3.scale.linear().domain([0, 1000]).range([height - margin.bottom - margin.top, 0]);
// add x axis
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
// add y axis
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(10);
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + (height - margin.top - margin.bottom) + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
//add dots
svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 1);
line, path {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I have a Y scale:
d3.scale.linear()
.domain([1, 10])
.range([15, 0])
When it is rendered it outputs html like
<rect class="background" style="visibility: hidden; cursor: crosshair;" height="15" width="15" x="0">
Then I can go and change height of that background rectangle like this:
slider.select(".background")
.attr("height", 7);
And that change is applied, however when i try to do the same with width:
slider.select(".background")
.attr("width", 7);
The rendered html output does not change - it still remains 15 and not 7.
How can I change width, given that I want to keep length of the scale the same, that is I dont want to change value here range([15, 0]) The reason I don't want to have such width is that I am making vertical slider and that background sheet is too wide and I don't need it to be 15x15.
Edit. This is actually vertical svg d3 slider. This is how I render it:
var slider=null, sliderActive = false;
var margin = {top: 200, right: 50, bottom: 200, left: 50},
height = 150;
var x = d3.scale.linear()
.domain([3, 500])
.range([height, 0])
.clamp(true);
var brush = d3.svg.brush();
/*
* Brush states:
* "-1" - brush is not not moved or clicked
* "1" - brush is clicked
* "2" - brush is moved
*/
var brushState = -1;
var svg = d3.select("svg")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var slider = svg.append("g")
.attr("class", "slider")
.call(brush);
var handle = slider.append("circle")
.attr("class", "handle")
//.attr("transform", "translate(0," + height + ")")
.attr("transform", "translate(" + 20 + ", 0 )")
.attr("r", 9);
var Sample = {
initialize: function(settings) {
brush.x(x)
.extent([0, 0])
.on("brush", brushMoved);
brush
.on("brushstart", brushClicked);
brush
.on("brushend", brushReleased);
svg.append("g")
.attr("class", "y axis")
//.attr("transform", "translate(0," + height + ")")
.attr("transform", "translate(" + 20 + ", 0 )")
.call(d3.svg.axis()
.scale(x)
.orient("left")
.tickFormat(function(d) { return d + "%"; })
.ticks(6)
.tickSize(0)
.tickPadding(12))
.select(".domain")
.select(function() { return this.parentNode.appendChild(this.cloneNode(true)); });
slider.selectAll(".extent,.resize")
.remove();
slider.select(".background")
.attr("height", height)
.attr("width", 20);
slider
.call(brush.event)
.transition() // gratuitous intro!
.duration(750)
.call(brush.extent([100, 100]))
.call(brush.event);
},
};
function brushClicked() {
brushState = 1;
}
function brushMoved() {
brushState = 2;
var value = brush.extent()[1];
if (d3.event.sourceEvent) { // not a programmatic event
value = x.invert(d3.mouse(this)[1]);
brush.extent([value, value]);
}
handle.attr("cy", x(value));
}
function brushReleased() {
brushState = -1;
}
Notice line:
slider.select(".background")
.attr("height", height)
.attr("width", 20);
It sets height, yes, but not width.
Code used as an example and reference is taken from: http://bl.ocks.org/mbostock/6452972
I'm looking for some times now to allow zooming into one or the other (or both) directions X/Y on a d3.js chart. Here is my simple chart :
var margin = parseInt(attrs.margin) || {top: 20, right: 20, bottom: 20, left: 20},
padding = parseInt(attrs.padding) || 30;
var svg = d3.select(ele[0]).append('svg')
.style('width', '100%');
var width = 960 - margin.left - margin.right - padding,
height = 500 - margin.top - margin.bottom;
// X scale
var x = d3.scale.linear().domain([d3.min(data, function(d){return d.x;}), d3.max(data, function(d){return d.x})]).range([0, width]);
// Y scale
var y = d3.scale.linear().domain([0, d3.max(data, function(d){return d.y;})]).range([height, 0]);
svg.call(d3.behavior.zoom().x(x).y(x).on("zoom", zoomed));
// create a line function that can convert data[] into x and y points
var line = d3.svg.line()
.interpolate("basis")
// assign the X function to plot our line as we wish
.x(function(d,i) { return x(d.x); })
.y(function(d) { return y(d.y); });
// Add an SVG element with the desired dimensions and margin.
svg.attr("width", width + margin.left + margin.right + padding)
.attr("height", height + margin.top + margin.bottom);
var g = svg.append("g").attr("transform", "translate(60," + margin.top + ")");
// create Axis
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left");
// Add the x-axis.
g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(" + 0 + "," + height + ")")
.call(xAxis);
// Add the y-axis to the left
g.append("g")
.attr("class", "y axis")
.attr("transform", "translate(" + 0 +",0)")
.call(yAxis);
// objects for the zooming clipping
var clip = g.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var chartBody = g.append("g")
.attr("clip-path", "url(#clip)");
chartBody.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
function zoomed() {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.select(".line")
.attr("class", "line")
.attr("d", line);
}
Is there a way to use a key modifier when zooming (for example when shift+mouse wheel, only zoom X) ?