How do I apply d3.behavior.drag() to the following arc?
var arc = d3.svg.arc()
.innerRadius(50)
.outerRadius(70)
.startAngle(45 * (pi/180)) //converting from degs to radians
.endAngle(3) //just radians
vis.append("path")
.attr("d", arc)
.attr("transform", "translate(200,200)")
I want to be able to drag the arc around. I have not been able to see anything that uses the drag behavior on any SVG path based object (only for basic elements like circle, rectangle, etc.)
The closest thing I can find related to dragging is this:
http://bl.ocks.org/mbostock/1557377
Though it appears that if you try to ".on("drag", dragmove) for the appended path (.append("path")) "d" comes out as undefined. And if you attach ".on("drag", dragmove)" to the arc itself, the event doesn't appear to fire...)
Drag is a behaviour that you create and then apply to the elements you want to execute that behaviour. There should be no issue applying it to an arc.
So with your arc (minor modification to make the translation accessible):
var position = [200,200];
var arc = vis.append("path")
.attr("d", arc)
.attr("transform", "translate(" + position + ")");
Start by creating the behavior you want. Our drag will update the translation of the arc:
var drag = d3.behavior.drag()
.on("drag", function(d,i) {
position[0] += d3.event.dx;
position[1] += d3.event.dy;
d3.select(this)
.attr("transform", function(d,i){
return "translate(" + position + ")"
})
});
Now we attach the behaviour to the arc:
arc.call(drag);
You can try it yourself here.
Related
I am new on D3 v4 and D3 in general. I created a rectangle and I can drag it on the canvas. Now I want to dynamically rotate the rectangle basing on the circle's radius.
You can check my code here:
https://jsfiddle.net/n4m1r8nb/208/
I also tried to add rotate attribute on drag function, but if I add it, the rectangle move following the mouse as per x and y definition in the snippet you can see here below, without rotating, and an error appear ".rotate is not a function".
var drag = d3.drag().on("drag", function () {
d3.select(this)
.rotate(d3.event.x)
.attr("x", d3.event.x)
.attr("y", d3.event.y);
console.log("X: ", d3.event.x)
console.log("Y: ", d3.event.y)
})
You can see what I mean in this pic (http://imgur.com/a/APbu9). I wanna rotate the black rectangle as per the screenshot in that url.
Thanks in advance.
I have created a fiddle to demonstrate this: https://jsfiddle.net/hsspve49/
Relevant part of the code are in the drag handler:
var drag = d3.drag().on("drag", function () {
var rect = d3.select(this);
var theta = Math.atan2(d3.event.y - height / 2, d3.event.x - width / 2) * 180 / Math.PI
rect
.attr("x", d3.event.x)
.attr("y", d3.event.y)
.attr('transform', `rotate(${theta + 90}, ${d3.event.x}, ${d3.event.y})`)
})
UPDATE: New JSFIDDLE Scaling now working, ditched the defs and rect altogether and just appended the image. But still stuck on translate.
The translating is still not working on zoom. I can set the translate to say -100 for both x and y to get the non-zoomed placement correct. But, when zooming, it's of course still translating it -100 and not the larger value it would need to be to keep it in place.
Appears to need something in the code in the zoom section toward the bottom. Been messing with the part currently commented out, but no luck so far.
// .attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; })
// .attr("x", function(d) { return d.r * k; })
// .attr("y", function(d) { return d.r * k; })
.attr("width", function(d) { return d.r * k; })
.attr("height", function(d) { return d.r * k; })
Here's JSFIDDLE. I have a d3 circle packing with a raster image inside an svg rect within each node. How do you make the image scale when zooming? The container scales, but the image stays small and repeats when zoomed. Been trying to set the defs correctly, but no luck.
var defs = svg.append("defs")
// .data(nodes)
// .enter()
.append("pattern")
.attr("id", "bg")
.attr('patternUnits', 'userSpaceOnUse')
.attr('width', imageWidthHeight)
.attr('height', imageWidthHeight)
// .attr("transform", "translate(40,80)")
.append("image")
// .html("xlink:href", "img/" + function(d) { return d.image; })
.attr("xlink:href", "http://www.public-domain-photos.com/free-stock-photos-4/travel/yosemite/yosemite-meadows.jpg")
.attr('width', imageWidthHeight)
.attr('height', imageWidthHeight)
// .attr("transform", "translate(40,80)");
Also, can't get the container/image to translate into the center of the circle. I've commented those bits out for now because it screws everything up.
Have tried to apply info from these discussions, but still stuck. Thanks.
http://bl.ocks.org/mbostock/950642#graph.json
https://groups.google.com/forum/#!topic/d3-js/fL8_1BLrCyo
How to fill D3 SVG with image instead of colour with fill?
Adding elements to a D3 circle pack nodes
Answer JSFIDDLE
Got it. The trick was changing this bit of horrible:
(d.x - v[0]) * k
to this even worse bit of horrible:
(((d.x - v[0]) * (k)) - ((d.r / 2) * k))
Then the same for y.
Don't get me wrong, I'm grateful for the zoom circle pack template and the genius(es) who put it together. Thank you. It's just for someone at my noob level, the code above looks like a punishment of some kind. :)
The following snippet add d3.geo.circle() object as path element in an svg element. The code is:
var g = svg.append("g");
var circles = g.selectAll("path.circle");
var circle = d3.geo.circle();
circles = circles.data(d3.values(data));
circles
.enter()
.append("path");
.datum(function(d) {
return circle
.origin([d.lon, d.lat])
.angle(0.2)();
})
.attr("class", "circle")
.attr("d", path);
Now, I want to add say, a mouseOver event on these circles, which will log(prints) latitude-longitude information, I cannot access like:
g.selectAll('path.circle')
.on("mouseover", function(d,i) {
console.log(d.lat + "-" + d.lon);
});
I understand that the 'd' represents the d3.geo.circle object, but I could not figure out how to reach the data[i]?
I have a force directed graph and I implemented an autocomplete in order to highlight a node. Basically, once you select a node it is colored in red. I would now like to "zoom" on this node, which is change my window to be 400% the size of the node and the node should be centered in it.
Here are the relevant samples of my code: (or you can directly go to the jsFiddle I setup.)
First the code used to create the svg element:
var w = 4000,
h = 3000;
var vis = d3.select("#mysvg")
.append("svg:svg")
.attr("width", "100%")
.attr("height", "100%")
.attr("id","svg")
.attr("pointer-events", "all")
.attr("viewBox","0 0 "+w+" "+h)
.attr("perserveAspectRatio","xMinYMid")
.append('svg:g')
.call(d3.behavior.zoom().on("zoom", redraw))
.append('svg:g');
Then, as an example, the function used to redraw the directed graph on "normal" zoom.
function redraw() {
trans=d3.event.translate;
scale=d3.event.scale;
vis.attr("transform",
"translate(" + trans + ")"
+ " scale(" + scale + ")");
}
Here are the nodes of my graph:
vis.selectAll("g.node")
.data(nodes, function(d) {return d.id;})
.enter().append("g")
.append("circle")
.attr("id", function(d){return "circle-node-"+ d.id})
.attr("fill","white")
.attr("r","50px")
.attr("stroke", "black")
.attr("stroke-width","2px");
And finally here is my autocomplete.
$(function() {
$( "#tags" ).autocomplete({
source: nodes; //...
select: function( event, ui){
// ...
vis.selectAll("#circle-node-"+ui.item.value)
.transition()
.attr("fill", "red")
}
})
});
I tried to put as little code as possible so, sorry if I forgot something.
Update Here is a jsFiddle illustrating where I am for now.
The scaling and translation should be handled in the same function where you color the node red. You haven't really described how exactly you want the zoom to behave, but probably the easiest way is to apply translate and scale to the g element containing the graph.
I've changed your jsfiddle to do this; result here. I've assumed that by "400% the size of the node" you mean that the node should be magnified 400%? I've introduced a variable for the zoom factor if you want to change it.
I am currently trying to place a svg:image in the centre of my arc:
var arcs = svg.selectAll("path");
arcs.append("svg:image")
.attr("xlink:href", "http://www.e-pint.com/epint.jpg ")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("width", "150px")
.attr("height", "200px");
I would appreciate it if someone could give me any advice on why it isn't appearing
thanks : http://jsfiddle.net/xwZjN/17/
Looking at the jsfiddle, you are creating the path elements after you try to append the svg:image elements to the them. It should be the other way around. You should first create the arcs and then append the images.
Second, as far as I know, the svg:path element should not contain any svg:image tags. It doesn't seem to display them if you place some inside. Instead what you should do is create svg:g tags with class arc and then use those to place the svg:images
Slightly modifying your jsfiddle could look something like this:
var colours = ['#909090','#A8A8A8','#B8B8B8','#D0D0D0','#E8E8E8'];
var arcs = svg.selectAll("path");
for (var z=0; z<30; z++){
arcs.data(donut(data1))
.enter()
//append the groups
.append("svg:g")
.attr("class", "arc")
.append("svg:path")
.attr("fill", function(d, i) { return colours[(Math.floor(z/6))]; })
.attr("d", arc[z])
.attr("stroke","black")
}
//here we append images into arc groups
var pics = svg.selectAll(".arc").append("svg:image")
.attr("xlink:href", "http://www.e-pint.com/epint.jpg ")
.attr("transform", function(d,i) {
//since you have an array of arc generators I used i to find the arc
return "translate(" + arc[i].centroid(d) + ")"; })
.attr("x",-5)
.attr("y",-10)
.attr("width", "10px")
.attr("height", "20px");
Where I also decreased the size of the images and offset them so that they fit into the arc.