D3.js resize and drag - javascript

I'm drawing a tree with D3. When the page is resized I want to move the tree to a specific position (basically on the space left for the svg so the tree stay visible).
I use this code
function resize() {
svg
.attr("width", getWidth())
.attr("height", getHeight());
svg.attr("transform", "translate(" + (window.innerWidth / 3) + "," + ((window.innerHeight / 3)) + ")" + " scale(1)");
var zoom = d3.behavior.zoom();
zoom.translate([window.innerWidth / 3, window.innerHeight / 3]);
}
d3.select(window).on('resize', resize);
This works fine, but when I start clicking to drag the graph, it's moved to the original position as starting drag point.
This is the drag function:
function redraw() {
if (!d3.event.sourceEvent) return;
d3.event.sourceEvent.stopPropagation();
svg.attr("transform",
"translate(" + d3.event.translate + ")" +
" scale(" + d3.event.scale + ")");
}
var svg = d3.select("#graph").append("svg")
.attr("id", "graph")
.attr("width", getWidth())
.attr("height", getHeight())
.call(zm = d3.behavior.zoom().on("zoom", redraw)).on("dblclick.zoom", null)
.append("g")
.attr("transform", "translate(" + initialWidth + "," + initialHeight + ")");
Any hint on how to solve this?

Make zoom a global variable like this:
var zoom = d3.behavior.zoom().on("zoom", redraw);
and use it in resize and zoom for svg as shown below.
Change 1:
var svg = d3.select("#graph").append("svg")
.attr("id", "graph")
.attr("width", getWidth())
.attr("height", getHeight())
.call(zoom).on("dblclick.zoom", null)
.append("g")
.attr("transform", "translate(" + initialWidth + "," + initialHeight + ")")
Change 2:
function resize() {
svg
.attr("width", getWidth())
.attr("height", getHeight());
svg.attr("transform", "translate(" + (window.innerWidth / 3) + "," + ((window.innerHeight / 3)) + ")" + " scale(1)");
zoom.translate([window.innerWidth / 3, window.innerHeight / 3]);
}

Related

d3.behavior.zoom() moves graph to corner instead of center

I want to add a mousewheel zoom to my sunburst:
What I did is I edited this piece of code:
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width/2 + "," + height * .52 + ")")
.call(zoom);
function zoomed() {
svg.attr('transform', 'translate(' + d3.event.transform.x + ',' + d3.event.transform.y + ') scale(' + d3.event.scale + ')');
//svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
The problem is that when the page loads everything seems to be ok, however when the mouswheel is used to scroll the sunburst ends up in the upper left corner:
I was able to fix this by changing this code:
function zoomed() {
svg.attr("transform", "translate(" + width/2 + "," + height * .52 + ")scale(" + d3.event.scale + ")");
}
The sunburst stays in the middle now, however this won't zoom to the place of the cursor and won't allow the user to zoom out further.
Any suggestions how I can zoom to the cursor place (as in the first code example) without the sunburst disapering in the upper left corner?
For the full code see JSfiddle, it's not working here, however running it local does work.
d3.event.translate returns an array. Thus, if you want to add those values (width/2 and height/2), you have to add them separately:
function zoomed() {
svg.attr('transform', 'translate(' + (d3.event.translate[0] + width / 2) +
',' + (d3.event.translate[1] + height / 2) + ') scale(' + d3.event.scale + ')');
}
Alternatively, if you want to use your original zoomed function...
function zoomed() {
svg.attr("transform", "translate(" + d3.event.translate +
")scale(" + d3.event.scale + ")");
}
... just break the svg selection, assigning another name for the group:
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var foo = svg.append("g")
.attr("transform", "translate(" + width / 2 + "," + height * .52 + ")")
.call(zoom);
I was using the standard d3.event.translate and d3.event.scale in the Zoomed function, like this:
function zoomed() {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
It all worked fine except the whole sunburst jumped to the top left when I first scrolled. It corrected itself after I dragged it back to the middle but it was annoying.
I struggled with this for hours. Eventually I solved it by changing the viewBox attribute on the svg:
.attr({viewBox: "" + (-width / 2) + " " + (-height / 2) + " " + width + " " + height})
I got the idea from this example: Basic Sunburst

Get size of svg g of element

As you can see here element g http://imgur.com/SZImQNB with different browser zoom levels. I would like to get those values 402x398 and 1608x1582 or whatever the values would be while zooming.
var container - is the svg g that I am looking for dimensions of
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
svg.append('rect')
.classed('bg', true)
.attr('stroke', 'transparent')
.attr('fill', 'transparent')
.attr("x", 0)
.attr("y", 0)
.attr('width', w)
.attr('height', h)
.call(d3.behavior.zoom()
.on("zoom", function() {
container.attr("transform", "translate(" + d3.event.translate +
")scale(" + d3.event.scale + ")");
}));
var container = svg.append("g")
.classed("node-area", true)
.attr("id", "test");
I'm using container.node().getBBox().width to get those values and I always receive 70 px width 30 px height no matter what.
console.log("width: "+container.node().getBBox().width+" height: "+container.node().getBBox().height+ "x: "+container.node().getBBox().x+" y: "+container.node().getBBox().y);
I am following d3.js force layout auto zoom/scale after loading TWiStErRob answer
#RobertLongson suggestion does work. In the code, getBoundingClientRect().width has to be used after the transform:
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate
+ ")scale(" + d3.event.scale + ")");
console.log("width: "+container.node().getBoundingClientRect().width);
};
Check the console in this fiddle, while you apply zoom: http://jsfiddle.net/gerardofurtado/tr4kx4af/

