Inside of my d3 code, I generate a rectangle like this:
svg.selectAll("g")
.append("rect")
.attr("width", 30)
.attr("height", 30)
.attr("class", "close-box initial-hidden")
.attr("x", 500)
.attr("y", 110)
.on("click", function() {
$('.initial-hidden').hide();
})
.append("text")
.text("x")
I am using click events on many of my other svg elements and they are working fine, but I can't get the click event on the rectangle to work at all. Am I missing something about SVG rectangle?
Related
As I am learning d3js I am trying to make a small program where there are (planets in solar system) as texts in the HTML DOM and there are ellipses(rings) around a circle (sun). I need to be able to drag the text and as they are dropped it should identify on which ellipse and append into the group. For now, it does not need to check if the location is correct just want to append a circle when the text gets dropped into the circle.
I looked at some Jquery UI and other samples, but I am not having much luck on this issue.
var svg = d3.select('body')
.append("svg")
.attr("id", "program")
.attr("height", 500)
.attr("width", 500);
var sun = svg.append("circle")
.attr("cx", 250)
.attr("cy", 250)
.attr("r", 25)
.attr("fill", "orange");
var mercg = svg
.append("g")
.attr("id", "mercury")
.append("ellipse")
.attr("class", "droppable")
.attr("cx", 250)
.attr("cy", 250)
.attr("rx", 55)
.attr("ry", 45)
.attr("fill", "none")
.attr("stroke", "black");
var venus = svg
.append("g")
.attr("id", "venus")
.append("ellipse")
.attr("class", "droppable")
.attr("cx", 250)
.attr("cy", 250)
.attr("rx", 85)
.attr("ry", 65)
.attr("fill", "none")
.attr("stroke", "black");
var drag = d3.behavior.drag()
.on("drag", dragged)
.on("dragend", dragend);
function dragged(d) {
d3.select(this).attr("cx", d3.event.x).attr("cy", d3.event.y);
}
function dragend(d) {
// Here, How do i find on what ring the item was dropped?
// I want the circle to be on the selected ring group
}
var ex = svg.append("circle")
.attr("transform", 'translate(0,0)')
.attr("cx", 279)
.attr("cy", 212)
.attr("r", 10)
.attr("fill", "blue")
.call(drag);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="mySidenav" class="sidenav">
<ul>
<li draggable="true" ondragstart="drag()" class="draggable"> Mercury</li>
<li draggable="true" class="draggable">Venus </li>
</ul>
</div>
In the end I am looking for something that looks like the image produced at the end.
My solution
You can drag-n-drop html to svg and find drop target. Now you can add if statement to detect if target is ellipse. Also you can add additional ellipse to each existing ellipses with opacity 0 and wider stroke-width parameter to avoid pixel hunting.
update: new fiddle you can drag-n-drop HTML elements to svg orbits and new circle will append (if you drop planet name on its orbit)
I'm trying to get an element with an appended image to transition using the d3.js library; I had successfully achieved this just using a plain circle that transitioned nicely around the screen but now that I've added a png the transition doesn't happen - the png does appear though when the page is refreshed, it just won't move like it did before! My code is below.. your help is appreciated!
<script>
var data = [60, 120, 40, 710, 560, 850];
var data1 = data[0];
var canvas = d3.select("body").append("svg")
.attr("width", 2000)
.attr("height", 2000);
var imgs = canvas.append("image")
.attr("xlink:href", "AWT-Bus.png")
.attr("x", "60")
.attr("y", "60")
.attr("width", "20")
.attr("height", "20")
.attr("cx", 50)
.attr("cy", 200)
.attr("r", 20)
;
imgs.transition()
.duration(data1*100)
.delay(2000)
.attr("cx", 200)
.transition()
.attr("cx", 50)
.attr("cy", 200)
.transition()
.attr("cx", 150)
.attr("cy", 300)
;
The attributes you are changing in your code (cx and cy) are applicable to circles which are described by the x and y co-ordinates of their center (cx and cy) plus the radius (r). This is why your circle example worked.
But images are described by their width, height and the x and y co-ordinates of the upper-left corner of the box (using x and y attributes as shown below).
Different svg elements have different attributes which describe their dimensions and their location on the page, so you need to be aware of the different attributes that each type of element has, perhaps using a reference such as https://developer.mozilla.org/en-US/docs/Web/SVG/Element. Then you can animate your svg element using transition as you have done in your code and changing the value of the appropriate attribute.
var canvas = d3.select("body").append("svg")
.attr("width", 2000)
.attr("height", 2000);
var imgs = canvas.append("image")
.attr("xlink:href", "AWT-Bus.png")
.attr("x", "60")
.attr("y", "60")
.attr("width", "20")
.attr("height", "20");
imgs.transition()
.duration(2000)
.delay(1000)
.attr("x", 200)
.transition()
.attr("x", 50)
.attr("y", 200)
.transition()
.attr("x", 150)
.attr("y", 300);
So I would like to show an image on a path. The pathes are created via topojson coordinates. The points are on the right position on my map. So the next thing is to show a SVG image on that point.
I tried that with appending svg:image, but no chance. I also tried to bring it into the path with the same result. I nowhere can see that image. Here an example with an PNG image. Because at least that should work to exclude SVG issues:
var featureCollection = topojson.feature(currentMap, currentMap.objects.points);
svgmap.append("path")
.attr("id", "points")
.selectAll("path")
.data(featureCollection.features)
.enter().append("path")
.attr("d", path);
svgmap.append("svg:image")
.attr("class","svgimage")
.attr("xlink:href", "pics/point.jpg" )
.attr("x", -20)
.attr("y", -20)
.attr("width", 13)
.attr("height", 13);
Edit
svgimage.append("pattern")
.attr("id","p1")
.attr("patternUnits","userSpaceOnUse")
.attr("width","32")
.attr("height","32")
.append("image")
.attr("xlink:href", "pics/point.jpg" )
.attr("width", 10)
.attr("height", 10);
svgmap.append("g")
.attr("id", "points")
.selectAll("path")
.data(featureCollection.features)
.enter().append("path")
.attr("d", path)
.attr("fill", "url(#p1)");
But still not working.
Edit2
I mentioned that it is an issue with the size. So I now played a bit with the sizes and there I can see some more, but most of them are not fully imaged. Just some pieces of the cirle somehow. Strange thing. I keep on testing:
svgimage.append("pattern")
.attr("id","p1")
.attr("patternUnits","userSpaceOnUse")
.attr("width","10")
.attr("height","10")
.append("image")
.attr("xlink:href", "pics/point.jpg" )
.attr("width", 15)
.attr("height", 15);
Here a picture of the current result (jpg): http://i.imgur.com/T58DA1j.png not yet perfect.
This is when I increase the pointRadius (this is now a SVG): http://i.imgur.com/Z7nZUWk.png
The solution is pretty easy. The size of the picture was just not correctly set. Also the userSpaceOnUse needs to be deleted and if needed you can set the creation position with x and y:
svgimage.append("pattern")
.attr("id","p1")
.attr("width","10")
.attr("height","10")
.append("image")
.attr("xlink:href", "pics/point.jpg" )
.attr("width", 5)
.attr("height", 5)
.attr("x", 1)
.attr("y", 2);
and in the second part it is important to set the pointRadius. You can set it directly on the path or in the definition. If you want to use different sizes later on it makes more sense to set it in the path directly:
.attr("d", path.pointRadius(3.5))
I am working on a d3.js project where I am displaying a number of rectangles to be the same height. The rectangles are connected to a input[type=number] that adjust the height of each group of rectangles. To make animation easier (so I only have to manipulate the svg:defs onchange of the number input), I would like to be able to specify the height of a group of rectangles with a svg:def tag like this:
var svg = d3.select("body").append("svg")
.attr("width", 960)
.attr("height", 500);
svg.append("defs").selectAll(".rectdef")
.data(data).enter()
.append("rect")
.attr("class", "rectdef")
.attr("id", function (d, i){return "rect" + d.name;})
.attr("x", 0) // overridden below
.attr("width", 0) // overridden below
.attr("y", 0) // overridden below
.attr("height", function (d, i){return d.height});
and then to be able to just refine placement x, y and width of the rectangles with something like this:
svg.selectAll(".bar")
.data(data).enter()
.append("use")
.attr("class", "bar")
.attr("xlink:href",function (d){return "#rect"+d.type;})
.attr("x", function(d) { return d.x })
.attr("width", function (d) {return d.w;}) // this does NOT adjust width!
.attr("y", function (d) {return 0;});
This snippet correctly changes the x and y coordinates but it does not properly change the width! Any ideas what's wrong here? Is this a browser issue (I'm using Chrome 24.0.1312.52)? Is width not editable like this on an svg:use tag?
There aren't any problems with the data (I've checked that) and I have been able to confirm that the animation does work correctly.
If you point a <use> element at a <rect> the width/height of the <use> are ignored according to the SVG specification
I recomment you put the <rect> in a <symbol>, and then have the use reference the symbol. That way the width/height of the use will apply to the rect. You probably want to make the rect's width/height 100% within the symbol.
In other words, something like this should work:
svg.append("defs").selectAll(".rectdef")
.data(data).enter()
.append("symbol")
.attr("class", "rectdef")
.attr("id", function (d, i){return "rect" + d.name;})
.append("rect")
.attr("x", 0) // overridden below
.attr("width", "100%") // overridden below
.attr("y", 0) // overridden below
.attr("height", function (d, i){return d.height});
svg.selectAll(".bar")
.data(data).enter()
.append("use")
.attr("class", "bar")
.attr("xlink:href",function (d){return "#rect"+d.type;})
.attr("x", function(d) { return d.x })
.attr("width", function (d) {return d.w;}) // this correctly adjusts width!
.attr("y", function (d) {return 0;});
A set of rectangles is drawn initially with the following enter/append/exit/remove sequence, no problem. When I pass different data (meant to replace the existing data entirely) the new rectangles are drawn on top of the existing rectangles.
I am selecting "lgnds" instead of rect, because I have drawn other rectangles that I don't wish to disturb.
var svg = d3.select("#graph").append("svg")
elements = svg
.selectAll("lgnds")
.data(data, function(d){return d;});
elements
.enter()
.append("rect")
.attr("width", 15)
.attr("height", rectHeight)
.attr("x", 5)
.attr("y", function (d,i){return ((i*rectHeight)+(gap*(i+1)));})
.style("fill", function(d){ return d.color;});
elements
.exit()
.remove();