Modifying Raphael SVG image with jQuery
I have a small application where I need to generate textPath labels for arcs. I'm drawing the arcs via Raphael and that is working great. However Raphael has no support for textPaths. We can add them to the svg element via jQuery but they don't render for some reason. When I duplicate the dynamically generated code statically it renders fine.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="600" height="600">
<desc>Created with Raphaël</desc><defs></defs>
<a title="This is a test entry">
<path fill="none" stroke="#1e79a0" d="M395.2627944162883,355A110,110,0,0,1,199.5099996593139,344.74103073833805" style="stroke-width: 20px; " stroke-width="20" id="This_is_a_test_entry"></path>
</a>
<text>
<textpath xlink:href="#This_is_a_test_entry">This is a test entry</textpath>
</text>
</svg>
With the above code you never see the label for the arc. However when the following is hard coded it renders as expected:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="600" height="600">
<a title="Bacon">
<path id="Arc" fill="none" stroke="#1e79a0" d="M395.2627944162883,355A110,110,0,0,1,201.13265490709162,348.2208261467985" style="stroke-width: 20;" stroke-width="20">
</path>
</a>
<text>
<textPath xlink:href="#Arc">This is an Arc</textPath>
</text>
</svg>
Any insight as to why I'm not seeing my entire svg would be much appreciated.
EDIT:
I've made changes to my javascript and I think it's almos开发者_Go百科t there but the textPath still won't render. Here is the current code, mostly thanks to Femi:
function drawRange(start, end, R, range, id, title) {
var color = "hsb(".concat(Math.round(R) / 200, ",", end / 360, ", .75)");
var key_position = key[id];
var svg_bits = document.getElementsByTagName('svg')[0].childNodes;
var svgns = document.createElementNS('http://www.w3.org/2000/svg', "path");
var path;
range.attr({title: title});
range.animate({arc: [start, end, R]}, 900, ">");
//Add an id to the path element that was just drawn. This doesn't seem to help though.
for(var i = 0; i < svg_bits.length; i++) {
if (svg_bits[i].tagName == "a" && svg_bits[i].getAttribute('title') == title){
path = svg_bits[i].childNodes[0];
}
}
path.setAttribute('id', title);
//Add the name to the key, set the color and unhide the element.
$(key_position).html(range.attr("title"));
$(key_position).css('color', Raphael.getRGB(color).hex);
$(key_position).show();
};
function makeSVG(tag, attrs) {
var el = document.createElementNS('http://www.w3.org/2000/svg', tag);
for (var k in attrs)
el.setAttribute(k, attrs[k]);
return el;
};
function addLabel(label) {
var text = makeSVG("text", {});
var title = makeSVG("textPath", {"xlink:href":'#' + label.replace(/\s/g, '_')});
title.appendChild(document.createTextNode(label));
text.appendChild(title);
r.canvas.appendChild(text);
};
I get a bounding box of 0x0 for the textPath but no textPath.
You may need to either remove the xlink
that appears in the first sample, or add the appropriate XML namespace definition.
EDIT: when you are adding the textPath
element you may need to use the correct element name (textPath
, not textpath
).
EDIT: moderately interesting, as it is combination of the browser and jQuery changing the path. To get the right thing, use this function (liberated from the answer to jquery's append not working with svg element?):
function makeSVG(tag, attrs){
var el= document.createElementNS('http://www.w3.org/2000/svg', tag);
for (var k in attrs)
el.setAttribute(k, attrs[k]);
return el;
}
And then do this:
var paper = Raphael("notepad", 320, 200);
// do all your other stuff here
...
var text = makeSVG("text", {});
var c = makeSVG("textPath", {"xlink:href":"#Arc"});
c.appendChild(document.createTextNode("This is an Arc"));
text.appendChild(c);
paper.canvas.appendChild(text);
That will get you the right SVG: however, it still won't draw right, which is odd.
If your not worried about your work not working in ie8 or earlier why not use kieth wood's jquery SVG plugin that supports textpath The path syntax for the plugin is different but easy to convert from Raphael The SVG plugin delivers beautiful radial gradients but this is why I am involved with it now.
If you want text on a path without gollowing the curve with every letter then you could stay with Rap and use this...http://irunmywebsite.com/raphael/additionalhelp.php?v=1&q=anglebannersoncurves
Excuse spelling iPod newbie
Femi's answer is pretty good, the only things missing are getting it to redraw and setting the xlink namespace.
I can't figure out the underlying reason, but it must be something in the core of Raphael that prevents the SVG canvas from updating, since all you need to do to fix drawing is force a redraw by quickly removing and then readding the svg element from the DOM:
$('#myRaphaelDiv').html($('#myRaphaelDiv').html());
It should be noted that any direct hacking of SVG attributes will break compatibility with browsers (sigh, IE) that use VML instead. I've yet to look into a way of doing text paths in VML. Code below is for completeness.
// add the new namespace attribute to the SVG canvas. 'raphael' is the Raphael instance.
// note that this only needs to be done once.
$(raphael.canvas).attr('xmlns:xlink', "http://www.w3.org/1999/xlink");
// draw the curved text
var lblId = 'some_unique_dom_id';
var path = raphael.path().attr({
stroke : 'none' // defaults to black
// ...other path attributes used in generating it...
});
var label = raphael.text().attr({ 'text-anchor' : 'start' });
// create and inject raw SVG textPath element
var link;
try {
link = document.createElementNS('http://www.w3.org/2000/svg', 'textPath');
} catch (e) {
// no createElementNS() method is pretty much synonymous with using IE, so
// this is where you would do your VML version of a text path
alert("Your browser does not support SVG, please use Firefox, Chrome etc");
}
link.setAttribute('xlink:href', '#' + lblId);
link.appendChild(document.createTextNode("path-oriented text"));
// finally, force a redraw of the SVG document.
// Obviously, you would defer this until all elements had been added.
$('#myRaphaelDiv').html($('#myRaphaelDiv').html());
So, after finding a patch to Raphael here and making a small tweak to the patch it is working. I'm still not sure what I was missing but the textPaths now render and everyone is happy. Thanks to everyone for the help.
精彩评论