resize nested and imported svg with svg.js - svg

heres a pen of what i'm trying to do:
http://codepen.io/amcc/pen/RVVRPX/
(simplified code below too)
i'm importing raw svg, then nesting it - as this is needed to allow it to be dragged. I want to be able to rescale the svg, whats the best way?
line 12 shows me trying to use size() which isn't doing much
var rawsvg1 = '<g><path d="M265.8,171.5V49H297v122.5H265.8z"/></g>';
var draw = SVG('drawing').size('100%', '100%');
var groupContainer = draw.nested();
var group1 = groupContainer.nested();
group1.svg(rawsvg1);
//change group1 attributes
group1.size(50, 50);

then nesting it - as this is needed to allow it to be dragged.
Why do you think that?
There is no need for all those nested <svg> elements you are creating. A group (<g>) works just as well.
And you can resize the content you are "importing" by using the transform functions.
var draw = SVG('drawing').size('100%', '100%');
var group1 = draw.group();
group1.svg(rawsvg1);
//change group1 attributes
group1.attr('fill', '#fff');
group1.translate(200,100).scale(0.5,0.5);
//make group1 draggable
group1.draggable();
group1.draggable().on('dragmove', function(e){ })
Updated codepen

Related

`use` apparently converting what it thinks are shortened hexadecimal colour strings

