I'm using D3 Drag to drag some circles around the canvas. I then have them with fixed positions. However, I wish to position them from a distance from another svg element that I have created:
node.attr("transform", function(d,i) { return "translate ("+ arc[i].centroid() +")" +"translate("+d.x+","+d.y+")"; })
When dragged the circles do not drag until multiple clicks of the mouse & is usually far from the actual mouse hand.
I know I can get rid of the centroid, but would like to know a work around this problem.
http://jsfiddle.net/Zc4z9/10/
The jump you are getting on the first drag attempt results from the fact that the translate on line 57 doesn't match the one further down. To avoid the jump, make lines 57 and 76 match. Change 57 to:
d3.select(this).attr("transform", "translate ("+ arc[i].centroid() +")" +"translate("+d.x+","+d.y+")");
To get this to work, I had to pass d and i as parameters in line 52:
.on("drag", function(d, i) {
Working fiddle: http://jsfiddle.net/Zc4z9/11/
Related
I have fought half a day with seemingly simple problem with no avail so I'd love to get some good advice from stackoverflow geniuses.
I have a demo at http://62.165.130.126/d3-question
I tried to put it into jsfiddle but couldn't get the libraries right, hope this still allows you to dive in.
The problem is demonstrating itself in a d3.js javascript code when rotating text disappears. Or actually part of the rotating text.
Each pair of bars on the page should have two lines of the text rotated 45 degrees (downhill) not to write over the neighbors. If I don't rotate text everything is visible (but overlapping), but if I do, only the first pair of lines is OK. After that the first text disappears (or on some version was misplaced by some tens of pixels seemingly haphazard) but the second text sits right where it should using exactly the same code structure. I have exchanged the values and the problem isn't connected with the values but the order.
Here is the main code in javascript I have written.
$.each(data, function(a_key,a_val){
$.each(a_val, function(form_key,form_val){
$.each(form_val, function(person_key,person_val){
svg.append("rect")
.attr("x", start*2*width+width)
.attr("y", 420-person_val.val1/person_val.val1_max*400)
.attr("width", width-5)
.attr("height", person_val.val1/person_val.val1_max*400)
.style("fill", "red");
svg.append("rect")
.attr("x", start*2*width)
.attr("y", 420-person_val.val2/person_val.val2_max*400)
.attr("width", width-5)
.attr("height", person_val.val2/person_val.val2_max*400)
.style("fill", "green");
svg.append("text")
.attr("text-anchor", "start")
.attr("transform","translate(" + start*2*width+20 + ",430) rotate(45)")
.style("font-size","0.85em")
.text(person_val.pname);
svg.append("text")
.attr("text-anchor", "start")
.attr("transform","translate(" + start*2*width + ",430) rotate(45)")
.style("font-size","0.85em")
.text(person_val.ptype);
start++;
});
});
The application is just a mockup of the real solution but should show the essential problem without distractions. Each pair of bars should have the describing text below them. Currently they aren't centered yet but if things sort out that change should be straightforward.
Anybody any idea how to correct the code?
The problem is with "translate(" + start*2*width+20 + ",430) rotate(45)" code.
One of the results is translate(8020,430) rotate(45). Javascript turned 20 into string. Try to put braces around start*2*width+20 expression.
I've generated a D3 visualization (a force directed graph) that requires zooming and panning. I've got 2 problems however when it comes to zooming, and I can't find any decent examples on how I might overcome these problems:
The first problem is I've followed all the examples I can find about zooming, which involves adding groupings and adding a rectangle to ensure that the entire area is zoomeable. If I style the rectangle a slightly opaque blue then I get SVG that looks like this when I zoom out:
The problem with this is that I can zoom in/out absolutely fine while I've got my mouse over the blue rectangle area. The problem is I want this to be fully opaque, which means that when I zoom right out, it's very easy to place the cursor outside of this box and then you're unable to zoom in. Is there a way I can make the SVG itself zoomeable or pick up on these events?
This is how I go about generating the various layers and the zoomed function:
function zoomed() {
group2.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
svg = d3.select(target)
.attr("pointer-events", "all")
.append("svg");
group = svg.append('svg:g')
.call(d3.behavior.zoom().on('zoom', zoomed))
.on("dblclick.zoom", null);
group2 = group.append("g");
rect = group2.append('svg:rect')
.style("opacity", 0.3)
.attr('width', width)
.attr('height', height);
The second problem I have is that I'm trying to automatically size my text based on this http://bl.ocks.org/mbostock/1846692 example. When I've tried this however I seem to be getting text that renders really poorly. It seems to suffer from:
Being difficult to read
Not appearing contained within the circle
Being so small the entire thing compresses (image 2)
var texts = planets.append("text")
.text(function(d) { return d.name; })
.style("font-size", "24px") // initial guess
.style("font-size", function(d) {
return Math.min( 2 * d.size, (2 * d.size - 8) / this.getComputedTextLength() * 24) + "px";
})
.attr("dx", function(d) { return -d.size; })
.attr("dy", ".35em")
.style("fill", "white");
I thought that SVG would just handle this, I understand that some of the font-sizes can come out small, but if you zoom in should that not all sort itself out?
I've got a JSFiddle http://jsfiddle.net/IPWright83/vo7Lpefs/22/ to demonstrate.
I've not yet managed to work out a resolution to my first issue (regarding the zooming box) however I did manage to track down the text rendering issue.
This was actually because the each circle/node had a stroke property to provide the white border. This was also applying to the text element, and when the font was very small the stroke was much larger than the overall fill of the text. Removing the stroke from the text elements ensured that they rendered even when very small.
I'm trying to implement a slider in mpld3, much like this previous question.
I'm trying to build off of the draggable points example to do this. I'm having a bit of trouble understanding how the following bit of code works:
function dragged(d, i) {
d[0] = obj.ax.x.invert(d3.event.x);
d[1] = obj.ax.y.invert(d3.event.y);
d3.select(this)
.attr("transform", "translate(" + [d3.event.x,d3.event.y] + ")");
}
In particular, what does this refer to in this context. I originally thought that I could replace d3.select(this) with something like d3.select("#"+foo) where foo = this.props.id (at the top of the draw() function). But this doesn't work, as shown in this notebook I made. (The second piece of code doesn't allow you to drag the red dots around).
In case what I'm trying to do isn't clear... have a look at this notebook. I've made a plugin that allows the red square (the slider) to be dragged horizontally. What I would like to do is make dragging the red dot change the position of the blue dot. So I want to do something like:
function dragged(d, i) {
d[0] = obj.ax.x.invert(d3.event.x);
sliderPosition = obj.ax.x(d[0]);
targetPosition = obj.ax.x(-d[0]); // inverted sign
d3.select("#redsquare")
.attr("transform", "translate(" + [sliderPosition,sliderObj.ax.y(d[1])] + ")");
d3.select("#bluedot")
.attr("transform", "translate(" + [targetPosition,targetObj.ax.y(d[1])] + ")");
}
The intended behavior for this simple example is to have the blue dot move in the opposite direction of the red square when it is dragged. The question is, what do I put in place of "#redsquare" and "#bluedot"?
Many thanks.
I know a hacky way to do this. Instead of d3.select(this), you can find the specific element you are interested in the obj element array as follows:
d3.select(obj.elements()[0][i])
There must be a prettier way, though.
I'm using d3.js. I'm trying to rotate an SVG element 360 degrees, so that it spins once and returns to it's original position.
Rotating it 3/4 of the way like this works fine:
thing
.transition()
.attr('transform', 'rotate(270,640,426)')
.duration(6000);
But trying to animate the complete rotation does nothing:
thing
.transition()
.attr('transform', 'rotate(360,640,426)')
.duration(6000);
I think d3 (or maybe this a more general fact about svg transform attribute) sees that the end is the same as the beginning and just takes the shortcut by doing nothing. Similarly, if I do 365 degrees, it only moves +5 degrees.
A. Why is this?
B. What's the right way to do it?
D3 normalizes the SVG transforms; this is the cause for the effect you're seeing. You can do this however with a custom tween function:
function rotTween() {
var i = d3.interpolate(0, 360);
return function(t) {
return "rotate(" + i(t) + ")";
};
}
Complete example here.
I'm attempting to integrate AngularJS with d3 for dragging and resizing. I've managed to create a rect object that is draggable in an SVG element, and resizable using resize handles. The resize handles work as they should, but resizing is choppy when I try to resize in the north or east direction. I created the following Plunk as a demo of the issue: http://plnkr.co/tG19vpyyw0OHMetLOu2U. (I've simplified it to show the issue I've run into, so there's only one resize handle.)
Dragging works as it should, and resizing in the west and south directions works as well (not shown in the demo).
Figured I'd ask the community and see if anyone had run into this before. Thank you all.
The problem is that you're modifying the rect element itself and the enclosing g element. There's a very short delay between setting the size of the rect and the position of the g simply because this has to be done with two separate commands. During this delay, the cursor position relative the the drag rectangle changes, firing a new drag event with values that correspond to the inconsistent intermediate state. This is fixed immediately afterwards (as soon as the attributes of both elements have been adjusted) and a new drag event is fired that fixes the inconsistency, but it is noticeable as a flicker.
The easiest way to fix this is to change both size and position for the rect and nothing for the g element. This means adjusting the position of the drag rectangle as well and makes the code less nice, but avoids the timing/inconsistency problem.
So myrect becomes
var myRect = d3.select(document.createElementNS("http://www.w3.org/2000/svg", "rect"))
.attr("data-ng-width", "{{square.w}}")
.attr("data-ng-height", "{{square.h}}")
.attr("stroke", "yellow")
.attr("stroke-width", 3)
.attr("fill-opacity", 0)
.attr("data-ng-x", "{{square.x}}")
.attr("data-ng-y", "{{square.y}}");
and resizer
var resizer = myGroup.append("rect")
.attr("width", 5)
.attr("height", 5)
.attr("stroke", "blue")
.attr("stroke-width", 1)
.attr("fill-opacity", 0)
.attr("cursor", "nw-resize")
.attr("x", "{{square.x-2.5}}")
.attr("y", "{{square.y-2.5}}")
.call(nresize);
I've updated your code with this solution here.