I am trying to use d3.zoom but there are some issues that I am facing. Below is the simple code that I am using.
var treeGroup = d3.select('.treeGroup'); //parent g element
treeGroup.call(d3.zoom().on('zoom', function() {
console.log(d3.event.transform);
treeGroup.attr('transform', d3.event.transform);
}));
JS Fiddle: https://jsfiddle.net/nohe76yd/9/
Below are the issues that I am facing. (Please refer above JS Fiddle for code)
I can not zoom or pan on tree unless I do it on nodes, text labels or links hence I can not zoom on parent g object.
There is a stutter when I try to pan by clicking and dragging on nodes, text labels or links
When I zoom out an try to pan than tree disappears means there is too much translate.
If you have any idea why d3.zoom is behaving like this, please help me with the solution.
Taken from https://stackoverflow.com/a/20072986/6060606 and adapted to this question:
var rootSVG = d3.select('.rootSVG');
var treeGroup = d3.select('.treeGroup');
rootSVG.call(d3.zoom().on('zoom', function() {
treeGroup.attr('transform', d3.event.transform);
}));
This fixes the jitter and makes the entire SVG "draggable". The excellent explanation as to why this works can be found in the linked SO answer.
Related
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'm building an SVG and animating it in snap.svg because of a lack of IE support for animations (thanks IE). What I have in it's most basic form is a pentagon I'd like to rotate on hover, on mouse out it then resets itself. Code is located in the fiddle below (obviously this is a cut down version of the real thing but it shows the bug clearly still)
The JS is as follows:
// Set up snap on the svg element
var svgElement = Snap("#svg");
// Create the hover on and off events
var hoverover = function() {
svgElement.select('#pentagram-one').stop().animate({transform: 's1r72,500,515'}, 1000);
};
var hoverout = function() {
svgElement.select('#pentagram-one').stop().transform('s1R0,500,515');
};
// Set up the functions for hover in and out
svgElement.select('#pentagram-one').hover(hoverover, hoverout);
Fiddle demo of this behaviour here: http://jsfiddle.net/kfs8o9mw/
The pentagon rotates as expected on the first hover in then out. However when doing it a second time the pentagon zooms in and out a bit which is completely unexpected (it's ends up at the right size but during the animation isn't). And I've been able to replicate this in IE(10), Chrome and Firefox.
Is this a bug in snap.svg or is there something wrong in the code?
Thanks in advance
You simply can remove the s1 you have defined for the scale as show in the following:
Also note that you're using a capital R in the transform event for hoverout, when it should be a lowercase r
http://jsfiddle.net/3phpbef7/2/
I would also define you pentagram element as a variable as you're using it multiple times. I've also included this in the Fiddle above.
Hope this helps
I'm looking at graphing solutions for my responsive webapp. I really like this Raphaël analytics demo: http://raphaeljs.com/analytics.html (Here it is on JS Bin: http://jsbin.com/svg/1/edit)
...but it's not responsive. I found this responsive SVG graph on codepen: http://codepen.io/meloncholy/pen/KxiJA, but I'm looking for a JavaScript integrated solution like Raphaël, not just a static SVG.
Any way to combine the two so as to make the Raphaël graph responsive?
Here's an article by the codepen demo's author on how he made the responsive SVG: http://meloncholy.com/blog/making-responsive-svg-graphs/
The Raphaël Demo you posted renders the graph when the window.onload event is fired. The Demo generates with var r = Raphael("holder", width, height) a stage with a fixed width of 800px and draws the graph somewhere here r.drawGrid(...).
Similar to the codepen.io example you've posted, you could reinit/redraw the whole thing with a different width depending on the width of the window on the window.resize event.
A second approach, you loop through all SVG elements on the window.resize event with a similar var unscale = function (el) { ... } function like in codepen.io example.
Just some ideas...
I am creating a rectangle element in raphael JS as a tooltip on an SVG circle element generated by raphael JS. I am unable to access the coordinates of the center of the circle (which I need to place the rectangle tooltip) using the following code in Internet Explorer..
var c = {};
c.x = parseInt(c_element.node.attributes[0].nodeValue);
c.y = parseInt(c_element.node.attributes[1].nodeValue);
//code for the rectangle element tooltip
r = paper.rect(c.x,c.y,50,50);
Its working fine in Firefox.. But in IE, the tooltip gets created at the upper left corner of the div enclosing my raphael JS paper and not on the center o the circle as desired..
How do I solve this cross-browser issue? Please help..
I don't know for certain why IE doesn't like this, but there's a much easier way:
c.x = c_element.attr("cx");
c.y = c_element.attr("cy");
jsFiddle
If you log the Raphael object to the console, you can check out the "attrs" property to see what's in there. Different for every type of Raphael object.
While we're all here: I highly recommend using an absolutely positioned div for your tooltips. HTML handles things like wrapping text and resizing the tooltip to accomodate different amounts of text much more easily than the SVG text element. See this answer. (Whatever you use, you'll still need to access the coordinates of the circle.)
When I use tipsy on my d3 force directed graph I have a problem: when I set the tipsy gravity to west, the tipsy begins at the upper left corner of my circle. How can I make it begin on the right side of my circle?
Here is the sample of the code I use in d3:
var node = vis.selectAll("g.node")
.data(json.nodes)
.enter().append("svg:g");
node.append("svg:circle")
.attr("r", function(d){return d.credits *5+"px";})
.style("fill", "orange");
$('svg circle').tipsy({
gravity: 'w',
html: true,
title: function() {
var d = this.__data__,
name = d.name;
return name;
}
});
Edit In this question: https://stackoverflow.com/a/10806220/1041692 they say the following:
You could try adding the tooltip to an svg:g that you overlay with the
actual circle, but give zero width and height. Currently it's taking
the bounding box and putting the tooltip at the edge. Playing around
with tipsy's options might help as well.
But either I do it wrong or it doesn't work, it didn't solve my problem.
EDIT 2 This problem also depends on the browser, in chrome the tipsy element is attached on the top left corner of the circle whereas I would like it to be attached on the middle of the right side of the circle. In Firefox, the tipsy appears on the top left of the whole webpage.
The D3 tipsy tutorial actually uses a modified version of tipsy:
http://bl.ocks.org/1373263
It is slightly tweaked to correctly calculate bounding boxes of SVG elements. So copy that source code, rather than using tipsy downloaded from the tipsy site.