JS with SVG: Rotate SVG-Element around its center - javascript

I have to rotate three SVG-Objects around its center with JavaScript and not the <animation> tag. The big star at the top of the tree rotates correctly, but from the others I don't find the center coordinates.
This is the second star and the rotation in JS:
star2.setAttribute("transform", "rotate(" + angle + " 2000 200)");
and this is the svg:
<defs>
<symbol id="stern">
<polygon points="1000 50 1100 310 850 160 1150 160 900 310 1000 50" fill="yellow" />
</symbol>
</defs>
<use id="stern2" xlink:href="#stern" transform="translate(500,600) scale(0.5)" onmouseover="rotate2();" onmouseout="stop();" />
Here is a fiddle:
http://jsfiddle.net/F325k/

to rotate around the center you need to translate it first: Rotate rectangle around its own center in SVG
ps: the latest and reworked lib from the author of Raphaël is http://snapsvg.io/ in case you can use one.

For SVG, definately use third-party libraries. SVG is a pain with plain javascript. My suggestion: Definately go for D3js, I have been using it for a year at my workplace and it is great: dynamic, good community, brilliant documentation.
See the following example with spinning circle around its center which is called Planetarium: Planetarium Does the rotation without animation as you wanted. It uses a D3- timer and that is all.
There are a lot of examples there, so read the full example list here: d3js Full list of examples

Related

Lots of path on screen makes tooltip slow on iPhone

I am drawing 21 * 9 windBarbs in my d3 chart using PATH.
Sample path looks like this:
<path marker-start="url(#meteo-barbheadLight)" stroke="black" stroke-width="0.5" class="meteo-aloft-barb meteo-aloft-barb-group-0 moderate" d="M1 2 L8 2 M0 0 L1 2 Z" transform="translate(0, 225.64285714285714) scale(1.875), rotate(339.35300899956496, 0, 0), translate(-4, -2)" style="opacity: 0.2;"></path>
This is working fine on desktop web browser. But when I run it on iOS safari. Due to some reason tooltip-trendline performance becomes very poor.
I am not sure what is causing this issue. I even tried to isolate these barbs from trendline -tooltip functionality, but still it is slow. If I dont draw these barbs then tooltip is pretty smooth.
So question is :
1) Are those 21 * 9 barbs making page so heavy that tooltip is having lag on mobile browser?
2) What can be a better way to do this?
Application URL:
http://ec2.buser.net/bnforecast/meteogramv2/#!/app/dashboard?start=KBTP&product=MAV
A better way to do this is either draw all those paths inside a canvas instead of SVG or just export those paths to canvas using canvg library and then remove SVG path container element. Canvas is way lighter than SVG.
Also, one tip is to use display: none instead of opacity for performance boost.

Animate circle along sine curve

I am trying to replicate the visuals of f.lux using jQuery which looks like this (sorry for the poor quality). Basically, it is a circle following a sine wave path. I have looked at the jQuery.path animations and I tried replicating the animation, but my sine wave is choppy since it is drawn using CSS. Is there a way to draw the sine wave so it is smooth and then animate the circle following the sine wave path? I couldn't get a fiddle working but this is what my animation currently looks like now. My animation also does not loop back to where it starts unlike the f.lux animation.
I am not sure what the limits are for this either, will I need to make f.lux's background (the blue and pink halves of the sine wave) and then lay the animation on top of it or can I make the whole visual using Javascript, CSS, and jQuery?
Does anybody have any ideas or can anyone point me in the right direction?
To make your line smoother, you can use a svg element, like so :
<svg width="190" height="160" xmlns="http://www.w3.org/2000/svg">
<path d="M10 80 C 40 10, 65 20, 95 80" stroke="black" fill="#E0E0E0"/>
<path d="M95 80 C 105 100, 150 150, 180 80" stroke="black" fill="#EEEEEE"/>
</svg>
This uses two different bézier curves so you can use different colors for your background. You will have to change the values a bit to match you desired width/height and curvature. There is an explanation on svg paths here : https://developer.mozilla.org/en/docs/Web/SVG/Tutorial/Paths.
You can create an insert a svg element with javascript or jQuery SVG if you don't have access to the HTML source file.

SVG and SMIL animate Ball bouncing 3 times and collision with wall

