multiple svg layered on canvas, let svg moveable and resizeable - javascript

As we can see, a div can be draggable, resizeable using javascript.
http://jqueryui.com/draggable/
svg is also a html element,
Is it possible that I use css+ jquery to drag, resize the svg.
Here, svg is the whole svg, not the children of svg elements.
I know if I can use group of svg element(g) to translate. But the svg is not changed, position is not changed.
I tried set left, top.
I also tried set x, y.
var svg = d3.select("body").append("svg")
.attr("left", 200)
.attr("top", 200);
.attr("width", 200)
.attr("height", 200);
var svg = d3.select("body").append("svg")
.attr("x", 200)
.attr("y", 200);
.attr("width", 200)
.attr("height", 200);
Is it possible to make it draggable and resizeable?
Div is an DOM element, it can be resizable, dragable.
svg is also a Dom element, can it be resizeable and dragable?
Although, I still has above question, thanks Alvin K: it solves my problem: http://jsfiddle.net/ynternet/bf3zck06/1/
thanks for your time.

A demo for scale and drag: http://plnkr.co/edit/sSXYpwjvVB8m1GVuE40A?p=preview
var svg = d3.select("#chart1").append('svg');
svg.attr("width", 200)
.attr("height", 200)
.attr("viewBox", "0 0 200 200");
var circle = svg.append('circle')
.attr({
r: 30,
cx: 100,
cy: 100
})
.style({
fill: "green",
stroke: "red"
});
var open = false;
circle.on('click', function(e) {
open = !open;
if (open) {
svg.attr("viewBox", "0 0 200 200");
} else {
svg.attr("viewBox", "0 0 400 400");
}
})
circle.on("mousemove", function() {
circle.attr("cx", d3.mouse(this)[0]);
circle.attr("cy", d3.mouse(this)[1]);
});
Yes, you should be able to work it just as other DOM.
If you are using d3, d3 is full capable for the resize and scale. no need to involve jQuery to make things more complicated. You could attach your code in jsbin or plunker
BTW, You need to get to know the different of d3selection.attr and d3selection.style.
attr is used for attribute pairs
style is used for css style.
In the browser war days, they are kind of mess up, but now, we have much better standard for this.

Related

How can I append the one SVG (graph) inside another SVG node (rect) after onclick using d3.js?

