I'm trying to integrate a constrained zoom (http://bl.ocks.org/mbostock/4987520) into the example featuring Bounding Box (https://bl.ocks.org/mbostock/9656675).
The point is to get the same behavior as the Bounding Box example while constraining the window zoom (and also being able to move around/zoom in/zoom out with the mouse which is already done in the given example).
I've been trying out and also found examples (not aiming to do the exact same thing though) but nothing worked as expected.
My code can be boiled down to the one in the Bounding Box, with the following "move" function :
var t = d3.event.translate,
s = d3.event.scale;
t[0] = Math.min(0,Math.max(t[0],width - width*s));
t[1] = Math.min(0,Math.max(t[1],height - height*s));
zoom.translate(t);
g.style("stroke-width", 1/s).attr("transform","translate(" + t + ")scale(" + s + ")");
Now the behavior I get almost meets the requirements except that when clicking on a specific state to zoom in on it, the view abruptly recenters on the previous clicked state before smoothly moving into the new chosen one.
Strangely enough, removing the "zoom.translate(t)" in the previous code fixes the bug but gives an "over-constrained" feeling to the manual zoom, i.e some areas are not reachable by zooming in and the "camera" translates in a peculiar way making it also impossible to zoom out - the zoom view is somehow attracted by the current selected state.
If someone knows how to work it out to get the expected behavior, I'll be thrilled.
Thanks in advance,
Killian.
Related
Good day, trying to figure out one last piece with a code rewrite that we've been working on. We have a bubble chart that animates, to essentially simulate a motion chart, since most of the motion chart libraries we relied on previously incorporated flash.
It seems like the mouseover space for the original render sticks around during animation, and causes the bubbles to "reset", I've noticed the tooltips also stick to the original location. Any ideas/suggestions?
I've copied up a mostly complete version here (the loading of additional variables isn't implemented):
https://nl.communityaccounts.ca/motionchart/motion_dev.asp
I'm working on a standalone jsfiddle as well, can put a link to that soon.
Highcharts uses plotX and plotY point's properties to position the tooltip in a default way. In your case, only properties: x, y, z and point's graphic are updated. You need to also update plotX and plotY:
dataObject.plotX = data.x + data.z;
dataObject.plotY = data.y + data.z;
Live demo: https://jsfiddle.net/BlackLabel/9drynwcz/
I'm using a third library that is adding 'transform: translate3d(200,0,0);' to a parent element. So the elements within the svg tend to move to the right. For drag&drop events, setting the origin is enough, but I'm having troubles to get corrected the d3.mouse(this).
I have created a jsfiddle of this example: http://bl.ocks.org/hlucasfranca/f133da4493553963e710
jsFiddle: https://jsfiddle.net/rahpuser/5jp6123u/
So, when clicking without applying the transform to the parent, everything is ok, but when the parent has the transform, d3.mouse returns a wrong number
var coords = d3.mouse(this);
coords[0] === 100; //true without transform..
coords[0] === 300; // true when the transform to the parent is applied.
// the values are not precise but you can verify the behavior in the jsfiddle
Why d3.mouse(this) is not able to get the correct value ?
my understanding is that d3.mouse should get the coords based on the container this ( svg ).
What should I do to fix the behavior keeping the parent with the new transform?
Thanks.
UPDATE:
Not working in Firefox 46 for ubuntu
Working well in chrome and opera
As you've discovered, this is a known FireFox bug. Even if you did fix FireFox, it's going to take some time to propagate, and it'll never be backwards-fixed. So you still need a workaround.
One thing you can do — as long as the SVG's top-left is always at the 0,0 coordinate of its containing div (as is the case with your jsFiddle) — is replace the call to d3.mouse(this) with:
d3.mouse(this.parentNode)
That'll get the mouse coordinate from the parent of the SVG, which is a normal div, so apparently it's not affected by this bug and will get you the value you want on all platforms.
This does not address the root problem (for example getClientBoundingRect would still return the wrong values), but it works around your specific problem.
I'm fiddling around with D3.js in this plunker.
It mostly does what I want, but I noticed a small inconsistency.
The idea is that the 2nd slider rotates the SVG elements in the container. As the elements within are actually text elements, I would like to have them displayed unrotated, and as such, I applied a transform: rotate to them with the symmetric value from the slider.
Although, I noticed that by doing that, the text elements don't rotate around their center, but rather around their top-left corner (I think). This is mostly visible when you observe a point near the edge and see how in transposes the edge on rotation.
I tried already setting both text-anchor and alignment-baseline to middle on these elements, hoping it would offset the text path, but apparently it doesn't change the point around which they pivot when rotated.
Additionally, should I try to set the rotate with pivot point coordinates, it boggles the effect entirely, by applying some translate to the elements, which I can't figure out.
Not sure if getBBox() could help me either in this subject, but I've considered maybe offsetting the points by half their width/height. This seems a bit convoluted though, and I was hoping for an easier/more elegant/cleaner fix.
Any thoughts?
Well, this is awkward. Right after I posted the question, I found an answer in D3.js docs: polygon.centroid().
Basically, I used the return value of this function as the transform: rotate pivot point coordinates and the offset is taken care of. Example (line 99 of the above plunker, rotate() function):
(...)
var textElement = d3.select(this);
return justTranslate+"rotate("+ -value+ textElement.centroid() +")";
(...)
Hope this helps anyone with the same issue.
EDIT: for some reason, Chrome's console says:
Uncaught TypeError: textElement.centroid is not a function
But the behavior is what I was looking for. Have no idea why.
EDIT2: ended up changing the above answer to a getBBox() approach, because the slider movement was bonked because of the previous edit error.
Changed it to this:
text.attr("transform", function(d){
var textElement = d3.select(this);
var current = textElement.attr("transform");
var alreadyRotated = current.indexOf('rotate');
var justTranslate = current.substring(0, alreadyRotated != -1 ? alreadyRotated : current.length);
var bbox = textElement.node().getBBox();
var point = [bbox.x + 0.5*bbox.width, bbox.y + 0.5*bbox.height];
return justTranslate+"rotate("+ -value +" "+ point +")";
});
I have a group of elements that are masked by a rect in SnapSVG and I want to translate the elements, bringing new ones into view (and hiding ones that are currently in view). The code is really simple - here's a codepen: http://codepen.io/austinclemens/pen/ZbpVmX
As you can see from the pen, box1, which starts outside the mask element (clip) should cross through it when animated, but it never appears. Moreover, box2, which should move out of the clipping area, remains visible.
This example seems to do a similar thing and has no problems: http://svg.dabbles.info/snaptut-masks2
Here's the code from codepen:
var t = Snap('#target')
var clip=t.rect(200,200,200,200).attr({fill:'#fff'})
var box1=t.rect(300,100,50,50).attr({fill:'#000'})
var box2=t.rect(300,300,50,50).attr({fill:'#000'})
var boxgroup=t.group(box1,box2)
boxgroup.attr({mask:clip})
boxgroup.animate({transform:'t100,300'},2000)
I notice that the svg.dabbles examples translates the clip region by 0,0 at one point, but adding something like that doesn't seem to get me anywhere.
Ok, I figured this out thanks in part to this really great article about SVG transforms: http://sarasoueidan.com/blog/svg-transformations/
The upshot is that when I translate the box group, it takes the mask with it. This is a little confusing to me still - I guess the mask attribute is causing this somehow? Anyways, the solution is to apply an opposite translation to the mask to keep it in place. Check the pen to see it in action but basically I just had to add:
clip.animate({transform:'t-100,-300'},2000)
The tricky part of this is that you now need to synchronize the movement of the mask and the movement of the box group.
edit - I now demonstrate how synchronization can be achieved using snap's set.animate method on the codepen.
I made a codepen with snap svg: Codepen
I try to rotate a svg-gear in an endless-loop around his own centerpoint.
This works on Internet Explorer, but fails on Mozilla-Firefox and Google-Chrome.
The center point in Chrome and Firefox seems wrong and so the gear move out of his position.
For the rotation i used following code:
function infRotate(el, time, cw) {
var box = el.getBBox();
el.transform('r0,' + box.cx + ',' + box.cy);
if (cw)
el.animate({transform: 'r360,' + box.cx + ', ' + box.cy}, time, mina.linear, infRotate.bind(null, el, time, cw));
else
el.animate({transform: 'r-360,' + box.cx + ', ' + box.cy}, time, mina.linear, infRotate.bind(null, el, time, cw));
}
What i have to do for Firefox and Chrome to find right center point?
Thanks for your help.
Found solution based on #lan's comment.
The gear was in a group, which contains a X/Y - transformation.
So I try to remove each group and layer in the svg file. To see clearly the nesting of objects, I work with the XML-Editor in Inkscape.
Each object must be moved to his original position by using relativ-transformation. Using relativ movements prevent inkscape to print out translations attributes to groups.
Steps to move object relativ in Inkscape:
Go to Edit -> Select All in All Layers
Go to Object -> Transform
In Transform panel:
Uncheck Relative move and check Apply to each object separately
Move object to target position
After clean up the svg file, firefox and chrome calculate the right values too and the gear is now rotation well (see updated code on codpen)
Maybe it exist a better solution to tell Inkscape not working with transformation-attributes, but i didn't found it yet.
So if you work with animated SVG, be sure that the file is has no unnecessary groups and layers and keep attentions on transformation.
Joel except of taking center by using box.cx and box.cy. take center by dividing width and height of container by 2 and then set with it.