I have an application where a bunch of glyphs get loaded into defs and then placed on a game board (https://github.com/AbstractPlay/renderer). I was running into this odd issue where sometimes pieces wouldn't get rendered. I narrowed the problem down and am asking for assistance.
Here's the minimal example (https://jsfiddle.net/krtwhuLb/3/):
var canvas = SVG().addTo('#canvas').size('100%', '100%')
// create your example here
const group1 = canvas.defs().group().id("A2");
group1.circle(200).translate(10,10).fill("red");
const group2 = canvas.defs().group().id("A20");
group2.circle(200).translate(50,50).fill("green");
const group3 = canvas.defs().group().id("A200");
group3.circle(200).translate(90,90).fill("blue");
const got1 = canvas.findOne("#A2");
canvas.use(got1);
const got2 = canvas.findOne("#A20");
canvas.use(got2);
const got3 = canvas.findOne("#A200");
canvas.use(got3);
If you look at the resulting SVG, you'll see that the <use> tag for #A20 got converted into <use xlink:href="#aa2200"></use>, which of course doesn't exist in defs and so the green circle doesn't display.
I can add() it, though, and it displays (https://jsfiddle.net/nmas40k7/). This happens in Firefox too.
I just don't know if this is some weird browser thing or if it's happening in SVG.js itself or what. Any assistance would be appreciated. Thank you!!

Snapsvg - get height and width of text element

I am altering the elements contents using
svgTextLines[name].node.innerHTML = line.val();
svgTextLines[name].node.textContent = line.val();
and trying to get the height and width of the element after the content has changed but I cant seem to find any property in the elemement.node object that gets updated. As the element has not transformed I can understand why but is there a way I can get this information?
Regards
I'm using SnapSVG and I had the same problem.
You can use getBoundingClientRect to get an object with this parameter.
var s = Snap("#svg");
Snap.load("mascot.svg", function (f) {
s.append(f);
var rect = s.searchAll("g")[0];
var dimens = rect.node.getBoundingClientRect();
console.log(dimens.width)
});

d3 create SVG path from array

Following this example
http://jsfiddle.net/4xXQT/
I was able to render the points coordinates stored in one array using D3 as follow
https://jsfiddle.net/il_pres/qq9o1ovt/.
var vis = d3.select("body").append("svg")
.attr("width", 30)
.attr("height", 30);
var regioni = [{regione:'Abruzzo',polygon:{points:'25.171,18.844 25.094,18.582 24.567,17.714 24.015,17.714 23.226,16.899 22.805,16.268 21.911,14.558 21.043,14.427 20.753,15.163 20.043,15.084 19.938,15.741 19.386,16.268 19.386,17.056 18.439,16.899 18.202,17.345 18.334,17.924 18.281,18.582 19.57,19.423 18.939,20.055 18.176,19.581 17.756,20.16 17.808,20.844 18.703,21.08 19.517,21.396 19.491,22.079 20.517,22.185 20.596,22.605 21.122,22.133 21.832,22.658 22.568,22.737 23.094,23.473 23.646,22.553 23.751,21.738 24.646,21.212 25.094,21.738 25.409,22.29 26.25,21.238 26.409,20.475 26.776,19.923 26.303,19.634 26.145,19.292'}},{regione:'Basilicata',polygon:{points:'24.607,15.268 23.476,14.716 23.818,14.4 23.161,13.848 22.556,14.111 22.135,14.005 21.529,14.452 20.53,14.716 19.294,14.111 18.952,13.742 18.426,14.242 18.479,14.926 17.637,14.321 17.295,14.531 17.479,15.426 16.611,16.083 17.216,16.451 17.637,17.556 18.321,18.214 18.453,18.792 19.189,18.871 19.61,18.424 20.662,19.186 20.662,19.581 20.109,19.844 19.978,20.344 20.662,20.081 21.188,20.318 21.372,20.002 21.951,19.713 22.424,19.713 22.949,20.265 23.423,20.896 23.555,21.448 23.187,22.053 23.213,22.658 23.844,23.027 24.475,23.105 25.159,23.605 25.08,23.894 25.58,24.157 26.158,24.157 26.5,23.552 27.105,23.552 27.631,22.816 27.92,22.238 27.579,21.291 26.658,20.475 26.132,19.844 26.053,19.239 25.238,18.45 25.238,17.766 26.001,17.74 26.264,17.398 26.21,16.609 25.764,16.32 24.712,16.136'}}];
vis.selectAll("polygon")
.data(regioni) .enter().append("polygon")
.attr("points",function(d) {return d.polygon.points})
.attr("stroke","red")
.attr("stroke-width",0.1);
Now I was trying to do the same with the same svg shape, this time stored as d coordinates
var regionico =[{Regione:'Abruzzo',polygon:{points:'m 127.945,84.9805 -0.781,2.6172 -5.273,8.6835 -5.508,0 -7.891,8.1528 -4.219,6.308 -8.9292,17.09 -8.6836,1.32 -2.8985,-7.363 -7.1015,0.789 -1.0547,-6.57 -5.5157,-5.266 0,-7.89 -9.4648,1.582 -2.3711,-4.4731 1.3164,-5.7812 -0.5273,-6.582 12.8906,-8.4063 -6.3086,-6.3203 -7.6367,4.7383 -4.1992,-5.793 0.5273,-6.8359 8.9453,-2.3633 8.1445,-3.1641 -0.2617,-6.8242 10.2617,-1.0664 0.7813,-4.1992 5.2656,4.7265 7.0977,-5.2539 7.3632,-0.7812 5.25,-7.3633 5.527,9.1992 1.055,8.1446 8.945,5.2656 4.473,-5.2656 3.164,-5.5157 8.399,10.5157 1.601,7.6367 3.652,5.5195 -4.726,2.8906 -1.582,3.418 -9.727,4.4805'}},{Regione:'Basilicata',polygon:{points:'m 123.746,104.57 -7.637,-4.7458 -9.453,3.4178 -3.961,8.692 -12.3512,2.089 2.1093,9.739 -7.6289,4.734 -5,-5.516 -6.0547,3.418 -7.3633,-7.109 0.5274,-7.891 -2.6172,-3.41 -7.6367,-0.527 0,-6.57 8.1445,-7.9027 0.8008,-6.0352 5.2539,-6.3281 9.4727,-8.1445 3.4101,-9.4727 -3.1562,-6.0547 -5,-7.0898 5.2617,-5.2617 2.1094,-5.2539 2.8906,6.8242 8.9375,0.8008 3.4375,-7.625 16.8128,4.207 3.945,14.9805 12.109,-1.0547 9.981,23.4101 -9.727,5.7891 0,14.4723 -7.617,3.418'}}
but if I use the same code with d as attr.
vis.selectAll("path")
.data(regionico).enter().append("path")
.attr("d",function(d) { return d.polygon.points})
.attr("stroke","red")
.attr("stroke-width",0.1);
it doesn't work.
Any suggestion?
The problem is simply that your SVG isn't big enough to make the path visible -- note in particular how you're first moving more than 100 pixels to the right before starting the path. It works fine if you make the SVG bigger, e.g. 300x300 here.

Ability to freely transform KineticJS objects like FabricJS

I love KineticJS, its speed, marriage with GSAP, but what is making my head spin is there a way to freely transform KineticJS objects like the way in FabricJS? Here is the link reference to what I am trying to say: http://fabricjs.com/customization/
I don't want to use FabricJs as its really slow, and its low performance evident from various unit tests.
I am really looking forward to finding a way to be able to freely transform object in KineticJS as it would make life so much easier.
Is there a way to do it?
Thanks for your help,
Praney
Like markE said, this tutorial on Eric's (creator of KineticJS) tutorial site is the basis for all free transforming within KineticJS.
I'll go into detail about the actual free transform logic, there are 2 main functions:
function addAnchor(group, x, y, name) {
var stage = group.getStage();
var layer = group.getLayer();
//Create the anchor shape
var anchor = new Kinetic.Circle({
x: x,
y: y,
stroke: '#666',
fill: '#ddd',
strokeWidth: 2,
radius: 8,
name: name,
draggable: true,
dragOnTop: false
});
//Calls the update function which handles the transform logic
anchor.on('dragmove', function() {
update(this);
layer.draw();
});
//When the anchor is selected, we want to turn dragging off for the group
//This is so that only the anchor is draggable, and we can transform instead of drag
anchor.on('mousedown touchstart', function() {
group.setDraggable(false);
this.moveToTop();
});
//Turn back on draggable for the group
anchor.on('dragend', function() {
group.setDraggable(true);
layer.draw();
});
// add hover styling
anchor.on('mouseover', function() {
var layer = this.getLayer();
document.body.style.cursor = 'pointer';
this.setStrokeWidth(4);
layer.draw();
});
anchor.on('mouseout', function() {
var layer = this.getLayer();
document.body.style.cursor = 'default';
this.setStrokeWidth(2);
layer.draw();
});
group.add(anchor);
}
The addAnchor function, like the name says, adds a single anchor (or free transform handle) to a Kinetic.Group. By default it uses a Kinetic.Circle but really you can use any shape you want.
group - the group to add the anchor to
x - the x position of the anchor
y - the y position of the anchor
name - name of the anchor (usually describe which position the anchor represents, like topLeft or bottomRight
You'll notice a bunch of events attached to the newly created anchor, most of these are pretty straight-forward but the one you want to pay attention to is the dragmove event - this event is the one that calls the update function which handles all the logic for transforming the group/node. It's useful to note that the update function is called for every pixel that you are dragging an anchor.
The tutorial uses 4 corner anchors (thus calling addAnchor 4 times for each group/node), but if you wanted 8 anchors (4 corners - 4 sides) then you just have to adjust the logic to position the anchors correctly and to move the anchors properly when transforming.
By the way, the reason we're adding Anchors to a Group, is because we need them to group with the node in question, and stick with each node through dragging and transforming.
The second method is the update function:
function update(activeAnchor) {
var group = activeAnchor.getParent();
//Get each anchor inside the group, by name. Keep a standard set of names for every anchor you use and note they have to be names not ids because there will be multiple anchors named .topLeft in your app
var topLeft = group.get('.topLeft')[0];
var topRight = group.get('.topRight')[0];
var bottomRight = group.get('.bottomRight')[0];
var bottomLeft = group.get('.bottomLeft')[0];
var image = group.get('.image')[0];
var anchorX = activeAnchor.getX();
var anchorY = activeAnchor.getY();
// update anchor positions
switch (activeAnchor.getName()) {
case 'topLeft':
//When topLeft is being dragged, topRight has to update in the Y-axis
//And bottomLeft has to update in the X-axis
topRight.setY(anchorY);
bottomLeft.setX(anchorX);
break;
case 'topRight':
topLeft.setY(anchorY);
bottomRight.setX(anchorX);
break;
case 'bottomRight':
bottomLeft.setY(anchorY);
topRight.setX(anchorX);
break;
case 'bottomLeft':
bottomRight.setY(anchorY);
topLeft.setX(anchorX);
break;
}
image.setPosition(topLeft.getPosition());
//New height and width are calculated with a little math
//by calculating the distance between the update anchor positions.
var width = topRight.getX() - topLeft.getX();
var height = bottomLeft.getY() - topLeft.getY();
if(width && height) {
image.setSize(width, height);
}
}
The update function only takes one argument: the activeAnchor which is the anchor being dragged.
After that, it selects the other anchors within the group (using static names that you need to give each node and keep consistent throughout your app) so that we can translate their positions while the activeAnchor is being dragged.
The switch statement can get pretty large if you use 8 anchors instead of 4. This is because you need to consider translating almost all the other anchors while dragging one of them.
For an 8 anchor example: If you drag the topLeft anchor, you need to update the y position of the topRight anchor, the x position of the bottomLeft anchor, and for the topMid and leftMid anchors you need to adjust both the x,y values to stay in between the other anchors.
After updating the anchor position, the function handles the logic to resize the shape. Notice that the shape is selected by var image = group.get('.image')[0]; But, what you can do is use the get function to select by type and do something like:
var shape = group.get('Shape')[0];
Obviously this will work best if you just have 1 shape per group (to transform) + 4 or 8 anchors.
Let me know if you have any other questions or comments! Good luck!
this project adds a transfrom tool (resize) and some nice handlers which can be used as a basis:
https://github.com/soloproyectos/jquery.transformtool
Hi please see my previous answer about this. just search my name. hope it will be a bid help for you :) Transform (Move/Scale/Rotate) shapes with KineticJS
I forgot to include the html tags there so here it is... :)``
<!-- INSIDE THE HEAD TAGS -->
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<!-- I am using jquery transform tool for kineticjs -->
<script type="text/javascript" src="../lib/jquery-1.7.2.min.js"></script>
<script type="text/javascript" src="../lib/jquery.timer-1.0.3.js"></script>
<script type="text/javascript" src="../lib/kinetic-v4.7.4.min.js"></script>
<script type="text/javascript" src="../src/jquery.transformtool-1.0.2.js"></script>
<script type="text/javascript">
//PUT THE JAVSCRIPT SNIPPET HERE FROM THE LINK THAT I PROVIDED ABOVE
</script>
//THE ADD div element with id = canvas and file element with id = files and name files[] INSIDE THE BODY

d3.js selectAll().each on svg path.. undefined?

I'm importing a svg (served as static content from the server) in this way
d3.xml("http://localhost:3000/mysvg.svg", "image/svg+xml", function(xml) {
var importedNode = document.importNode(xml.documentElement, true);
var mySvg = d3.select("#somediv").node().appendChild(importedNode);
then I'm trying to iterate through all svg paths and do something with them
d3.selectAll("#somediv svg path").each(function(d, i) {
console.log(this, d, i);
});
}
what I'm getting is this problem
i is from 1 to number of path, which is correct.
d is undefined instead of being the right svg path element.
this is the svg path element, like this one
<path id="m021" fill="#00AAFF" d="M225.438,312.609c-0.665-1.084-1.062-1.691-2.368-1.963c-0.582-0.121-1.686-0.271-2.265-0.069 c-0.507,0.174-0.637,0.649-1.431,0.368c-0.934-0.33-0.665-1.272-0.71-2.104c-0.597-0.021-1.18,0-1.733,0.262 ...etc" ></path>
I expected d to be the real svg path, why is it not?
EDIT:
A little insight on what I want to do could maybe help.
I have a svg with one path for each district of my town. I want to make some piecharts in the center of each path. I don't have the data now, it will be used for the piecharts. I want to make a mouseover function on the path, and add a little red circle (that in a future step will become the pie chart) on each path.
What is the best way to do this?
Simplifying your original request, let's suppose you want to add a circle in the center of each district. Let's assume that the districts are relatively square. Note that this would be much more simpler if you have geographical data instead of paths.
var svg = d3.select("#somediv svg");
var districts = svg.selectAll("path");
var district_centers = districts[0].map(function(d, i) {
var bbox = this.getBBox();
return [bbox.left + bbox.width/2, bbox.top + bbox.height/2];
});
svg
.selectAll("circle")
.data(district_centers)
.enter()
.append("circle")
.attr("class", "district_circle")
.attr("cx", function(d){ return d[0]})
.attr("cy", function(d){ return d[1]})
.attr("r", 10)
.attr("fill", "red");
According to the API doc for selection.each, d should be the datum, which you will not have if you have not previously called .data() to bind data to the nodes. All you have is pure SVG with no data bound to it.
I notice that your paths do have IDs, so if you have a dataset matching those ID's you can probably bind to it using the keys parameter of the .data function

Resources