I'm converting a JavaScript library to Haxe. In this library, there is an animated effect constructed with many of shapes. So I used the OpenFL library to render shapes.
But now I have a technical problem with transformation.
Some of the shapes has the child shapes so it's transform should be applied to the child shapes too.
For example, please imagine shapeC is attached on shapeB and, shapeB and shapeD are also attached on shapeA. In this case, shapeB, shapeD should be transformed by both of transformA and their own transform and, shapeC also should by transformA, transformB and transformC.
To achieve this, is it a good solution to render the same level shapes in one graphic and apply the parent's transform to that graphic? (on above example, render shapeB and shapeD to one graphic and a apply transformA to that graphic)
I think it's not a good optimized solution to calculate the final transform from all parents transforms and apply that to all vertexes of that shape. Please tech me the best optimized solution for rendering.
Any suggestion will be welcome.
And if there is any confused things on this question, please pardon me and let me check.
You can use the Sprite class:
var parentShape = new Sprite ();
parentShape.graphics.beginFill (0xFF0000);
parentShape.graphics.drawRect (0, 0, 100, 100);
var childShape = new Sprite ();
childShape.graphics.beginFill (0x00FF00);
childShape.graphics.drawCircle (0, 0, 50);
childShape.x = 200;
childShape.y = 200;
parentShape.addChild (childShape);
addChild (parentShape);
Each shape will use its own canvas element, so if you create a lot of shapes, you may decide to flatten it into a single image when you are ready. This is possible using cacheAsBitmap or bitmapData.draw
parentShape.cacheAsBitmap = true;
...or
removeChild (parentShape);
var bitmapData = new BitmapData (Math.ceil (parentShape.width), Math.ceil (parentShape.height), true, 0);
bitmapData.draw (parentShape);
var bitmap = new Bitmap (bitmapData);
addChild (bitmap);
I'd like to create choropleth map of Czech Republic. Inspired by this article http://bl.ocks.org/mbostock/4060606, I have created this
http://jsfiddle.net/1duds8tz/2/
var width = 960;
var height = 500;
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height);
var offset = [width / 2, height / 2];
var projection = d3.geo.mercator().scale(6000).center([15.474, 49.822]).translate(offset);
var path = d3.geo.path().projection(projection);
queue().defer(d3.json, "..map.geojson").await(ready);
function ready(error, reg) {
var group = svg.selectAll("g").data(reg.features).enter().append("g");
group.append("path").attr("d", path).attr("fill", "none").attr("stroke", "#222");
}
When I tried to fill svg path with some color, I ended on this
http://jsfiddle.net/1duds8tz/3/
group.append("path").attr("d", path).attr("fill", "red").attr("stroke", "#222");
There are odd values in path d attribute.
My GeoJSON data must be somehow faulty but I can't figure what is wrong.
Everything looks right here: https://gist.github.com/anonymous/4e51227dd83be8c2311d
Your geoJSON is corrupted and as a result your polygons are being drawn as the interiors of an infinitely bounded polygon. That's why when you attempt to give a fill to the path, it goes beyond the extent of the screen but still displays the border just fine. I tried to reverse the winding order of your coordinates array, and that seemed to fix all of them except for "Brno-venkov", which might be the source of your problems (especially given its administrative shape).
I'd suggest going back to where you created the original GeoJSON and try to re-export it with simplification. If you want to reverse the coordinates on your GeoJSON to correct the winding order, that's pretty simple:
geodata = d3.selectAll("path").data();
for (x in geodata) {geodata[x].geometry.coordinates[0] = geodata[x].geometry.coordinates[0].reverse()}
But this won't fix the problem polygon, nor will not reversing its coordinates.
In case you are familiar with svg manipulation you can try geojson2svg. This allows you manipulate svg in standard way but you have to code a little more. In case your application requires d3 for many other purpose then d3 is best solution.
I've got exactly the same problem with Mapzen's .geojson files.
.reverse()-ing isn't good enough, if you can't make sure all your data has the same winding order.
I solved it with this one:
https://www.npmjs.com/package/geojson-rewind
You'll need to have npm & require available
Install it, and save it to your project
npm i -g geojson-rewind
Import it, to make it useable
var rewind = require('geojson-rewind');
Use it on the data, in this case:
req = rewind(req);
Tip: If you are working with static data, you can do this only once on the console, and you're good to go.
Here's the fiddle: http://jsfiddle.net/DevChefOwen/CZ6Dp/
var text = g.append("text")
.style("font-size",30)
.style("fill","#000")
.attr("dy",0)
.append("textPath")
.attr("xlink:href","#yyy")
.style("text-anchor","left") // using "end", the entire text disappears
.text("some text");
I've tried a number of different things to no avail. The left align is the easy part. If you did a middle, though, you see only "text" instead of "some text", implying that "some" is just hidden because it went "out of span" for the given arc.
If, however, I added:
.attr("startOffset","39%")
(as in here: http://jsfiddle.net/DevChefOwen/2H99c/)
It would look right aligned, but outside of programmatically trying to get the width/height of the text element and look for sharp changes in width/height (which seems wrong and likely error-prone), I can't seem to find a way to right align the text.
I've also tried using an SVG path (essentially a curved arc line) and the same disappearing act happens with the text when "text-anchor" is set to "left".
Thanks ahead for your time!
The question is somewhat confusing matters. The issue isn't aligning text at the end of the path -- that's easy to do with "text-anchor"="end" and "startOffset"="100%".
However, using those settings with the path created by the d3 arc function, you end up with the text cornering around the end of the inside curve and the left straight edge, to the end of the path as defined by the arc function:
http://jsfiddle.net/CZ6Dp/8/
The real issue is that the path that you want the text to be aligned along (the outside arc of the shape) is only one segment of the path that defines the shape.
(By the way, "left" and "right" are not valid values for the "text-anchor" property, and will just be ignored).
The answer by #defghi1977 gives one way to approach the problem, by figuring out the length of the path segment that you do want to use and adjusting the start offset accordingly.
Another way to approach the problem is to create a separate path (not drawn on screen) that represents only the part of the path that you want to be used for positioning text.
There are a number of possible ways to create a path that only represents the outside arc (some example code here). #defghi1977's approach of grabbing it from the existing path with regular expressions is probably the most efficent for your situation. But instead of just creating a temporary element to calculate a length, I actually have to add the new path to the DOM so it can be used as the reference path for the <textPath> element. (Which I suppose is the downside to this approach -- twice as many DOM elements!)
var path = g.append("svg:path")
.attr("d", arct)
.style("fill","#ccc")
.attr("transform", "translate("+cfg.w/2+","+cfg.h/2+")")
.each(function(d,i) {
var justArc = /(^.+?)L/;
//grab everything up to the first Line statement
var thisSelected = d3.select(this);
var arcD = justArc.exec( thisSelected.attr("d") )[1];
defs.append("path")
.attr("id", "yyy") //normally the id would be based on the data or index
.attr("d", arcD)
.attr("transform", thisSelected.attr("transform") );
//if you can avoid using transforms directly on the path element,
//you'll save yourself having to repeat them for the text paths...
});
var text = g.append("text")
.style("font-size",30)
.style("fill","#000")
.attr("dy",0)
.append("textPath")
.attr("xlink:href","#yyy")
.style("text-anchor","end")
.attr("startOffset","100%")
.text("some text");
http://jsfiddle.net/CZ6Dp/9/
Again, factoring in the extra DOM load #defghi1977's method is probably slightly preferrable, although this version has the benefit of not being dependent on browser support for getTotalLength. But as far as I know that method is fairly well implemented.
So just consider this an alternate approach for completeness' sake.
This path is constructed by 4(or 5) path segments.
So, this probrem will be solved to get first arc path length.
But I don't know how to get sub path length by using d3.js, thus I use svgdom directly.
I tried to fix your code. If this code is not what you hope, I'm sorry.
path-anchor attribute to end.
define function to get startOffset value.
var path = g.append("svg:path")
.attr("id","yyy")
.attr("d", arct)
.style("fill","#ccc")
.attr("transform", "translate("+cfg.w/2+","+cfg.h/2+")");
var text = g.append("text")
.style("font-size",30)
.style("fill","#000")
.attr("dy",0)
.append("textPath")
.attr("xlink:href","#yyy")
//.style("text-anchor","left") // using "end", the entire text disappears
.attr("text-anchor", "end")
.text("some text")
.attr("startOffset",function(){
var d = document.getElementById("yyy").getAttribute("d");
var tmp = document.createElementNS("http://www.w3.org/2000/svg" ,"path");
//get the arc segment of path
var arc = d.match(/(^.+?)L/)[1];
tmp.setAttribute("d", arc);
//return offset position
return tmp.getTotalLength();
});
I think the confusion comes from the meaning of text-anchor - it's not "relative to where on the parent will I justify" but rather "what part of me should I align to the start".
You're right to try to use startOffset to move the origin. Since the outer radius of your path is longer than the inner radius, the correct start offset is a little more than half of the path (around 53%).
Just a little more twiddling with your settings and you should have it. Here's a fiddle with my interpretation of what you're looking for.
I am trying to create some login buttons based on the RaphaelJs icons, but on the example page there is only the Twitter paths that are available!
So I am looking to understand how to extract the SVG paths from Inkscape and update the example on http://jsfiddle.net/aqoon/LN23r/5/ for Google using http://upload.wikimedia.org/wikipedia/commons/2/28/Google_free_icon.svg file
var facebookBtn = "",
googleBtn = "M47.446122,148.46699L47.463496,149.0968L47.430196,149.09969C47.363594,148.8854247.278172,148.7259147.173az931,148.62119C47.069686,148.5164646.943725,148.464146.796048,148.4641C46.681186,148.464146.582493,148.4875146.499968,148.53432C46.417441,148.5811346.340465,148.6571446.269039,148.76235C46.124256,148.9775946.051865,149.2237246.051865,149.50074C46.051865,149.6329846.069239,149.7577346.103987,149.875C46.138734,149.9922846.189408,150.095846.256009,150.18556C46.385347,150.359346.54316,150.4461746.729448,150.44617C46.802803,150.4461746.871816,150.4307346.936487,150.39984C47.001155,150.3689547.054242,150.3264847.095748,150.27243C47.152694,150.1971547.181168,150.1078647.18117,150.00458C47.181168,149.9090347.155349,149.8400247.103711,149.79755C47.05207,149.7550846.96689,149.7338446.84817,149.73384L46.84817,149.70054L47.771883,149.70054L47.771883,149.73384C47.686942,149.7415647.62734,149.7533947.593076,149.76931C47.558809,149.7852447.533472,149.8125147.517065,149.85111C47.498724,149.8935947.489555,149.9780447.489557,150.10448C47.489555,150.1392347.491485,150.1932847.495348,150.26664L47.498244,150.29994C47.461564,150.2864347.434538,150.2796747.417165,150.27967C47.395929,150.2796747.368903,150.2907747.336087,150.31297C47.252112,150.3708847.154625,150.4152847.043626,150.44617C46.932625,150.4770646.815351,150.492546.691804,150.4925C46.521925,150.492546.368697,150.4635546.23212,150.40563C46.095541,150.3477245.984782,150.2647145.899844,150.15661C45.836139,150.0755345.787154,149.9790145.752889,149.86704C45.718624,149.7550845.701491,149.6363545.701491,149.51088C45.701491,149.338145.732378,149.1773945.794152,149.02875C45.855926,148.8801145.942795,148.7575346.054761,148.661C46.146456,148.5828246.252147,148.5224946.371835,148.48002C46.491521,148.4375646.617964,148.4163246.751165,148.41632C46.828382,148.4163246.900049,148.4235646.966168,148.43804C47.032284,148.4525247.102503,148.4761647.176826,148.50898L47.262248,148.54807C47.283481,148.5567647.302785,148.561147.320161,148.5611C47.358768,148.561147.388207,148.5297347.408479,148.46699L47.446122,148.46699z",
twitterBtn = "M14.605,13.11c0.913-2.851,2.029-4.698,3.313-6.038c0.959-1,1.453-1.316,0.891-0.216c0.25-0.199,0.606-0.464,0.885-0.605c1.555-0.733,1.442-0.119,0.373,0.54c2.923-1.045,2.82,0.286-0.271,0.949c2.527,0.047,5.214,1.656,5.987,5.077c0.105,0.474-0.021,0.428,0.464,0.514c1.047,0.186,2.03,0.174,2.991-0.13c-0.104,0.708-1.039,1.167-2.497,1.471c-0.541,0.112-0.651,0.083-0.005,0.229c0.799,0.179,1.69,0.226,2.634,0.182c-0.734,0.846-1.905,1.278-3.354,1.296c-0.904,3.309-2.976,5.678-5.596,7.164c-6.152,3.492-15.108,2.984-19.599-3.359c2.947,2.312,7.312,2.821,10.555-0.401c-2.125,0-2.674-1.591-0.99-2.449c-1.595-0.017-2.608-0.521-3.203-1.434c-0.226-0.347-0.229-0.374,0.14-0.64c0.405-0.293,0.958-0.423,1.528-0.467c-1.651-0.473-2.66-1.335-3.009-2.491c-0.116-0.382-0.134-0.363,0.256-0.462c0.38-0.097,0.87-0.148,1.309-0.17C6.11,10.88,5.336,9.917,5.139,8.852c-0.186-1.006,0.005-0.748,0.758-0.46C9.263,9.68,12.619,11.062,14.605,13.11L14.605,13.11z",
yahooBtn = "";
$('.twitterBtn').each(function(i) {
paper = Raphael($(this)[0], 40, 40)
paper.path(twitterBtn).attr({
"fill": "#333"
})
})
$('.googleBtn').each(function(i) {
paper = Raphael($(this)[0], 40, 40)
paper.path(googleBtn).attr({
"fill": "#333"
})
})
I tried to strip the SVG file so that I just have one layer and only the 'G' but on the http://jsfiddle.net/aqoon/LN23r/5/ is not being displayed, what am i missing?
Also how do i add extra layers to the var googleBtn, as when I open the http://upload.wikimedia.org/wikipedia/commons/2/28/Google_free_icon.svg in Inkscape there are many layers and paths?
Raphael does not support ( SVG files in Raphael, can they be used?… ) loading whole SVG images and using those as paths, so the only reasonable option here is to extract separate paths and store them in some sort of datastructure, like in the example with the tiger ( http://raphaeljs.com/tiger.html ) -- check the source code there.
The SVG files can also have rather strange internal coordinate system, so it pays to adjust the view box after loading one, like this
var path = paper.path(googleBtn).attr({
"fill": "#333"
});
var bbox = path.getBBox();
paper.setViewBox(bbox.x, bbox.y, bbox.width, bbox.height);
To group different elements, one can use Raphael.Set http://jsfiddle.net/LN23r/40/
var shadow = paper.path(googleBtn).attr({"fill": "#0F0", "stroke":"none"}).translate(0.08,0.08);
var path = paper.path(googleBtn).attr({"fill": "#333", "stroke":"none"});
var set = paper.set(shadow, path);
var bbox = set.getBBox();
paper.setViewBox(bbox.x, bbox.y, bbox.width, bbox.height);
When grouping elements, note that they have transformation matrices associated with them in the SVG file (e.g. transform="matrix(204.67566,0,0,204.67566,-9225.9642,-30242.949)"), which affects the respective position and scale of the elements.
On the whole, the process of porting paths from SVG is not entirely trivial, but manageable. There is also a plugin that may help you with this, see https://github.com/wout/raphael-svg-import
I'm not sure how to fade in and out a path and the drop shadow I've created for it:
var p = "M10,10L810,10L810,190L10,190L10,10";
var s = "M16,16L816,16L816,196L16,196L16,16";
var paper = Raphael(100, 100, 830, 210);
var shadow = paper.path(s);
shadow.attr({stroke: "none", fill: "#999999", opacity:0.1});
shadow.blur(4);
var c = paper.path(p);
c.attr({fill:"#ffffff", stroke:"none"});
Do I have to manually animate c and shadow at the same time? Is there a way I can just tell this particular paper to fade everything inside of it?
put your path and your shadow in a set. apply your animation to the set and it'll affect both.
Here's a really terrible example where I move both at once.
Maybe animating the drop shadow path to match the specification of the other path?
http://www.irunmywebsite.com/raphael/additionalhelp.php?q=fadingdropshadow
the amadan answer is incorrect. please discard it.
If you put elements in a set, the glow will be part of the set but set is logical only.
it means that if you animate the set, your shadow animation will look wierd.
use set only to refer to logical zoning for events for instance.
best is to animate the shadows in // of the other objects.