I need to animate a circle that bounce 3 times and hits a wall and return. it should follow a given path. i have tried it with animateMotion. This is how it is so far,
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<title>animation</title>
<!--<rect x="15" y="5" rx="5" ry="5" width="20" height="10" style="fill:#CCCCFF;stroke:#000099">-->
<circle cx="0" cy="50" r="15" fill="blue" stroke="black" stroke-width="1">
<animateMotion dur="6s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#path1"/>
</animateMotion>
</circle>
<!--</rect>-->
<path id="path1" d="m21,39c0,0 46,-44 79,-1c33,43 62,58 97,26c35,-32 86,-30 86,
-31c0,-1 61,-9 29,43c-32,52 -19,51 -87,51c-68,0 -158,-5 -158,-6c0,-1 -40,-11 -41,-12 Z"
stroke-width="5" stroke="#000000" fill="none"/>
</svg>
actually something like below is what i am expecting, as i am new to the area, appreciate any guidance or support.
In order to create the impression that the ball is bouncing organically, you probably want a trajectory that looks a bit more like this:
In addition, you do not want a linear timing function. A linear timing function means the element will move at the same speed throughout the animation. But, the closer a ball is to the apex of a bounce, the slower the ball is going. Also consider that it is going faster at the start and end of the tallest bounce than it is at the start and end of the shortest bounce.
Based on the aforementioned info about the behaviour of a bouncing ball, we might guess that the timing function for one bounce should look something like this:
It starts out fast, slows down, and then speeds up again.
This diagram represents a cubic-bezier curve, also known as a spline. This particular cubic-bezier can be written as 0.1 0.8 1 0.3 - the x and y coords of the first control point (P1) followed by the x and y coords of the second control point (P2).
Combining the trajectory and the timing function:
jsfiddle. The timing needs to be refined a bit, but that's the general idea.
The <animateMotion> element requires four additional attributes to make this happen:
keyPoints="0;0.5;0.75;1"
keyTimes="0;0.35;0.6;1"
calcMode="spline"
keySplines="0.3 0.5 0.8 0.7;0.3 0.5 0.8 0.7;0.3 0.5 0.8 0.7"
what these mean:
keyPoints="0;0.5;0.75;1" - I've decided to split up the path into three sections, each leg of which is associated with a start and end time (from keyTimes) and a timing function (from keySplines). The values are between 0 and 1, separated by semi-colons, where 0 is start of path and 1 is end of path.
keyTimes="0;0.35;0.6;1" - the total duration of the animation is segmented according to these times. There should be exactly as many keyTimes times as there are keyPoints. The values are between 0 and 1, separated by semi-colons, where 0 is start and 1 is the total duration of the animation.
calcMode="spline" - indicates that we want the timing function to be cubic-bezier. setting calcMode to spline means we also have to set keySplineson this element.
keySplines="0.3 0.5 0.8 0.7;0.3 0.5 0.8 0.7;0.3 0.5 0.8 0.7" - each leg of the trajectory gets its own timing function. (So, there should be one fewer keySplines value than there are keyTimes. Here, I've used the same timing function for each, 0.3 0.5 0.8 0.7. Since there are four keyTimes, there are three keySplines values because the trajectory has three legs. (Again, the actual values that I used need to be refined but that's the general idea.)
If you want your circle to follow the path, you need the circle's position to be (0,0), since the motion animation will be relative to the circle's current position:
<circle cx="0" cy="0"...
You can also remove rotate="auto" since it isn't of any use in this case.
See jsfiddle

SVG Scaling and Coordinates