d3 timeline update issue

I got this piece of code in which the timeline updates his value by sliding(manually). Problem is That I got an error saying that projection is not a code. Can someone help me please.
Setting the size of the slider.
var margin = {
top: 50,
right: 90,
bottom: 50,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 300 - margin.bottom - margin.top;
// scale function
var timeScale = d3.time.scale()
.domain([new Date('2016-01-01'), new Date('2016-12-31')])
.range([0, width])
.clamp(true);
// initial value
var startValue = timeScale(new Date('2012-03-20'));
startingValue = new Date('2016-08-12');
//////////
defines brush
var brush = d3.svg.brush()
.x(timeScale)
.extent([startingValue, startingValue])
.on("brush", brushed);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
// classic transform to position g
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
// put in middle of screen
.attr("transform", "translate(0," + height / 2 + ")")
// inroduce axis
.call(d3.svg.axis()
.scale(timeScale)
.orient("bottom")
.tickFormat(function(d) {
return formatDate(d);
})
.tickSize(0)
.tickPadding(12)
.tickValues([timeScale.domain()[0], timeScale.domain()[1]]))
.select(".domain")
.select(function() {
console.log(this);
return this.parentNode.appendChild(this.cloneNode(true));
})
.attr("class", "halo");
var slider = svg.append("g")
.attr("class", "slider")
.call(brush);
slider.selectAll(".extent,.resize")
.remove();
slider.select(".background")
.attr("height", height);
var handle = slider.append("g")
.attr("class", "handle")
handle.append("path")
.attr("transform", "translate(0," + height / 2 + ")")
.attr("d", "M 0 -20 V 20")
handle.append('text')
.text(startingValue)
.attr("transform", "translate(" + (-18) + " ," + (height / 2 - 25) + ")");
slider
.call(brush.event)
Sets the canvas with the projection.
var width = 1280;
var height = 900;
var projection = d3.geo.mercator()
.scale(200000)
.center([4.9, 52.36])
.translate([width / 2, height / 2]);
var canvas = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom().on("zoom", function () {
canvas.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")")
}))
.append("g")
Logs the events of the day and the coordinates to the console.
function brushed() {
var value = brush.extent()[0];
console.log()
if (d3.event.sourceEvent) { // not a programmatic event
value = timeScale.invert(d3.mouse(this)[0]);
brush.extent([value, value]);
}
handle.attr("transform", "translate(" + timeScale(value) + ",0)");
handle.select('text').text(formatDate(value));
slidedate = (formatDate(value));
console.log(slidedate)
console.log("dit is datum" + slidedate)
dataCsv.forEach(function(d) {
//console.log("datum is" + slidedate)
if (slidedate == d.Datepattern_startdate) {
console.log(slidedate)
console.log(d.Title)
coords = projection([+d["Longitude"],+d["Latitude"]]);
console.log(coords);
var circle = canvas.append("circle")
.attr("cx", coords[0] )
.attr("cy", coords[1] )
.attr("r", "6px")
.attr("fill", "orange")
.style("opacity", .6)
.append("title")
.text(function(dataCsv) {return d.Title})
} else {
//console.log(" Werkt niet")
}
});
Error = mapm.html:272 Uncaught TypeError: projection is not a function.

Unable to get the Zoom Effect in D3.js

I Had Create some rectangle and want to zoom it.
function zoomed() {
console.log(d3.event.translate);
console.log(d3.event.scale);
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
when I use the zoom effect, d3.event.translate changed properly, but its not transformed.
[enter image description here][1]
Script
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = wrap.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("margin-left", -margin.left + "px")
.style("margin-right", -margin.right + "px")
.classed('network-svg', true)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.style("shape-rendering", "crispEdges")
.call(zoom);
var g = svg.append('g');
function zoomed() {
console.log(d3.event.translate);
console.log(d3.event.scale);
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var drawRectangle = function(rect) {
svg.append("rect")
.style("fill", "none")
.style("stroke", "blue")
.style("stroke-width", "2")
.attr("x", rect.x)
.attr("y", rect.y)
.attr("width", rect.width)
.attr("height", rect.height);
};
Your rectangle is not in the g you are zooming...
var drawRectangle = function(rect) {
g.append("rect") //<-- append it to the g you are zooming
.style("fill", "none")
.style("stroke", "blue")
.style("stroke-width", "2")
.attr("x", rect.x)
.attr("y", rect.y)
.attr("width", rect.width)
.attr("height", rect.height);
};
Working example:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="script.js"></script>
</head>
<body>
<script>
var width = 500,
height = 500,
margin = {left: 10, top: 10, right: 10, bottom: 10};
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var svg = d3.select('body').append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("margin-left", -margin.left + "px")
.style("margin-right", -margin.right + "px")
.classed('network-svg', true)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.style("shape-rendering", "crispEdges")
.call(zoom);
var g = svg.append('g');
function zoomed() {
console.log(d3.event.translate);
console.log(d3.event.scale);
g.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
var drawRectangle = function(rect) {
g.append("rect")
.style("fill", "none")
.style("stroke", "blue")
.style("stroke-width", "2")
.attr("x", rect.x)
.attr("y", rect.y)
.attr("width", rect.width)
.attr("height", rect.height);
};
drawRectangle({x: 100, y: 100, width: 100, height: 100})
</script>
</body>
</html>

How to zoom with d3

I'm drawing a simple bar chart with d3, and I have an x axis setup and have the brush setup so that I can brush to "select" a group of the bars. I would like to then drill down and scale the chart to contain only these bars. The following code works and the colors of the bars inside and touched by the brush turn the right color, but I can't make the thing zoom.
I have looked at this: http://bl.ocks.org/mbostock/1667367 and a bunch of other stuff and just cannot figure it out.
Here is a fiddle, can someone show me how to simply zoom the darn thing?
var brush;
function go3()
{
var dataset = [];
var m = 40;
var count = 500;
dataset.push(m);
for (var i = 0; i < 150; i++) { //Loop 25 times
var newNumber = Math.random() * m; //New random number (0-30)
dataset.push(newNumber); //Add new number to array
}
margin = {top: 10, right: 10, bottom: 30, left: 10},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom
w = width;
h = height;
yScale = d3.scale.linear().domain([0, d3.max(dataset)]).range([0, h * .95]);
xScale = d3.scale.linear()
.range([0, w])
.domain([0, w]);
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
console.log("Max: " + d3.max(dataset));
svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
;
svg.append("rect")
.attr("stroke", "grey")
.attr("stroke-width", 1)
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.attr("fill", "#FFFFFF")
.classed("main-container", true);
svg.append("rect")
.attr("width", w)
.attr("height", h)
.attr("stroke", "grey")
.attr("stroke-width", 0)
.attr("fill", "#EEFFEE")
.attr("x", margin.left)
.attr("y", margin.top)
.classed("brushable-container", true)
;
xAxisGroup = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + (h + margin.top) + ")")
.call(xAxis)
padding=2;
rects = svg.append("g").selectAll(".brushable")
.data(dataset)
.enter()
.append("rect")
.classed("brushable", true);
brush = d3.svg.brush()
.x(xScale)
.on("brush", brushmove)
.on("brushend", brushend);
context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.attr("class", "brush")
.call(brush)
.selectAll('rect')
.attr('height', h);
barWidth = w / dataset.length;
console.log("Width: " + barWidth);
rects
.attr("width", 4)
.attr("height", function(d, i){ return yScale(d)})
.attr("stroke", "yellow")
.attr("stroke-width", .3)
.attr("x", function(d, i){ return (i * barWidth + margin.left) })
.attr("y", function(d, i){ return h - yScale(d) + margin.top} )
;
}
function brushend(){
var extent = brush.extent();
var min = extent[0] >= extent[1] ? extent[1] : extent[0];
var max = extent[0] >= extent[1] ? extent[0] : extent[1];
var lolobb = d3.selectAll("rect.brushable");
var lob = lolobb[0];
console.log(min + " - " + max);
var i = 0;
while( i < lob.length ){
var bbb = lob[i];
try {
var p = parseFloat(bbb.attributes.x.value);
if(min <= p && max >= p) {
d3.select(bbb).attr("fill", "#00FF00");
} else {
d3.select(bbb).attr("fill", "#000000");
}
i++;
} catch(r) {
console.log("BBB");
console.log(bbb);
console.log("Error with " + i);
console.log(typeof(bbb));
console.log(r);
}
}
console.log(min + " - " + max);
console.log(lolobb);
}
function brushmove() {
var extent = brush.extent();
}
You can do this by adding calling a zoom function on "body", you can do so by doing:
svg = d3.select("body")
.append("svg")
.attr("width", w + margin.left + margin.right)
.attr("height", h + margin.top + margin.bottom)
.append("g")
.call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
;
The zooming part is:
.append("g")
.call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", zoom))
Then add the zoom() function:
function zoom() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
Here is a Fiddle Example. Use the scroll wheel to zoom.

Categories