SVG animate path to rotate around its center - javascript

I want to animate an image for The Center for Humane Technology for use on a html landing page. The image looks like this and contains 28 gears positioned in a heart shape. I would like to have each gear to rotate continuously around its center, either clockwise or counter-clockwise.
I have read the other SO posts that deal with similar issues, but the solutions do not work for me. When I add e.g. an animateTransform to the shape of a gear, specifying its center coordinates, then it rotates in a wide circle around its center, not staying into position. I am confused.
I have the orginal artwork (created by nivedita) from EPS to SVG, which resulted in rather large paths. This is the resulting SVG image.
First I was planning to use AnimateJS for the animation, but inline SVG animation markup may also do the trick (maybe better).
Reading other SO submissions, I tried rotating gear1 by calculating its center from these coordinates:
X: 42.623
Y: 309.810
Width: 60.796
Height: 60.774
Resulting in following transform:
<svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" id="gears-of-the-heart" width="333.92" height="526.28">
<g id="canvas" transform="matrix(1.25 0 0 -1.25 0 526.27)">
<g id="heart">
<path d="M58.42 265.7a6.41 6.41 ..." id="gear1" fill="#1e2227">
<animateTransform attributeType="xml" attributeName="transform" type="rotate"
from="0 73.111 340.197" to="360 73.111 340.197" additive="sum" dur="3s" repeatDur="indefinite"/>
</path>
<!-- The other 27 gears here -->
</g>
<!-- More SVG elements (unrelated) -->
</g>
</svg>
Its not working. Is this because of the transform on the parent canvas group?
Two questions:
What am I doing wrong?
Is there an easier way to animate this, so I don't have to calculate center for all gears (I know I could use javascript for coordinates calc)?

Related

how to create d3 radial with dynamic radios

I created a radial with two tiers of options. I did in a way that isn't really dynamic and isn't really responsive to screen size. I now need it to be both of those things. Here is what it looks like when on the screen size I designed it for.
I created a working demo on sandbox that has the dimensions set how I need to use it on. This is what it looks like.
Here is link WORKING DEMO
any help is appreciated. Also keep in mind the outer tiers can have less or more options. it would be great if the blue toggle button would always align at the bottom of the radial like under the En of Energy Loss
I would consider using an SVG ViewBox in order to maintain consistency. What this basically does is create a consistent scalable SVG, mapping the size and coordinates of its container into a consistent range inside the SVG.
For example:
<div height="400px" width="400px">
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<rect x="0" y="0" width="100%" height="100%" stroke="red" fill-opacity="0"/>
<circle r="4" cx="10" cy="10"/>
</svg>
</div>
So it basicalley creates a mapping from the 400x400 dimensions of the div, into the 100x100 of the svg, so the circle positioned at (10, 10) inside the svg will actually be in coordinates (40, 40) of the div

Changing the colour of regions on an SVG circle tiled world map when hovered

I am attempting to make a world map made from an svg comprising many circles. I based this from a codepen I found here: https://codepen.io/mvaneijgen/pen/NRzENO
E.g.
<svg viewBox="0 0 845.2 458">
<circle class="st0" cx="826.1" cy="110.3" r="1.9"/>
<circle class="st0" cx="819.3" cy="110.3" r="1.9"/>
<circle class="st0" cx="819.3" cy="117.1" r="1.9"/>
<circle class="st0" cx="812.6" cy="90" r="1.9"/>
The map is great. I have been dividing it up into coloured continent regions using classes. These change colour when hovered over. All good so far. Most of my functionality is there.
The issue is that you have to be hovering directly on a circle to make the colour change happen. I am using a javascript mouseover event to change the colour.
Is there any way of increasing the area of effect around the circle elements? Maybe putting an invisible square either behind or in front? I am still getting to grips with front-end stuff and any pointers here would be great.
That’s the right idea: transparent rects behind each circle. (Or transparent continent-shaped paths based on geo data, depending on what you’re going for.)
The trick is to use the SVG CSS property pointer-events. Setting it to fill or all should do the trick.

Scaling an SVG with complex animation paths

