Zoom in zoom out in d3 js collapsible tree - d3.js

I am using this working code as a starter. I am trying to modify according to my data and need.
The only thing I ain't getting is why this zoom in zoom out not working. I tried this solution but got no luck.
I ain't able to find where the problem is within the code but I can see the comment where it stated that the functionality is deactivated. How can I make it activated? Sorry if this is a silly question I am still a newbie in js. There are similar questions too but the thing is jsfiddle are not working anymore so I can't see the whole code to understand the logic.
// Zoom functionnality is desactivated (user can use browser Ctrl + mouse wheel shortcut)
function zoomAndDrag() {
//var scale = d3.event.scale,
var scale = 1,
translation = d3.event.translate,
tbound = -height * scale,
bbound = height * scale,
lbound = (-width + margin.right) * scale,
rbound = (width - margin.left) * scale;
// limit translation to thresholds
translation = [
Math.max(Math.min(translation[0], rbound), lbound),
Math.max(Math.min(translation[1], bbound), tbound)
];
d3.select('.drawarea')
.attr('transform', 'translate(' + translation + ')' +
' scale(' + scale + ')');
}
Here is the working demo with the code:
https://blockbuilder.org/ninjakx/2c4e726a531b40f9f09d7df41217d1de

D3 uses the Mouse Wheel event for zoom in/out functionality.
Your sample disables the mouse wheel event in this line:
d3.select('#tree-container').select('svg').on(mouseWheelName, null);
Also, the demo uses version 3 of D3, I strongly recommend to use the latest version of the library (V5).

Related

set zoom after each redraw

