I have an array with names of images, I want to crate an svg canvas and position them in correct rows and columns so that it will not overlap. Here is my code for that.
<script type="text/javascript">
var imagesObjects = ["1.png","2.png","3.png","4.png","6.png","3.png","4.png","6.png","1.png","2.png","3.png","4.png","6.png","1.png","2.png"];
var iconsArea = d3.select("#icons").data(imagesObjects)
.enter()
.append("svg:image")
.attr("xlink:href", function(d){
console.log(d);
return "images/"+d;
})
.attr("width", 20)
.attr("height", 20)
.attr("x", function(d, i){
return i*10;
})
.attr("y",function(d, i){
return i*10;
});
</script>
But When I run this, My tags won't append to the canvas. All I get is a blank canvas. Can anyone point my mistake
I'm not quite sure why you say svg canvas, because that are two different things, but you are appending svg:image so i assume you are working with an svg.
Is there an svg element with id="icons"? You have to select all the images you want create as a placeholder and bind the data to it. basically I just added the .selectAll("image").
var iconsArea = d3.select("#icons").selectAll("image")
.data(imagesObjects)
.enter()
.append("svg:image")
.attr("xlink:href", function(d){
console.log(d);
return "images/"+d;
})
.attr("width", 20)
.attr("height", 20)
.attr("x", function(d, i){
return i*10;
})
.attr("y",function(d, i){
return i*10;
});
Related
So I'm sort of new to Javascript and I am trying to create a histogram using d3.js. I've been following tutorials and examples of previously created histograms in d3 but cannot figure out how to make my rectangles appear.
My histogram currently contains 4 bins with the numbers 1, 2, 3 and 4 in each bin symbolizing a color attribute of each data point in my dataset. When I do console.log(d) in the .attr "x" function it will appear as an a kind of array with 4 different indices, each with the total number of data points in my dataset with that specific color. Now I'm trying to make that "array" into rectangles but my width and height functions aren't correct. If someone could explain what d.dx and d.y do any why they're wrong that would be helpful. I'm using d3.v3.min.js as my script src value
d3.csv("data.csv", function(data) {
var map = data.map(function (i) { return parseInt(i.color); })
var histogram = d3.layout.histogram()
.bins(4)(map)
var canvas = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500);
var bars = canvas.selectAll(".bar")
.data(histogram)
.enter()
.append("g")
bars.append("rect")
.attr("x", function (d)
{
//console.log(d)
return d.x * 5; })
.attr("y", 0)
.attr("width",function(d) { return d.dx; })
.attr("height", function(d) { d.y; })
.attr("fill", "steelblue");
});
I updated your plunk as follows.
bars.append("rect")
.attr("x", function(d) { return d.x*100; })
.attr("y", 50)
.attr("height", function(d) { return d.y * 10;})
.attr("width", function(d) { return d.dx*50;})
.attr("fill", "steelblue")
.on("mouseout", function()
{
d3.select(this)
.attr("fill", "steelblue");
})
.on("mouseover", function()
{
d3.select(this)
.attr("fill", "orange");
});
Your code seems to work fine, only your elements are overlapping (also, d3 v4 was referenced instead of v3). What I did is:
multiply d.x by 50 to space the elements
multiplied d.dx by 50 to reduce the overlapping
As to your former questions:
d.x corresponds to the extent of a bin, in your case 0.75 (4 ranges make between 1 and 4 make 0.75: 1+(0.75*4)=4)
*d.y corresponds to the 'height' of a bin, i.e. the number of elements.
I'm trying to build out a simple color chart, as an introductory d3 exercise, and I'm already stuck.
I have the following:
var colors = ["#ffffcc","#c7e9b4","#7fcdbb","#41b6c4","#2c7fb8","#253494"];
var barHeight = 20,
barWidth = 20,
width = (barWidth + 5) * colors.length;
d3.select("body").selectAll("svg")
.data(colors)
.enter().append("rect")
.attr("class", "block")
.attr("width", barWidth)
.attr("height", barHeight - 1)
.text(function(d) { return d; })
.attr("fill", function(d) { return d; });
https://jsfiddle.net/xryamdkf/1/
The text works fine. I see the hex codes, but the height and width are definitely not respected, and I can't seem to set the color.
This works to set the color: .style("background", function(d) { return d; }) but I think that is the text background, not the rect fill.
What am I doing wrong here? How can I make 20x20 rectangles filled with color in d3?
As you are not giving any index and reference of colors array into your function the code will not understand from where to pick colors. try with below code it will help.
d3.select("body").selectAll("svg")
.data(colors).enter().append("rect")
.attr("class", "block")
.attr("width", barWidth)
.attr("height", barHeight - 1)
.text(function(d) {
return d;
})
.attr("fill", function(d,i) { return colors[i]; });
So, a few things. You should call data() on what will be an empty selection of the things you will be adding.
svg.selectAll("rect").data(colors)
.enter().append("rect")
The rect doesn't have a text property. There is an svg text node that shows text and you'll want to add it separately.
I hope this https://jsfiddle.net/xryamdkf/8/ gets you closer.
I'm trying to get the widths of a bunch of text elements I have created with d3.js
This is how I'm creating them:
var nodesText = svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d.name;
})
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return 45;
});
I'm then using the width to create rectangles the same size as the text's boxes
var nodes = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return 25;
})
.attr("width", function(d, i) {
//To Do: find width of each text element, after it has been generated
var textWidth = svg.selectAll("text")
.each(function () {
return d3.select(this.getComputedTextLength());
});
console.log(textWidth);
return textWidth;
})
.attr("height", function(d) {
return 30;
})
I tried using the Bbox method from here but I don't really understand it. I think selecting the actual element is where I'm going wrong really.
I would make the length part of the original data:
var nodesText = svg.selectAll("text")
.data(dataset)
.enter()
.append("text")
.text(function(d) {
return d.name;
})
.attr("x", function(d, i) {
return i * (w / dataset.length);
})
.attr("y", function(d) {
return 45;
})
.each(function(d) {
d.width = this.getBBox().width;
});
and then later
var nodes = svg.selectAll("rect")
.data(dataset)
.enter()
.append("rect")
.attr("width", function(d) { return d.width; });
You can use getBoundingClientRect()
Example:
.style('top', function (d) {
var currElemHeight = this.getBoundingClientRect().height;
}
edit: seems like its more appropriate for HTML elements. for SVG elements you can use getBBbox() instead.
d3.selectAll returns a selection. You can get each of the elements by navigating through the array in the _groups property. When you are determining the width of a rectangle, you can use its index to get the corresponding text element:
.attr('width', function (d, i) {
var textSelection = d3.selectAll('text');
return textSelection._groups[0][i].getComputedTextLength();
});
The _groups property of d3's selection has a list of nodes at [0]. This list contains all of the selected elements, which you can access by index. It's important that you get the SVG element so that you can use the getComputedTextLength method.
You may also want to consider creating the rect elements first, then the text elements, and then going back to the rectangles to edit the width attribute, so that the text elements are on top of the rectangles (in case you want to fill the rectangles with color).
Update:
It's typically preferred that you don't access _groups, though, so a safer way to get the matching text element's width would be:
.attr('width', function (d, i) {
return d3.selectAll('text').filter(function (d, j) { return i === j; })
.node().getComputedTextLength();
});
Using node safely retrieves the element, and filter will find the text element which matches index.
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.
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();