This is my first SVG project, and I’m not a programmer, but I dabble in interactive infographics. My previous experience in this area comes from working with ActionScript.
I’m using plain SVG (no Raphael, D3, etc.) and trying to create an interactive barchart. After some initial difficulty with the SVG coordinate system and scaling, I found some code online that handles the postscaling translation:
<text x="x_coord0" y="y_coord0" transform="scale(x_scale, y_scale) translate(-x_coord0*(x_scale-1)/x_scale, -y_coord0*(y_scale-1)/y_scale)" …>text</text>
And I converted it into this JavaScript:
var translationfactor = ((0 - y_position)*(y_scalefactor - 1) / y_scalefactor);
var matrix = "scale(1," + y_scalefactor + ") translate(0," + Number(translationfactor) + ")";
targetbar.setAttribute("transform", matrix);
The problem is that I need the bars “translated” back to the chart’s baseline, not the original locations of their topmost points. Currently the correctly scaled bars are hugging the top of the chart:
http://billgregg.net/miscellany/upsidedown-barchart.png
I’ve tried several fixes, including plugging the bars’ ”missing height” into translationfactor (the bars start out the full height of the chart and get scaled down dynamically). Nothing has worked. Part of my problem is that, besides being new to SVGs, I can stare at that code all day and my brain still can’t parse it. Multiplying negative numbers is too abstract and at a fundamental level I just don’t “get” the math, which of course makes modifying the code difficult.
My questions:
(1) What’s the fix for the code above to position the bars back on the baseline of the chart?
(2) Is there a more transparent, more pedestrian way of accomplishing the translation? My first thought along these lines was that if a bar’s height is reduced to 40% of its original value, then multiplying the original Y coordinate value by 250% should reset the bar to its original location (at least its topmost point), but that doesn’t seem to work.
(3) Is there a way to set a bar’s point of origin to its bottom? In Flash it’s possible, though as far as I know it’s a manual, not a programmatic task.
(4) Is there a method similar to .localToGlobal() in ActionScript that would allow me to avoid having to mess with the local coordinate system at all?
Behind the scenes there is matrix math going on and it can be hard to get your head around the pre and post multiplication of arrays.
It's not entirely clear what you are trying to achieve, but reading between the lines, it sounds like you are wanting to provide graph coordinates in their raw(ish) form and have the SVG scale and position them for you(?)
If that's the case, then I think the solution is simpler than what you think.
Assuming I'm right, we'll start with something that looks like this:
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g transform="">
<rect x="0" width="1" height="5" fill="red"/>
<rect x="1" width="1" height="11" fill="green"/>
<rect x="2" width="1" height="12" fill="orange"/>
<rect x="3" width="1" height="8" fill="blue"/>
</g>
</svg>
Where x is obvious and the bar length is in height. y defaults to 0, so we don't need it here.
You basically want to know what goes in the transform to scale and position the bars on your page. The fact that your graph is "upside-down" helps a little. Because the origin in an SVG is at the top left.
First apply a scale. Let's make the bars 20 pixels wide, and scale the lengths up by 10.
<g transform="scale(20,10)">
Next you want to position the graph on the page. Let's put the top-left corner at (40,40).
In SVG the transformations are concatenated in order (post-multiplied) so in order for the translation to be what you specify and not be multiplied by the scale, you should put it first.
<g transform="translate(40,40) scale(20,10)">
So the final SVG looks like this:
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<g transform="translate(40,40) scale(20,10)">
<rect x="0" width="1" height="5" fill="red"/>
<rect x="1" width="1" height="11" fill="green"/>
<rect x="2" width="1" height="12" fill="orange"/>
<rect x="3" width="1" height="8" fill="blue"/>
</g>
</svg>
The above has been simplified by assuming you have already subtracted the values from your base 20%. If you wanted to keep the pure raw values, it's possible, but things get a bit trickier. You would need to either tinker with both the y and height value of each bar, or use clipping to hide the part of the bar above 20%.
For "right way up"/normal graphs. All you need to do is make the y scale negative and translate the graph so that the bottom-left is where you want it.
<g transform="translate(40,140) scale(20,-10)">
Hope this helps.

Translating SVG elements for an Isometric view

I'm working on some JavaScript code to render standard 2D SVG/Canvas elements (drawn with Raphael-JS) in an isometric 3Dish view.
Say we have two rectangles drawn next to each other. I then have them redrawn at the correct angles (basically a 30 degree twist) for an isometric view.
(In the image above I've shown the origin for two corresponding elements.)
My problem is I don't know how to properly translate all the individual elements so they "tile" correctly instead of just overlapping.
While actually using tiles would make things easier as I could just base any given tile's placement on the one before it, tiles won't work in this case. Everything is dynamic and will be more complex than simple x/y planes.
Here is an image of some isometric tiles if there's any confusions as to how I want these objects to be placed.
You shouldn't apply the transformation to the individual elements, but to the source elements as a collection. In Raphael, you could use something like
var s = paper.set();
s.push(square1, square2);
and now do the transformations without too much math, which is supposed to work like this:
// s.clone(); // if you want to keep originals
s.rotate(45, 0, 0).scale(1, .7).translate(100, 0);
(However, scaling of rotated items seems to be broken in RaphaelJS.)
Plain SVG example:
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="-200,-500 1000,1000">
<title>Isometric</title>
<g id="source"> <!-- group -->
<circle cx="-50" cy="-50" r="50"/>
<rect width="100" height="100"/>
<rect width="100" height="100" x="101"/>
<rect width="100" height="100" x="50" y="-200"/>
</g>
<!-- make copy of group and apply transformations -->
<use xlink:href="#source" transform="translate(500) scale(1, .7) rotate(-45)"/>
</svg>
Using Raphel.js 2.0 you can do this using the .transform() method and providing a transform string that rotates 45 degrees and scales vertically 70% (or whatever pitch you want). It's important to pay attention to the position you are rotating and scaling around as well - in this case I'm using 0,0. You will also notice I'm translating 100 over to the right to compensate for the rotation.
Transform strings are also great for this use case because you can simply prepend the projection transformation to the transformation of other objects in the scene and they will all end up in the right place.
For example (see http://jsfiddle.net/k22yG/):
var paper = Raphael(10, 10, 320, 240),
set = paper.set();
// Build a set to make it easier to transform them all at once
set.push(
// Grid of rectangles
paper.rect(0, 0, 50, 50),
paper.rect(60, 0, 50, 50),
paper.rect(0, 60, 50, 50),
paper.rect(60, 60, 50, 50)
);
// Rotate, then scale, then move (describe in "reverse" order)
set.transform('t100,0s1,0.7,0,0r45,0,0');​

Categories