I have this code for an SVG on my page. It's a track and I have a car that scrolls along the path as you go down the page.
<div class="my-svg-container">
<svg viewBox="-220 -219 1250 2212.5" style="
position: absolute;
" class="my-svg" preserveAspectRatio="xMinYMin meet"><defs>
<style>.cls-1,.cls-10,.cls-11,.cls-12,.cls-13,.cls-14,.cls-15,.cls-16,.cls-17,.cls-3,.cls-4,.cls-5,.cls-6,.cls-7,.cls-8,.cls-9{fill:none;}.cls-2{clip-path:url(#clip-path);}.cls-3{stroke:#2d2d2d;stroke-width:100px;}.cls-10,.cls-11,.cls-12,.cls-13,.cls-14,.cls-15,.cls-16,.cls-17,.cls-4,.cls-5,.cls-6,.cls-7,.cls-8,.cls-9{stroke:#fad000;stroke-width:9px;}.cls-4{stroke-dasharray:12 11.54;}.cls-5{stroke-dasharray:12 10.64;}.cls-6{stroke-dasharray:12 12.26;}.cls-7{stroke-dasharray:12 13.27;}.cls-8{stroke-dasharray:12 12.67;}.cls-9{stroke-dasharray:12 10.99;}.cls-10{stroke-dasharray:12 12.24;}.cls-11{stroke-dasharray:12 13.42;}.cls-12{stroke-dasharray:12 12.63;}.cls-13{stroke-dasharray:12 10.47;}.cls-14{stroke-dasharray:12 11.73;}.cls-15{stroke-dasharray:12 11.95;}.cls-16{stroke-dasharray:12 12.17;}</style>
<clipPath id="clip-path" transform="translate(4349 2519.5)"><rect class="cls-1" x="-4349" y="-2519.5" width="868.18" height="1964.5"></rect></clipPath></defs><title>Road(whole)</title>
<g class="cls-2">
<path id="theMotionPath" class="cls-3" d="M709.08 0.54v306s7,91-106,91h-461s-92-8-92,96v444s-6,78,64,78h606s98-10,98,90v394s5,84-84,84h-522s-90-3-90,90v290" ></path>
<line class="cls-4" x1="709.08" y1="17.54" x2="709.08" y2="294.23"></line>
<path class="cls-5" d="M-3640.93-2196.91c-3.89,25-21,69.87-93.68,74.09" transform="translate(4349 2519.5)"></path>
<line class="cls-6" x1="584.81" y1="397" x2="154.21" y2="397"></line>
<path class="cls-7" d="M-4226.14-2121.52c-25.35,3.77-67.35,19.56-72.3,82.4" transform="translate(4349 2519.5)"></path>
<line class="cls-8" x1="50.08" y1="511.67" x2="50.08" y2="924.67"></line>
<path class="cls-9" d="M-4298.32-1565.54c2.45,21,12.58,54.8,51.94,60.28" transform="translate(4349 2519.5)"></path>
<line class="cls-10" x1="132.32" y1="1015" x2="707.96" y2="1015"></line>
<path class="cls-11" d="M-3609.53-1504.15c26.79,2.72,72.47,16.34,78,77" transform="translate(4349 2519.5)"></path>
<line class="cls-12" x1="818.08" y1="1123.63" x2="818.08" y2="1486.69"></line>
<path class="cls-13" d="M-3532-1004.08c-3.44,22.94-17.31,62.35-71.73,67.11" transform="translate(4349 2519.5)"></path>
<line class="cls-14" x1="716.35" y1="1583" x2="223.99" y2="1583"></line>
<path class="cls-15" d="M-4154.73-934.92c-24.59,4.26-66.45,19.86-71.61,76.46" transform="translate(4349 2519.5)"></path>
<line class="cls-16" x1="122.13" y1="1691.17" x2="122.13" y2="1950.92"></line>
<path class="cls-17" d="M-4226.87-562.5
v6m.13-296
q-.14,2.93-.13,6
v6
m96-96
h-6
s-2.23-.07-6,.16
m533.95-.29q-2.92.14-6,.13
h-6
m90-90
v6
s.13,2.24,0,6
m-.09-406
c.08,2,.12,4,.12,6
v6
m-104-96
h6
s2.22-.23,6-.27
m-618,.07
c1.94.13,3.93.2,6,.2h6m-70-84v6s-.18,2.26-.12,6m.22-456
q-.11,2.94-.1,6v6m98-102h-6s-2.23-.19-6-.13m473,0q-2.94.09-6,.09
h-6m112-97v6a57.48,57.48,0,0,1,0,6m0-312v6" transform="translate(4349 2519.5)"></path></g>
<g class="shakyimage">
<image speed="2" width="5%" height="5%" x="-65" y="-30" xlink:href="https://cdn3.iconfinder.com/data/icons/cars-28/512/Car_5-512.png" id="dot" ></image>
</g>
<g >
<image width="45%" height="11%" x="423" y="-100" xlink:href="https://cdn0.iconfinder.com/data/icons/gpsmapicons/red/gpsmapicons07.png">
</g>
<g >
<image width="30%" height="10%" x="-66" y="1782.17" xlink:href="https://cdn0.iconfinder.com/data/icons/gpsmapicons/red/gpsmapicons07.png">
</g>
</svg>
</div>
This functions very well as-is. My main issue is I have built the page to align with the track. This means that the end of the track perfectly aligns with the last section of the page. The problem is, when you make the page smaller the track shrinks in size.
Currently I have tried doing this:
.my-svg-container{
display: inline-block;
position: relative;
width: 100%;
padding-bottom: 38.5%; /* depends on svg ratio, for my zebra height/width = 1.2 so padding-bottom = 50% * 1.2 = 60% */
vertical-align: middle; /* top | middle | bottom ... do what you want */
}
.my-svg{ /* svg into : object, img or inline */
display: block;
position: absolute;
top: 0;
left: 0;
width: 100%; /* only required for <img /> */
}
However this seems to have no effect on the responsiveness.
So is it possible to basically make this scale, but retain its position on the page? The height seems to be the main issue; positioning seems to be intact, but the height shrinks so everything isn't aligned any more.
Thanks! Let me know if you need more info
You are scaling the SVG image proportionally, which means that when the width changes (to adapt to the page width), the height must also change to keep the image proportions.
In part this is what you want, because scaling the image disregarding proportions would cause your traffic sign or the car to become stretched. On the other hand you want to keep the road height fixed, so here are your alternatives:
Alternative 1 — don't scale the image
This may seem counter to what you're looking for but it's definitely worth considering. You're not scaling the text around which the image revolves, so why scaling the image at all? Just keep both centered with constant size.
Alternative 2 — scale parts of your SVG separately
You can split your SVG into the parts you want with fixed proportion (like the traffic sign and vertical parts of roads) and variable proportion (like horizontal roads). You can use the preserveAspectRatio and viewBox attributes to. There's an example here (works on Chrome but not on current Firefox).
Alternative 3 — scale in discrete steps
If you really want the image to become bigger on a larger display, but don't feel like going through the complexity of alternative 2, you can prepare several SVGs for several width / height ratios and use media queries to display the appropriate one for the appropriate width interval.
Try changing your SVG to
preserveAspectRatio="xMinYMin slice"
It should mean that the SVG stays the same height as your content, but that the right hand side of the SVG gets clipped off as the page gets narrower.
If you want both sides (left and right) to get clipped off at the same rate, so the SVG remains centred, then use:
preserveAspectRatio="xMidYMin slice"

Svg polygon rounding

I am working on an application that is using svg move/rotate/zoom functionalities. I'm programming the back-end in Laravel and the front-end is using html/css/javascript. I've seen on the web that is possible for a polyline to have some sort of cubic-bezier to it.
Now my question is: is it possible for a polygon svg element to have the same cubic-bezier to it as the polyline like in this example?
The structure of the svg looks like is:
<svg>
<g data-type="track">
<polygon class="track" points="2588,851 2537,1157 1796,916 1117,723 0,382 40,80 816,314 1885,638 1887,634"></polygon>
<polygon class="track" points="114,19 73,0 17,497 46,485"></polygon>
</g>
</svg>
Is it possible to give the polygon element a cubic bezier so that it can create a fluid polygon instead of the square no-rounded polygon?
I think some of the responses here have been a little confusing.
(is it) possible for a polygon svg element to have the same cubic-bezier to it as the polyline
The short answer is no. <polygon> (and <polyline>) elements are always rendered as a sequence of straight line segments between the coordinates you provide. There is no way to automatically make the joins have a radius - like an HTML border-radius. If that is what you are asking.
If the line has a bigger stroke width, you can choose to round the outside corner of the line joins.
.track {
fill: none;
stroke: black;
stroke-width: 20;
}
.round {
stroke-linejoin: round;
}
<svg width="300" height="300">
<polygon class="track" points="20,20 290,20 290,130 20,130"></polygon>
<polygon class="track round" points="20,170 290,170 290,280 20,280"></polygon>
</svg>
If you want to include bezier curve segments in your "line", you will have to use the <path> element instead. As was used in the example you linked to.
I suggest to put one duplicated figure above another one with just smaller stroke-width. Profit! :)
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<polygon points="50,30 55,50 70,50 60,60 65,75 50,65 35,75 40,60 30,50 45,50" stroke-linejoin="round" stroke-width="50" stroke="red"/>
<polygon points="50,30 55,50 70,50 60,60 65,75 50,65 35,75 40,60 30,50 45,50" stroke-linejoin="round" stroke-width="30" stroke="#fff"/>
</svg>
A polygon does not use cubic Bézier curves, a path does. The example linked does not use any polygons, but a path which includes such curves.
The difference between a polyline and a polygon is simply that the latter is closed, so you can simply create a path and close it (implicitly or explicitly).
Beyond that, I'm not sure what your actual issue is.

Cross-platform SVG rotation animation:

Hi I am currently working with some SVG animations that are to work on multiple platforms.
However I have an issue with Internet Explorer (of course).
I use Keith Woods, Jquery SVG extension.
I have mainly been using layers and show, hide to animate my pages but now I need to do a rotation of a line:
$('#topBar').stop().animate({
svgTransform: 'rotate('+angle+','+(545+amplification)+',-260)'}, time);
The SVG object:
<g
id="g3953">
<path
sodipodi:nodetypes="cc"
inkscape:connector-curvature="0"
id="topBar"
d="m 545,-260 322,0"
style="fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<rect
y="-260"
x="545"
height="5"
width="5"
id="centerPoint"
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:10;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
</g>
This partly works but the line called top bar rotates and translates in another manor than on Chrome and Firefox. I.e. it jumps around a little.
Anybody have a solution to this? can i do the translation differently or just use CSS- or other JQuery rotate method to get better support??
I have seen JQuery rotate but does it support translation?
See how the image jumps, below, this occurs when I just moved the point of rotation...
You may fiddle with the code...
IE does not support animations in SVG. The basic way to rotate across all browsers is to use the getBBox() for the container and rotate from the center of the bounding box. As Follows:
<svg id="mySVG" width=400" height="400">
<g id="containerG">
<polygon id="myPolygon" fill="yellow" stroke="blue" stroke-width="1" points="380,80 200,10 40,80 100,320 300,350" />
<circle id="myCircle" cx="170" cy="200" r="40" fill="lime" />
</g>
var deg=30
var bb=containerG.getBBox()
var bbx=bb.x
var bby=bb.y
var bbw=bb.width
var bbh=bb.height
var cx=bbx+.5*bbw
var cy=bby+.5*bbh
containerG.setAttribute("transform","rotate("+deg+" "+cx+" "+cy+")")
From personal experience, animations are not meaningfull to me in most svg applications. However, what is important, are transitions. By transitions, I mean the smooth movement of a graphic element's attributes as they visually change. I think currently the best cross-browser handling of svg transitions is by D3. The D3 api may be more than you want to get into at this time...But if you plan to work seriously with svg, you should give it a try.

Categories

Resources