I want to populate the data inside the parent graph node, that is I want to add nested svg on click event using d3.js.
I have tried following code but the output is looking very different I want to show the graph inside the after on click event of first svg node.
var svg = d3.select("body").append("svg")
.attr("width", 600)
.attr("height", 400)
svg.append('rect')
.attr('class', 'content shape')
.attr("width", 20)
.attr("height", 20)
.attr("stroke", "black")
.attr("stroke-width", 1)
.style("fill", function (d) {
return d._children ? "lightsteelblue" : "#fff";
})
.on("click", createMap);
var createMap (mapData){
if(mapdata.data){
d3.select(this).attr("width", 200)
.attr("height", 200)
.call(function(d){
var nodeMap = getGeneratedmap(mapdata.data);
return nodeMap
}
}
}
var getGeneratedmap(mapdata){
var generatedmapSVG = d3.select("#subnodeMap").append("svg")
....
....(logic for map generation)
....
return generatedmapSvg
}
<body>
<div id="mainMap">
<svg></svg>
</div>
<div id="subnodeMap"></div>
</body>
Please guide me and give the proper way to solve the above issue. Thanks in advance.
Your first SVG, the var svg is being appended to <body>. The second SVG is being appended to the div with ID #subnodemap, so you are not actually nesting the first SVG inside the second. You probably want to do something like:
var generatedmapSVG = svg.append("svg")
That's about as much as I can help you without a working example in Codepen or similar, hope it gets you towards a solution.

D3:reflect data changes on visualization keeping the same array size. Trying to represent a sort algorithm visually

I'm trying to represent a selection sort visually with d3 to show some students and I'm having problems updating the data once the positions swap(I will add the transitions and delays once it's working). The positional attributes don't seem to be working as well, I don't know why, any ideas?. The codepen is here:
HTML:
<div id="canvas">
</div>
CSS:
rect{
border:1px solid black;
}
JS:
function selectionSort(array,svg){
//where the index will position
var positioningIndex=0;
var aux;
var minIndex;
var minVal=Number.MAX_VALUE;
while(positioningIndex<array.length){
//get the index of the mínimum
for(var i=positioningIndex;i<array.length;i++){
if(array[i]<minVal){
minIndex=i;
minVal=array[i];
}
}
//swap the mínimum to the positioningIndex
aux=array[minIndex];
array[minIndex]=array[positioningIndex];
array[positioningIndex]=aux;
//update visualization
svg.selectAll("rect").data(array);
minVal=Number.MAX_VALUE;
++positioningIndex;
}
return array;
}
var dataSet=[10,7,8,44];
var svg=d3.select("#canvas").selectAll("rect")
.append("svg")
.attr("width", 960)
.attr("height", 500);
var rect=svg.data(dataSet)
.enter()
.append("rect");
rect.text(function(el){
return el;
})
.attr("width", 30)
.attr("height", 30)
.attr("x", function(d, i) {
return i*5;
})
.attr("y", 30);
.style("color","green");
array=selectionSort(dataSet,svg);
You've got a lot of mixing up of html and svg elements going on there
First off, your svg element isn't getting appended:
var svg=d3.select("#canvas").selectAll("rect") // <- selectAll("rect") is causing problems
.append("svg")
No existing rect elements at the start means no svg's getting appended (do you mean to add one for each rect?) Edit: and in fact that one error is the cause of everything that happens afterwards - the selectAll("rect") needs moved to the line where elements are added to the svg - not on the line where the svg itself is added -->
var rect=svg.selectAll("rect").data(dataSet) // rect should really be g
.enter()
.append("rect");
Secondly, and because of the above error, the elements called 'rect' that are added (and added directly to the #canvas id div) aren't svg:rect objects - they're just html elements with the name 'rect' - see Is there a way to create your own html tag in HTML5?. The browser just treats them as inline elements, so none of your x's or y's make a difference they just line up one after the other
Finally, if this was svg you wouldn't be able to add text directly to a rect, you'd need to use a group (g) element and add both rect and text elements to that to keep them associated, and style("transform", translate(x,y)) the group element to move them around.
var g=svg.selectAll("g").data(dataSet) // <-- now changed from rect
.enter()
.append("g") // <-- and here
.attr ("transform", function(d,i) {
return "translate("+(i*35)+" 30)";
})
;
// texts n rects added here
g.append("text").text(function(el){
return el;
})
.attr("dy", "1em")
g.append("rect")
.attr("width", 30)
.attr("height", 30)
;
See http://codepen.io/anon/pen/bwJWEa?editors=1111

d3 + drawing svgs + incorrectly overwriting

this i my jsfiddle that shows 2 piecharts which gives me something like this in the html view:
<svg width="220" height="220">
<svg width="220" height="220">
this is my fiddle where I am trying to insert an svg before the 2 piecharts, but I am not writing it correctly, 1 of the pie charts gets over written.
Can anyone advise how I can have all 3 svgs showing?
All my code as it is in the 2nd fiddle
var width = $(document).width()-50 || 960,
height = $(document).height()-50 ||500;
//attaches/appends a svg tag to a body tag with a width and height
/* COMMENT IN */
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
data=[[10, 5],[32, 24]]
z = d3.scale.category20c();
//attaches/appends a svg tag to a body tag with a width and height
var svg2 = d3.select("body").selectAll("svg")
.data(data)
.enter().append("svg:svg")
.attr("width", 220)
.attr("height", 220)
.append("svg:g")
.attr("transform", "translate(" + 100 + "," + 100 + ")")
svg2.selectAll("path")
.data(d3.layout.pie())
.enter().append("svg:path")
.attr("d", d3.svg.arc()
.innerRadius(10)
.outerRadius(100))
.style("fill", function(d, i) { return z(i); });
NOTE: Some further background is that the first svg is a map that I have working. And What I am trying to do is create a layer on top of this which will be piecharts, in this case just 2, but you have to start somewhere :)
Your interpretation of the results is slightly wrong. The first pie chart doesn't get overwritten, it won't get rendered in the first place. In the commented in version you are appending the svg for your map, which works fine and can easily be identified by the width and height values assigned to it.
When trying to append the SVGs for your pie charts, you are selecting all SVGs in the body by doing d3.select("body").selectAll("svg"). This selection will include the previously appended SVG. You are then binding your data to the selection which will compute the data join. Since you are binding an array of data without any key function the join will be based on the index. Thus, the existing SVG will correspond to the first element in the data array and will be put to the update selection. As your code only works on the enter selection the first pie chart will never be drawn because it doesn't belong to this selection. The second one is output as expected.
One way to work around this is to use a CSS marker class for the pie charts. That way you could limit the second selection to the SVGs having the marker class:
var svg2 = d3.select("body").selectAll("svg.pie") // select with class "pie"
.data(data)
.enter().append("svg:svg")
.attr("width", 220)
.attr("height", 220)
.classed("pie", true) // set marker class for all pie charts
.append("svg:g")
.attr("transform", "translate(" + 100 + "," + 100 + ")")
Because no SVG is present having class pie, all your data will end up in the enter selection rendering an SVG containing a pie chart for every data element as expected. I have updated your JSFiddle to show the effect.

Get pixel width of d3.js SVG element after it's created with width as percentage

I want to place a svg bar chart into a certain div and fail to get the calculated svg width if I set the attribute "width" as a percentage. I would like to stick to giving the size as a percentage to be able to calculate bar widths etc. from the svg size.
The div for the chart:
<div id="chart"></div>
The code for appending the SVG, place in a function before that. The function is called to put data in a bootrap modal:
var prodChart = d3.select("#chart");
var w = "100%";
var h = "100%";
// remove existing svg in div
prodChart.select("svg").remove();
var chartSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","prodchartsvg")
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
To get the calculated size in pixels, I already tried this:
$("#prodchartsvg").width(); // not working, returns 100
$("#chart").width(); // not working, returns null
prodchartsvg.node().getBBox(); // prodchartsvg.node is not a function
d3.select("#prodchartsvg").style("width"); // returns 100%
chartSVG.style("width"); // returns auto
d3.select("#prodchartsvg").node().getBoundingClientRect(); // returns 0 for all parameters
d3.select("#chart").node().getBoundingClientRect(); // returns 0 for all parameters
I realise some of the above functions I tried are unsuitable in this context (DOM, object, ...).
Can you give me an idea how to get the svg width in pixels instead of a percentage?
Thank you for the help!
As #nrabinowitz commented, your problem is most probably caused by the fact that your SVG is not visible in the screen at the moment when you attempt to retrieve its width.
Test case
In the following snippet, we compare the value returned by $("#prodchartsvg").width() for two svgs, one hidden and one displayed.
var prodChart = d3.select("#chart");
var w = "100%";
var h = "100%";
// remove existing svg in div
prodChart.select("svg").remove();
var hiddenSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","hiddenSVG")
.style('display', 'none') // not shown on screen
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
$('#result').html('hiddenSVG width is ' + $("#hiddenSVG").width())
var shownSVG = prodChart.append("svg")
.attr(":xmlns:svg", "http://www.w3.org/2000/svg")
.attr("id","shownSVG")
.style("width", w)
.style("height", h)
.attr("class", "thumbnail");
$('#result').append('<br>shownSVG width is ' + $("#shownSVG").width())
svg {background-color: #eee}
<div id='chart'></div>
<div id='result'></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"></script>
How to fix
Try one of these solutions:
make sure the chart is displayed on screen whenever you compute the width
make sure the DOM is loaded whenever you compute the width. Either move you script at the end of the html body, or wrap your code inside a jQuery DOM ready function...

Changing size of rect to fit inside text

I have some text I append to an svg object with D3.js using append('text').
My code looks like this:
var countries = svg.append("svg:g")
.attr("id", "countries");
var stateTexts = svg.append('rect')
.attr('x', xstateText)
.attr('y', ystateText)
.attr('width', 'auto')
.attr('height', 'auto')
var stateText = svg.append('text')
.attr('x', xstateText)
.attr('y', ystateText)
.style("font-family", "Arial")
.style("font-size", "14px")
.style("font-weight", 'bold');
What I'd like is to put that text "inside" a rect which changes size based on the length of the text I append. The rect would have a stroke of 1px so as to give the appearance of a box.
How can I accomplish this? Obviously, width and height can't be set to auto (css properties). I need something else there that can work native to D3.
Edit: Confused by the downvote..
You can't do this automatically in SVG -- the dimensions of the text have to be computed and the rectangle added accordingly. Fortunately, this is not too difficult. The basic idea is illustrated in this function:
function mkBox(g, text) {
var dim = text.node().getBBox();
g.insert("rect", "text")
.attr("x", dim.x)
.attr("y", dim.y)
.attr("width", dim.width)
.attr("height", dim.height);
}
Given a container and a text element, compute the dimensions of the text element (the text must be set for this to work correctly) and add a rect to the container with those dimensions. If you want to get a bit fancier, you could add another argument that allows you to specify padding so that the text and the border are not immediately next to each other.
Complete demo here.

Categories

Resources