I am using 'dagre d3' for displaying dependency graph. I also use slider(which I use to display step by step evolution of graph when user slides the slider) so each time Slider is moved, graph is redrawn. For the first time, 'zoom' feature will be set like below:
var zoom = d3.behavior.zoom().on("zoom", function () {
inner.attr("transform", "translate(" + d3.event.translate + ")" +
"scale(" + d3.event.scale + ")");
});
svg.call(zoom);
I want to retain the zoom level if the user has zoomed in or zoomed out the graph and then moved the slider. So make it work, I did the following.
this method will be called whenever user slides the slider.
function redrawGraphToStage(stageToRedraw) {
// Not sure how to get previous values. below one works but feel its bit hacky.
if(inner[0][0].transform.animVal.length){
tempInnerTranslate = [inner[0][0].transform.animVal[0].matrix.e, inner[0][0].transform.animVal[0].matrix.f];
tempInnerScale = inner[0][0].transform.animVal[1].matrix.a;
}
// do modifications to graph. and re-render.
if(tempInnerScale || tempInnerTranslate){
// this will set up the zoom level to before.
zoom.translate(tempInnerTranslate).scaleExtent(tempInnerScale, tempInnerScale).event(svg);
}
Even though it setup the zoom level to previous values, I wont be able to zoom after this. Any suggestions would be great.
Try with,
var zoom = d3.behavior.zoom().on("zoom", zoomed);
svg.call(zoom);
function zoomed() {
inner.attr("transform",
"translate(" + zoom.translate() + ")" +
"scale(" + zoom.scale() + ")"
);
}
function redrawGraphToStage(stageToRedraw) {
// Not sure how to get previous values. below one works but feel its bit hacky.
if(inner[0][0].transform.animVal.length){
tempInnerTranslate = [inner[0][0].transform.animVal[0].matrix.e, inner[0][0].transform.animVal[0].matrix.f];
tempInnerScale = inner[0][0].transform.animVal[1].matrix.a;
}
// do modifications to graph. and re-render.
if(tempInnerScale || tempInnerTranslate){
// this will set up the zoom level to before.
zoom.translate(tempInnerTranslate).scale(tempInnerScale, tempInnerScale);
zoomed();
}
I'm not sure what you're using the slider for, but I made you a demo where you can control the zoom scale with a slider bar. Significant change: scaleIntent replaced with scale.
JSFiddle working demo

Zooming works only when hovering on the dots in scatterplot

I am trying to implement a scatter plot with zooming capability with D3. I have generated the plot and the visualization,it can zoom in and out only when the mouse cursor is over the data points. In other words, when the mouse is on anywhere other than the dots, I am unable to zoom.
I have been searching and trying a number of examples from the Internet, but I had no luck. Please check the code residing in this link: https://github.com/e-kaya/scatter.git What do you think I have done wrong?
Thanks in advance.
You are calling the zoom function on the .dots.
svg.select(".dots").call(zoom);
Try binding the zoom function to the <svg>.
d3.select('svg').call(zoom);
depending on your requirement you may have to tweak the zoomed function as well.
function zoomed() {
d3.select('svg).attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
This will zoom your svg container.

d3 - Axis Label Transition

In Mike Bostock's cubism demo (http://bost.ocks.org/mike/cubism/intro/demo-stocks.html), there is a cursor which displays the values of all horizon charts on display. Furthermore, the cursor text shows the time axis point in time. As the cursor text obscures an axis label, the label fades.
I am working on a similar display with d3.js (but not cubism). I have all working except that fade portion. I have searched through the CSS in the developer's window, searched the source code (as best I could), but I don't understand what manner of magic is being used to accomplish this feat. I've even looked through SO "axis label transition" questions, but I have failed to connect the dots on xaxis label transitions.
How does that fade in/out when obscured by text happen?
UPDATE:
I think I located the event script area where this happens - its just a little over my head at the moment - can anyone help me decipher what this event listener is doing? Specifically, in the second g.selectAll in the else clause below - what data (d) is being used here? What is causing this event to fire?
This is the coolest part of the display (outside of the horizon charts), I would love to figure this out ...
context.on("focus.axis-" + id, function(i) {
if (tick) {
if (i == null) {
tick.style("display", "none");
g.selectAll("text").style("fill-opacity", null);
} else {
tick.style("display", null).attr("x", i).text(format(scale.invert(i)));
var dx = tick.node().getComputedTextLength() + 6;
g.selectAll("text").style("fill-opacity", function(d) { return Math.abs(scale(d) - i) < dx ? 0 : 1; });
}
}
});
I used this as reference to accomplish the same effect.
I'm not sure what the context variable is or how the id's are set or what the tick flag references but what I did was simply update the opacity of the ticks according to their proximity to the mouse. With this, the vertical tick fades as well as the label text.
svg.selectAll('.x.axis .tick').style('opacity', function (d) {
return Math.min(1, (Math.round(Math.abs(d3.mouse(svg.node())[0] - x(d))) - 10) / 15.0);
});
This way, the opacity is set to 0 if it's within 10 pixels, and fades from 1-0 between 10 and 25. Above 25, the opacity would be set to an increasingly large number, so I clamp it to 1.0 using the Math.min function.
My labels are slightly rotated, so I also added an offset not shown inside the formula above (a +3 after [0]) just to make it look a bit nicer. A year late to answer your only question, but hey it's a nice effect.
same answer as Kevin Branigan's post, but using the d3 scale to calculate the opacity value.
var tickFadeScale = d3.scale.linear().domain([10,15]).range([0,1]).clamp(true);
svg.selectAll('.x.axis .tick').style('opacity', function (d) {
return tickFadeScale(Math.abs(d3.mouse(svg.node())[0] - x(d)));
}

D3 transition end events not firing correctly?

This should be straightforward: I just want to start a D3 transition running when the user clicks on a button.
However, the D3 end event does not seem to be firing correctly on my transitions: I've asked for a transition to rotate(120), but the end event seems to be firing when the rotate attribute is only 45.
This is my code:
var angle = 0;
// handle click event
d3.selectAll('.mycolour').on('click', function() {
d3.select("#loadingicon").remove();
// redraw the icon (excluded here for brevity)...
// setting the transform attribute to 0 explicitly..
group2.attr("transform", "rotate(0,0,0)");
// and start it turning:
angle = 0;
function rotateLoadingIcon() {
angle = angle%360;
angle += 120;
console.log('rotateLoadingIcon', angle, group2.attr("transform"));
d3.select(".icon").transition().ease("linear")
.duration(4000)
.attr("transform", "rotate(" + angle + ",0,0)")
.each("end", function() {
angle += 120;
console.log('innerRotateLoadingIcon', angle, group2.attr("transform"));
group2.transition().ease("linear")
.duration(4000)
.attr("transform", "rotate(" + angle + ",0,0)")
.each("end", function() {
rotateLoadingIcon();
});
});
}
console.log('about to start icon loading');
rotateLoadingIcon();
});
It works the first time the icon is drawn:
rotateLoadingIcon 120 rotate(0,0,0)
innerRotateLoadingIcon 240 rotate(119.99999999999999)
But after second or subsequent clicks, I've seen it produce console output like this:
about to start icon loading
rotateLoadingIcon 120 rotate(0,0,0)
innerRotateLoadingIcon 240 rotate(45.14999999999999)
In practice what this means is that the direction of rotation changes :(
Why has innerRotateLoadingIcon started, when the rotate attribute is only set to 45.1...? Surely given the code, it shouldn't start until rotate reaches 120 - as in the first time the code is run.
I'm wondering if the way I have set up the JavaScript means that two different versions of rotateLoadingIcon could be running at the same time. Is this possible, and can I fix it if so?
Update: Here's a JSFiddle that demonstrates the problem, though I'm now trying setInterval rather than end - click twice and you'll see the rotation change direction, I can't figure out why: http://jsfiddle.net/QtBvJ/
In the version in your jsfiddle, you only need to clear the timeout properly to make it work. The function setTimeout returns a reference that you need to pass to window.clearTimeout, i.e.
var t = null;
...
window.clearTimeout(t);
...
t = setTimeout(...);
Working fiddle here.

Zoom toward mouse (eg. Google maps)

I've written a home-brew view_port class for a 2D strategy game. The panning (with arrow keys) and zooming (with mouse wheel) work fine, but I'd like the view to also home towards wherever the cursor is placed, as in Google Maps or Supreme Commander
I'll spare you the specifics of how the zoom is implemented and even what language I'm using: this is all irrelevant. What's important is the zoom function, which modifies the rectangle structure (x,y,w,h) that represents the view. So far the code looks like this:
void zoom(float delta, float mouse_x, float mouse_y)
{
zoom += delta;
view.w = window.w/zoom;
view.h = window.h/zoom;
// view.x = ???
// view.y = ???
}
Before somebody suggests it, the following will not work:
view.x = mouse_x - view.w/2;
view.y = mouse_y - view.h/2;
This picture illustrates why, as I attempt to zoom towards the smiley face:
As you can see when the object underneath the mouse is placed in the centre of the screen it stops being under the mouse, so we stop zooming towards it!
If you've got a head for maths (you'll need one) any help on this would be most appreciated!
I managed to figure out the solution, thanks to a lot of head-scratching a lot of little picture: I'll post the algorithm here in case anybody else needs it.
Vect2f mouse_true(mouse_position.x/zoom + view.x, mouse_position.y/zoom + view.y);
Vect2f mouse_relative(window_size.x/mouse_pos.x, window_size.y/mouse_pos.y);
view.x = mouse_true.x - view.w/mouse_relative.x;
view.y = mouse_true.y - view.h/mouse_relative.y;
This ensures that objects placed under the mouse stay under the mouse. You can check out the code over on github, and I also made a showcase demo for youtube.
In my concept there is a camera and a screen.
The camera is the moving part. The screen is the scalable part.
I made an example script including a live demo.
The problem is reduced to only one dimension in order to keep it simple.
https://www.khanacademy.org/cs/cam-positioning/4772921545326592
var a = (mouse.x + camera.x) / zoom;
// now increase the zoom e.g.: like that:
zoom = zoom + 1;
var newPosition = a * zoom - mouse.x;
camera.setX(newPosition);
screen.setWidth(originalWidth * zoom);
For a 2D example you can simply add the same code for the height and y positions.

Resources