I define a zoom function:
var zoom = d3.zoom().on("zoom", function () {
svg.attr('transform', d3.event.transform);
});
and call it on this svg variable:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(zoom)
.append("g")
.attr("transform", "translate("
+ width/10 + "," + height/2 + ")");
(where width and height happen to be the size of the screen).
This works great, except for the first time the user zooms. The zoom state is still at the origin, as opposed to the width/10 and height/2 translation.
How do I change the zoom state programmatically to fix this?
Just after writing this I found a very helpful answer here
What worked for me was this:
d3.select('svg').call(zoom.translateBy, width/10, height/2);
Related
I created a scatter plot using D3.js.I would like to add the functionality to zoom and pan.
I have declare zoom behaviour for scatter plot.Try to use it in svg element.
But it throws error says argument of type zoom behavior is not assignable to parameter of type selection:selection SVGElement
I am just a beginner with using D3.Can anyone help or have any suggestions?
var zoom = d3.zoom()
.scaleExtent([0.3, 2])
.on("zoom", function () {
svg.attr("transform", d3.event.transform)
});
var svg = d3.select(component).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);
Welcome to Stack Overflow.
You need to provide a function to the onzoom handler
.on("zoom", function () {
svg.attr("transform", d3.event.transform)
});
should look more like this, where you are calling your own function and passing it the transform as a parameter
d3.select(context.canvas).call(d3.zoom()
.scaleExtent([1, 8])
.on("zoom", () => zoomed(d3.event.transform)));
The zoomed function could be this:
function zoomed(transform) {
context.save();
context.clearRect(0, 0, width, height);
context.translate(transform.x, transform.y);
context.scale(transform.k, transform.k);
context.beginPath();
for (const [x, y] of data) {
context.moveTo(x + r, y);
context.arc(x, y, r, 0, 2 * Math.PI);
}
context.fill();
context.restore();
}
These snippets were taken from this example:
https://observablehq.com/#d3/zoom-canvas
I have a d3.js svg which I overlaid on Google map using OverlayView. Then a user specifies a zip code in an input text, and I need for my map to zoom in to that particular zip code. I have a latitude and longitude for the zip code. But I can't get it to zoom to the svg. I could zoom in using Google's API, but that's not what I need. I need to zoom in to the svg since there are data in there that needs to show in linear color. The following is my code so far:
function zoomtoZipCode(){
var counties = d3.select("svg").select("g") //apply zoom here.
.call(d3.behavior.zoom().scaleExtent([1, 7])
.on("zoom", zoom))
.selectAll(".counties")
.data(conus.features)
.enter()
}
function zoom(){
var p = d3.select("path")[0];
d3.select(p).attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Appreciate any assistance. Thanks.
I needed to define my zoom variable and use the terms in d3.js version 4 as follows:
var zoom = d3.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed2);
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.call(zoom);
function zoomed2() {
d3.select("#svgMap2").select("g").selectAll(".county")
.text("text", function (d) { return d.properties.County; })
.attr("transform", d3.event.transform)
.select(".county").style("stroke-width", .5 / d3.event.transform.k + "px");
}
I'm working with d3 v4 and struggling to create a simple chart, to use as a reference for further development, which both utilises the d3.zoom() function and the chart resizes to fit the browser window. Whenever I get one of these working the other breaks. The Typescript code is within an Angular 4 component but that shouldn't have an impact.
First I initialise some properties:
// Create a variable to hold the DOM Element which the chart will be attached to.
this.element = this.chartContainer.nativeElement;
// Set the width and height
this.width = this.svgWidth - this.margin.left - this.margin.right;
this.height = this.svgHeight - this.margin.top - this.margin.bottom;
// create scale objects
this.xScale = d3.scaleLinear()
.domain([0, 100])
.range([0, this.width]);
this.yScale = d3.scaleLinear()
.domain([-10, -20])
.range([this.height, 0]);
// create axis objects
this.xAxis = d3.axisBottom(this.xScale);
this.yAxis = d3.axisLeft(this.yScale);
Then I define my zoomFunction():
this.zoomFunction = () => {
if (d3.event.transform != null) {
//console.log(d3.event.transform);
if (this.xScale != null) {
// create new scale ojects based on event
let new_xScale = d3.event.transform.rescaleX(this.xScale);
let new_yScale = d3.event.transform.rescaleY(this.yScale);
// update axes
this.xChartAxis.call(this.xAxis.scale(new_xScale));
this.yChartAxis.call(this.yAxis.scale(new_yScale));
// update circle
this.circles.attr("transform", d3.event.transform)
};
};
};
Define my zoom() function:
this.zoom = d3.zoom().on("zoom", this.zoomFunction);
Define my windowResize() function:
this.onWindowResize =() => {
// Set the width and height.
// The element.offsetWidth is the total width of the DOM element including scrollbars, borders etc.
this.svgWidth = this.element.offsetWidth;
this.svgHeight = this.element.offsetHeight;
// Update the width and height which the chart fits into.
this.width = this.element.offsetWidth - this.margin.left - this.margin.right;
this.height = this.element.offsetHeight - this.margin.top - this.margin.bottom;
// Update the width of the svgViewport.
this.svgViewport.attr("width", this.svgWidth);
this.svgViewport.attr("height", this.svgHeight);
// Update the size of the zoomView
this.zoomView
.attr("width", this.width)
.attr("height", this.height);
// Update the Range of the x scale.
this.xScale.range([0, this.width]);
this.yScale.range([0, this.height]);
// Update the axis.
this.innerSpace.select(".axisY")
.call(this.yAxis);
this.innerSpace.select(".axisX")
.attr("transform", "translate(0," + this.height + ")")
.call(this.xAxis);
};
Add an EventListener:
window.addEventListener("resize", this.onWindowResize);
Finally create my chart:
createChart() {
// Create a circle
this.originalCircle = {
"cx": 50,
"cy": -15,
"r": 20
};
// Append an svg object to the chart element and save in the variable 'svgViewport'.
this.svgViewport = d3.select(this.element).append('svg')
.attr("class", "chart")
.attr("width", this.svgWidth)
.attr("height", this.svgHeight);
// Inner Drawing Space
this.innerSpace = this.svgViewport.append("g")
.attr("class", "inner-space")
.attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")")
.call(this.zoom);
// Add the circle.
this.circles = this.innerSpace.append('circle')
.attr("id", "circles")
.attr("cx", this.xScale(this.originalCircle.cx))
.attr("cy", this.yScale(this.originalCircle.cy))
.attr('r', this.originalCircle.r);
// Draw Axis
this.xChartAxis = this.innerSpace.append("g")
.attr("class", "axisX")
.attr("transform", "translate(0," + this.height + ")")
.call(this.xAxis);
this.yChartAxis = this.innerSpace.append("g")
.attr("class", "axisY")
.call(this.yAxis);
// append zoom area
this.zoomView = this.innerSpace.append("rect")
.attr("class", "zoom")
.attr("width", this.width)
.attr("height", this.height)
.call(this.zoom);
};
The curious thing is that if I refresh the browser and resize the window the axis move and resize as expected, but the circle does not move. When I pan/zoom the circle moves and scales correctly and the axis move correctly. Having pan/zoomed and then resize the window the axis do not move any more and the circle remains static.
I think I am close, but I can't see the next step. Any suggestions very welcome.
I am working on a scatterplot in d3 where I need to be able to update the yAxis domain when I click on a button.
This is what I have right now. This is the lowest and highest value.
yAxis = d3.scaleLinear().rangeRound([height, 0]);
yAxis.domain([23500, 29600]);
How can I change the Domain to like domain([26500, 33600]) when I click on a button?
Do I need to add it to the buttons with a click function?
d3.select('#data2010').on('click', function () {
Or is there a way to automatically look for the lowest and highest value and update it?
This is a simple example on how to update a domain and rescale an axis.
You need to get something from the user to be able to know when to rescale the axis. So that click event has to come from some mode of input like a button, radio button, checkbox, etc.
First off, you need to update the domain for the scale that you're using for your y-axis.
Call .transition() with a .duration() with a set timeframe for the axis to transition when the user wants it to rescale.
You can accomplish the above with something like the following (You call the function when your button is clicked):
function rescale() {
y.domain([26500, 33600])
svg.selectAll("g.yaxis")
.transition().duration(1000)
.call(d3.axisLeft(y));
}
Check working snippet below:
var margin = {
top: 20,
right: 80,
bottom: 30,
left: 50
},
width = 500 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
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 + ")");
var y = d3.scaleLinear().range([height, 0]);
y.domain([23500, 29600]);
svg.append("g")
.attr("class", "yaxis")
.call(d3.axisLeft(y));
function rescale() {
y.domain([26500, 33600])
svg.selectAll("g.yaxis")
.transition().duration(1000)
.call(d3.axisLeft(y));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.8.0/d3.min.js"></script>
<button onclick="rescale();">Rescale</button>
I want zoom + brush on a bar chart. I was able to implement zooming ok: demo here, but I found out that after zooming, when you mouse down to drag, the bar chart moves. I don't want the bar chart to move on mouse drag. I want the bar chart to stay in its original place and I want to use the mouse drag for brushing. The bar chart could probably scroll left and right, but it should not be dragged left and right.
How do I make the bar chart stay in its original place?
My code snippet is below. Full script here (134 lines)
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([0.8, 10])
.on("zoom", zoomed);
function zoomed() {
// scale x axis
svg.select(".x.axis").call(xAxis);
// scale the bars
var width_scaled = columnwidth * d3.event.scale;
svg.selectAll(".bargroup rect")
.attr("x", function(d) {return x(d.date) - width_scaled/2;})
.attr("width", width_scaled);
}
var brush = d3.svg.brush()
.x(x)
.on("brush", brushed);
function brushed() {
//console.log(brush.extent());
}
var svg = d3.select("#timeline").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("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height + 7);