I've got a behavior with d3.zoom whose solution I'm sure is to be found in something I'm obviously missing, but I can't seem to make sense of it. I've reviewed and reviewed examples, and seem to be following them precisely, but something is causing this particular function to not behave.
The following, rather than zoom to focusElement as intended, flips between zooming away from it, and then back to it. The values of -focusBBox['x'], for example, flip between the following two values on subsequent executions. 2500 is svgWidth/2
-208.586669921875
2500
function focusObject(focusElement) {
var focus = document.getElementById(focusElement);
var focusBBox = focus.getBoundingClientRect();
gridGroup.transition().duration(750).call(zoom.transform,d3.zoomIdentity.translate(-svgWidth / 2, -svgHeight / 2).translate(-focusBBox['x'], -focusBBox['y']));
}
Can someone just please take a moment to give a kind virtual slap to point out what it is that I'm missing?
Aha! From a previous incarnation of this particular endeavor, I was using a fixed-position SVG to capture mouse events and applying my transformations to a child SVG. The fact that getBbox() returns local coordinates and getBoundingClientRect() returns coordinates from the outer SVG coord system thus mucked things up.
I've included the revised snippet below. Note that focusBBox2 uses getBBox rather than getBoundingClientRect(), and that grid is the parent SVG as distinct from gridGroup in the original post.
function focusObject(focusElement) {
var focus = document.getElementById(focusElement);
var focusBBox2 = focus.getBBox();
grid.transition()
.duration(750)
.call(zoom.transform,d3.zoomIdentity.translate(focusBBox2.x, focusBBox2.y));
}
Related
I have a rowchart ordered by value (from greater to lesser). When filtering data, the row order changes as expected. However, the row's order change instantly.
Is there a way to animate the reordering of the rows when it changes?
I have searched for possible ways, but I couldn't find anything.
I see that each row has a class according to its position. So. the first one has the classes row _0, the next one row _1, etc., but couldn't figure out how to get the _0, _1 ... classes and using a pretransition function, such as
myChart.on('pretransition', function(chart) {...}
to animate the order. But couldn't figure it out.
Doing this animation was pretty simple, but testing it exposed a major bug in the base mixin, which I fixed. So this demo will only work with version 4.0.4.
There's no good way to implement the animation without changing the source.
This fiddle copies dc.RowChart out to create an AnimatedRowChart, makes a few minor changes to locate symbols in dc.js and d3, and here are the important changes:
Using a key function
When adding the data, we'll use a key function,
.data(this._rowData, ({key}) => key);
This will join the data so D3 knows which bar went where.
Animating the rows
Instead of just setting the positions of the rows
rows.attr('transform', (d, i) => `translate(0,${(i + 1) * this._gap + i * height})`)
we'll give them an animated transition
dc.transition(rows, this.transitionDuration(), this.transitionDelay())
.attr('transform', (d, i) => `translate(0,${(i + 1) * this._gap + i * height})`);
I also thought it looked better if new rows start from their final position rather than the top, so I changed that as well. The resulting transitions still have plenty of issues, but they mostly make sense.
I'm not ready to release this, so it's on a branch of dc.js.
Here is the demo fiddle with complete AnimatedRowChart that you can paste into your own app.
SO doesn't seem to let my GIF loop, so reload the page if you missed it!
I'm backend developer for several years but a newbie in frontend issues.
I used "graphviz" (using d3.js) to draw an SVG graph from DOT notation.
Everything is working fine but one thing I don't get in my mind:
If I "open" another (or the same one) graph its starting position is the
same as this from the previous drawn graph even if I completely remove
the whole node content from the dom as follows:
var svg = d3.selectAll("svg");
var otherBelow = svg.selectAll("*");
otherBelow.remove();
// svg.remove();
Doing this and checking the page source the nodes below SVG are realy dropped
but drawing the new graph it has exactly the position of the previously
moved graph in "transform" attribute. Doing a work around by resetting the
position bevore solves this problem but then the problem remains for the
"moving on mousedown" capability. Then the graph immediately "jumps" to the old
ones position. But therefor I can't even get an information about somewhere
in the page source. Really the generated page code is 100% the same (with
diff tool) but has a different behaviour. Don't understand how this is possible.
So now my question: Is there a kind of caching? Or is there perhaps the
browser cache used somehow internally? How to fix this?
P.s. if I remove the SVG node itself I get a completely courious behaviour.
Then the newly drawn graph is not movable at all.
This (ugly) workaround does it for me.
// Snipped to render a new graph and reset its position
// #graph -> id of d3-graphviz div-container
document.getElementById('graph').innerHTML = ''
setTimeout(() => {
d3.select("#graph").graphviz()
.dot(yourDotData)
.render()
}, 50)
Explanation/Assumption why this works:
It seems that the deletion of the old graph with .innerHTML = ''and the creation of the new one should not happen in the same rendering phase/at the same time, therefore the timeout function.
The second part of the workaround is to use a new graphivz-instance to render the new graph, therefore the
d3.select(...) within the timeout function.
The graphviz renderer is still present on the element it was created on and has all its data still intact.
Removing the svg and then reuse the renderer is not a valid use case. Nico's answer is probably correct, but there are better ways to do it, but in order to tell you how you should do it instead I would need so see all of your code so I can understand what you really want to do.
Hi excuse the newbie nature of this question (and code snippet).
I want to set the extent of a brush. I firstly, I better make sure that my definition of "extent" is correct.
It seems that in most brush examples (such as this: http://bl.ocks.org/mbostock/6232620 ) when the user is initially presented with the brush -- a region is not selected.
I would like to set an initial region...so that the user can already see a selection.
I figure that this selected region is called the "extent".
I have defined my brush as follows:
var the_brush = d3.brushX().extent([[0, 0], [width, height]]).handleSize(50).on("brush", brushed);
And then attach it to an svg element using the following:
svg.append("g").attr("class", "brush").call(the_brush);
The height and width are initially defined as 50 and 880 respectively.
I then figure that I should be able to experiment in Chrome console to redefine the extent such that a shaded area is displayed.
I use this command (in the Chrome console):
the_brush.extent([200, 0], [500, 50])
But a function is returned.
Mmmm I am obviously missing something quite fundamentially here...by what..?
I hope it is clear from my question...my actual objective. That is when the user first opens the page that (s)he is presented with a brush widget that already has a portion selected.
There is a hacky (but working) copy of the code here
My question is similar to a previous question here
But I do not think that the previous solution is applicaton to D3 version 4.
Thanks.
I'm creating an example illustrating a layout with resizeable cells using the D3 drag behaviour and CSS {display: table} styles. It works fine for dragging horizontally, but not vertically. For vertical resizing, d3.event.y is providing values that do not make sense to me.
Here is a fiddle showing the working horizontal drag and the broken vertical drag. Take a look at the console output while dragging to see that the values returned by d3.event match the values returned by d3.mouse() for the horizontal drag, but they diverge for the vertical drag.
I can fix the behaviour by using the d3.mouse() y-coordinate instead of the d3.event y-coordinate. To see this, comment out the "DOESN'T WORK" line and uncomment the "WORKS" line. However, I don't understand why I need to do this, and it seems less general in that I have to assume a mouse input instead of using the more generic d3.event.
Is this a bug, or am I failing to understand something here?
Note that this question seems to be hitting the same issue, but using HTML tables instead of CSS tables. I thought it would be helpful to document that this problem is occurring in both contexts.
Also note that commenting out the two lines that actually do the vertical resizing, commented with "RESIZE CELLS", makes the d3.event work correctly. Of course, the table doesn't get resized then. This suggests that it is something about the act of resizing the divs that is leading d3.event astray.
Alright, I think I've figured out the issue. If you look at the code for drag behavior, you'll notice in dragstart that the value used to calculate the mouse offset is based off this.parentNode. In short, it uses this.parentNode as a reference point, and assumes that it's going to be stable for the duration of the drag. You're modifying the parent nodes during the drag, so its reference point gets, to put it technically, pretty borked. In this case, using d3.mouse is your best bet, since d3.event.y is only going to be reliable as long as the parent node stays in place.
The reason this only happens in the y direction for you is that the x position of all the rows, which are the parent nodes here, stay constant, whereas the y component changes during the drag.
The relevant code sections:
parent = that.parentNode,
function moved() {
var position1 = position(parent, dragId), dx, dy;
//...
dispatch({
type: "drag",
x: position1[0] + dragOffset[0],
y: position1[1] + dragOffset[1],
dx: dx,
dy: dy
});
I'm having a heck of a time with transitions in D3js. I posted a fiddle here: dynamic area graph. The problem that I run into is that when trying to follow this tutorial path transitions, I run into sync problems with the xAxis. Bostock indicates that the domain should be skewed slightly so that the data appears to "shift" in from the side. However, when I do that, the data reflected will be listed under a tick mark that is "2 minutes" behind the actual time it should be listed. If I just update the data as-is, without doing the tricky stuff with the clip-path, it works fine. All of the data is in sync. Just for reference, the xAxis is an integer, linear scale. Dealing with date strings was madding, even though d3 has great time manipulation, I just find dealing with epoch easier. If someone could check out the fiddle and let me know how to transition the entire drawing...I want it to be smooth like in the examples that bostock has.
Since SO requires some code, here's the data structure that I'm generating. The rest is in the fiddle:
setInterval(function(){
lastTime = lastTime.add('m',1);
var data = {"apikey":"FOO",
"description":"a dumb description",
"beg_effective_dt_tm":lastTime,
"data":{
"sum":getRandomInt(385,4000),
}
};
tick(data);
},1000)
I think this is close to what you are after: http://jsfiddle.net/JJ7Rj/1/
It is close because the graph is delayed by one value.
Most of the architecture was already there, and your concern that you might lose the sync between the xAxis and the data was correct. Mike gets around it by changing his range of the scales. The other (and better, IMO) way is to do it by changing the domains. I made the following two primary changes.
The domain of the axis
I have modified the minMax function such that it does not include the latest and the last point in the domain of the xAxis. Note that this means that the most recent value (as well as the oldest value) is actually displayed outside the visible region. This is an unfortunate limitation of using the monotone interpolation, which Mike talks about at the bottom of his post.
function minMax(pd) {
return [
d3.min(pd.slice(1).slice(0, -1),function(d){ return d.beg_effective_dt_tm; }),
d3.max(pd.slice(1).slice(0, -1),function(d){ return d.beg_effective_dt_tm; })
];
}
If you want to have all the values visible, then you'll get the wiggling effect of the discontinuous tangent suddenly forming when the new value comes in. You can obtain that by removing the .slice(0, -1).
The initial transform
For each element, I have initially placed the DOM element one step to the right.
var step = x(newVal.beg_effective_dt_tm) - x(pd[pd.length - 1].beg_effective_dt_tm);
// ...
.attr("transform", 'translate(' + step + ')');
// ...
Then finally, I have transitioned everything back to their rightful place:
clipPath.selectAll("path,circle,.dp").transition()
.ease("linear")
.attr("transform", "translate(" + 0 + ",0)");
Also, I have enabled the transition for the xAxis.