I use embedded SVG in XHTML and want to create animations from Javascript, but it does not work as expected
I am modeling business processes with XPDL and connect the simulation to a SVG graphics which I animate using javascript. I am doing this in Firefox, and model and graphics are embedded in XHTML. Now the problem is that I want to use an animateMotion-Tag to move an object along a path. Both are already existing, so I tried writing my solution into the SVG file, and this worked fine. It looked like:
<animateMotion xlink:href="#id1" rotate="auto" dur="2s">
<mpath xlink:href="#id2">
</animateMotion>
Of course, the namespaces are set correctly, so this works as expected. I trigger it manually, so there is no begin time needed. Now, my approach for doing the same thing in an existing mixed XHTML/SVG-dom:
function moveAlongPath(elemId,pathId,rotate,duration)
{
var svgNs = "http://www.w3.org/2000/svg";
var xlinkNs = "http://www.w3.org/1999/xlink";
var motionElem = document.createElementNS(svgNs,"animateMotion");
motionElem.setAttributeNS(xlinkNs,"href","#" + elemId);
motionElem.setAttributeNS(svgNs,"rotate",rotate);
motionElem.setAttributeNS(svgNs,"dur",duration + "ms");
var pathRef = document.createElementNS(svgNs,"mpath");
pathRef.setAttributeNS(xlinkNs,"href","#" + pathId);
motionElem.appendChild(pathRef);
var animElem = svgRootNode.getElementById(elemId); // It is indeed the <svg>-Node
animElem.appendChild(motionElem);
// Setting x and y to 0 is important for the Element to be "on" the Path
animElem.setAttribute("x",0);
animElem.setAttribute("y",0);
motionElem.beginElement();
}
When I check the dom in firebug, this seems to produce the same node structure with the same attributes, although the href isnt prefixed with xlink:, but setAttributeNS should do this, right? The problem here is that i cannot start the animation with beginElement(). Nothing happens here.
I hope there is help out there, i am really desperate right now.
EDIT:
I found it at last. The problem disappears when I use
setAttributeNS(null,"attr",value)
instead of
setAttributeNS(svgNs,"attr",value)
Correct this if I am wrong, but is not my first approach the way XML was thought? That there shouldn't be namespaceless attributes? Anyway - SOLVED!
Use
setAttributeNS(null,"attr",value)
instead of
setAttributeNS(svgNs,"attr",value)
variableElementNS.href.baseVal = value;
Related
using Raphaƫl 2.1.4 - JavaScript Vector Library
do something like that:
var textDummy = paper.text(50,500, 'hello world').attr({fill: 'transparent', 'font-size': 14});
var textBox = textDummy.getBBox();
with chrome and firefox everything is fine,
but in IE8 it give back NaN/NaN/NaN,
par exemple textBox.height is NaN.
how i can fix this?
i found a workaround solution from this answer to the question
"Raphael JS and Text positioning"
If i use _getBBox() instead of getBBox() everything is working in ie 8 also.
_getBBox() is undocumented but used internally by Raphael itself, and it works!
I had the same problem in Rapahel 2.2.0 and 2.2.1, and using ._getBBox() didn't fix it for me.
What did fix it for me is falling back to .auxGetBBox() if it's defined and regular .getBBox() doesn't work, like this:
var bbox = path.getBBox( );
// Workaround for apparent bug in Raphael's VML module's getBBox() override
if( isNaN( bbox.x ) && path.auxGetBBox ){
bbox = path.auxGetBBox();
}
I don't have a fix for the underlying bug, but I have found the source of it.
In VML mode, Raphael takes the initial getBBox() function, saves it as auxGetBBox() on the element prototype, then replaces it with a function that appears to be broken.
It has calculations based on a variable defined as var z = 1/this.paper._viewBoxShift.scale;, which clearly expects _viewBoxShift.scale to be some factor of the scale of the current viewbox compared to the initial viewbox , but actually _viewBoxShift.scale is an object like this which appears to come from paperproto.getSize():
{ height: someNumber, width: someNumber }
This is where all the NaNs are coming from. Cannae divide by an object.
So this workaround works fine if no zoom is applied using a viewbox, but may give incorrect results if a zoom has been applied (something I can't get to work at all in recent versions of raphael in VML mode, but that's a seperate question). Fixing that will involve digging deep into Raphael's VML module to pipe a proper zoom factor into this z variable.
I am trying to make a website which helps its users to create a page by dragging and dropping elements on the canvas. The user should be able to save the html file of the edited canvas. I cannot figure how to convert the changes made to the canvas to an html file.
I don't think it's possible to get Markup out of canvas. I've searched it for a month but can't find a valid solution. but may be some experts may know. Best of luck buddy.
Canvas is basically just a bit-map image. Whatever you draw on the canvas is stored as pixels not as elements. So changes to the canvas are just changes in pixel values. To do what you wish you would need to store your 'elements' as 'objects' within your code where each 'object' stores all the required data for your 'element'.
it would then be possible to open a new window and export code into it using document.writeln
The code below may give you an idea of what sort of thing would be needed
newwindow=window.open('','_blank');
newwindow.document.writeln('<!DOCTYPE HTML>');
newwindow.document.writeln('<html>');
newwindow.document.writeln('<head>');
newwindow.document.writeln('<style>');
newwindow.document.writeln('#element0 {');
newwindow.document.writeln('background:'+ obj0.background+';');
newwindow.document.writeln('width:'+ obj0.width+';');
newwindow.document.writeln ('}');
newwindow.document.writeln('</style>');
newwindow.document.writeln('</head>');
newwindow.document.writeln('<body>');
newwindow.document.writeln('<div id="element0"></div>');
newwindow.document.writeln('</body>');
newwindow.document.writeln('</html>');
newwindow.document.writeln('<html>');
newwindow.document.close();
Hope this helps
Canvas won't help you here for anything other than to visualize the objects you have dropped onto it.
You need to record the objects you drop in a "shadow" structure behind the scene sort of. That is to say: build a object list internally which you then can use as source data to render:
Canvas visualization of it
Raw HTML code from it.
You can for example drop an image to the canvas and your code will record a new object (intention with the following code is to show the principle not to provide a full working solution):
var myObjects = [];
/// a drop occurred
var o = new myElement(x, y, width, height, id, type);
myElement is a pre-defined object that you set up in advance to hold the given arguments.
Then push the object to your object stack and render it to canvas:
myObjects.push(o);
for(var i = 0, o; o = myObjects[i]; i++) {
/// draw the look of this object here to canvas
}
When you then need a HTML version of it you do the same:
for(var i = 0, o; o = myObjects[i]; i++) {
var el = '<' + o.type + ' id="' + o.id + ' .... other things here
}
This way you can produce canvas graphics, HTML, send data over a socket etc.
The key in these sort things is to keep raw base data available. In this case it would be the element type you want to drop, its position and dimension. For HTML you also have to consider things as nesting etc. but that would require a bit more code than shown here.
So I want to make a drawing tool using SVG, I'm using a rather naive approach to change the d attribute of my Path:
$("div#drawarea").bind("mousemove", function(ev) {
ev.preventDefault();
ev.stopPropagation();
var pX= (ev.pageX - this.offsetLeft);
var pY= (ev.pageY - this.offsetTop);
$path.attr("d", $path.attr("d") + " L" +pX+ "," + pY); //using jquery-svg here to change the d attribute
});
As you can see I do this on the mousemove function. The code works but it becomes unresponsive when the mouse is moving fast creating numerous straight lines when I actually want it to be smooth lines. I think this is happening because the numerous string concatenations I'm doing on the mousemove event (the d attribute on the path can become quite big when the click has been held for long, thousands of characters long in fact).
I'm wondering if there is any native way to add new values at the end of a path instead of manipulating the d attribute directly. I checked the jquery-svg sourcecode and it seems that the library also uses the naive string concatenation mode internally so using its methods would not wield any benefit.
Also I'm wondering if this is the case or if the browser just limits the amount of mousemove events (once every X milliseconds?) that can be triggered and so no performance optimizations would improve this.
Use the SVG pathseg DOM methods. You have to write more complicated code but the browser doesn't have to reparse the whole path attribute. Firefox for instance does take advantage of this and it's quite likely other broswers also.
In case someone else stumbeld upon the quesion of what is the fastes way to update an SVG-Path data attribute (for realtime applications), I run a small test on that:
http://jsperf.com/svg-path-test
Yes, setting it as string means that it needs to be parsed, which isn't the case for the DOM SVG interface but the first method is still much faster. Maybee the interface updates the DOM with each point added, slowing down the whole process.
I am looking for a possibly fast way to apply all transform matrices of a given svg-graphic. In other words: the algorithm should remove all "transform" attributes and transform all coordinates of the graphic to absolute coordinates.
Is their any library that can do this, or is their any SVGDomInterface method that coulld do that?
EDIT::
If I call the consolidate method like this:
$.each( svg.find( 'path' ), function( i ){
this.transform.baseVal.consolidate();
});
nothing happens, if i call it like this:
$.each( svg.find( 'path' ), function( i ){
this.transform.animVal.consolidate();
});
i get this error:
So, how should i use the "consolidate" method, on which elements shall I call it?
Greetings
philipp
Here's a jsFiddle with some javascript library code (based in part on Raphael.js) to bake the transforms into all paths' data:
http://jsfiddle.net/ecmanaut/2Wez8/
(Not sure what's up with Opera here, though; it's usually best in class on SVG. I may be stumbling in some way the other browsers are more forgiving about.)
The consolidate method only reduces the list of matrices to a single matrix. And the error you get on the animVal example is because you are not allowed to modify the animated values (consolidate destructively modifies the transform list).
To answer your question, no there's no existing method in SVG DOM that applies the transforms by modifying the values of paths etc. There are options in Inkscape (and Illustrator too IIRC) for applying transforms like that.
If you're looking for a library or utility that does this you can try SVG Cleaner.
SVG Cleaner didn't seem to apply all transforms for me, but Inkscape does. Here's the command line I use when I need to apply one:
inkscape copy-of-file.svg --select=id-of-node \
--verb=EditCut --verb=EditPaste \
--verb=FileSave --verb=FileClose
Assuming you have "Transforms -> Store transformation" set to "Optimized" in inkscape's prefs (I believe it is on by default), this will apply it and produce the wanted result. Be sure you operate on a copy of your input file, as the operation replaces your original file!
If you are running this on a mac, you may want to first do this in your shell:
alias inkscape=/Applications/Inkscape.app/Contents/Resources/bin/inkscape
Better use EditPasteInPlace instead of EditPaste. EditPaste pastes at the mouse location, which is not the location of the node.
In order to retreive the path relative to some other, parent DOM node (e.g. the SVG root container), you can use the technique here.
It uses SVG.getTransformToElement which calculates the transform between a parent and some node on the SVG tree. The returned object contains methods to return the inverse of the transform, etc, to do practical things with it.
How to determine size of Raphael object after scaling & rotating it?
I dont know exactly what you are trying to achieve, but this has the power to do it.
I am using raphael to do some SVG animation and cannot seem to get the function animateAlong to work. I continue to get the error "attrs[0] is undefined" referencing line 3450 of the un-compressed raphael code.
Basically, I create a circle with a given center and then want to animate an image around that path. Here is that simple code:
var circle = paper.circle(circleCenterX, circleCenterY, circleRadius);
I then clone an image (since I plan to have a number of these on this path) and place at the edge of the circle:
var wheelClone = wheel.clone();
var wheelRadius = parseInt(wheel8ImageWidth/2);
wheelClone
.translate((circleCenterX + circleRadius)-3, circleCenterY-wheelRadius);
where I init circleCenterX earlier with circleCenterX = circle.attr(cx);
This all works fine with image placed correctly - but it errors on animateAlong.
I have studied as many examples as i can find and have dissected the documentation but cannot get the hang here.
So, I simply try to call the function but have no earthly idea what the documentation is referring to. The documentation animates a dot around a path but refers to two variables - rx and ry which I cannot suss out - both in an init function and then with the callback.
Here is what I have - - where the rx and ry and just made up as I have no idea what they refer to.
var wheelAttr = {
rx: 5,
ry: 3
};
wheelClone.attr(wheelAttr).animateAlong(circle, 2000, true, function() {
wheel.attr({rx: 4, ry: 4});
});
My current jsFiddle is a bit of a mess at the moment and I can clean it up, but I suspect that there is some obvious thing here?
Thanks to all
S
I don't think a circle is actually a valid path (i.e, something you can pass to animateAlong()). I think you need to create a path that is circular. See the following:
svg-animation-along-path-with-raphael
Hopefully, it will help.