How to animate svg path with jQuery/JavaScript? - javascript

I'd like to animate an svg's path. I know this can be achieved with <animate>, but the animation starts only after the element is appended to some div. So I need to find another solution, but I weren't able to find any help online.
Here is the fiddle so you can take a look at what I have.
Note: Now I'd like the svg's path to stretch the svg. So at first, it is a triangle, but it needs to smoothly convert into a square that fits the svg around it.
Thank you!
// -- click the path -- //
$('path').on('click touch', function() {
$(this).parents('svg').css({
height: '100vh',
width: '100vw'
});
})
/* -- click the path -- */
* {
margin: 0;
padding: 0;
}
body {
overflow: hidden; /* -- just for this example */
}
svg {
-webkit-transition:
height 350ms ease-in-out,
width 350ms ease-in-out;
transition:
height 350ms ease-in-out,
width 350ms ease-in-out;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!-- click the path -->
<svg fill="#000" xmlns="http://www.w3.org/2000/svg" version="1.0" height="200" width="200" viewBox="0 0 1024 1024">
<path d="M0 0 L405 0 L405 635"/>
</svg>

SMIL animations (ie. ones using <animate>) can be made to start on a click.
<svg fill="#000" xmlns="http://www.w3.org/2000/svg" version="1.0" height="200" width="200" viewBox="0 0 1024 1024">
<path d="M0 0 L405 0 L405 635 L405 635">
<animate attributeName="d" dur="350ms" to="M0 0 L405 0 L405 635 L0 635"
begin="click" fill="freeze" />
</path>
</svg>

Related

How can I get elementFromPoint() to return an SVG g element? [duplicate]

I'm working on some animated SVGs with CSS animations that triggers on hover.
I'm being able to have the SVG animate on hover the way I want to for Chrome but I'm facing a Firefox and Safari issue.
Apparently, the pointer-events property applied to groups <g></g> doesn't give same behavior on this browser than on the other modern ones, at least when trying to set the value to bounding-box.
I'm doing
g {
pointer-events: bounding-box;
}
but the hover only gets triggered when the actual <path> element is hovered, not the whole group <g> as I would need to.
Can I use doesn't say anything about this, it mentions svgs also have support for this property.
Below there's a sample code for you to see how one of my SVGs looks like.
On chrome hovering the main containing group area will trigger the hover animation, on Firefox the actual path (the icon lines in this case) needs to be hovered in order to that to happen.
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<style>
g {
pointer-events: bounding-box;
//not working on FF
}
#mobile:hover .flip {
transform-origin:55% 50%;
-moz-transform-origin:55% 50%;
animation: flip_left 1.6s forwards;
}
#keyframes flip_left {
0% {transform: perspective(2000px) rotateY(90deg) skewY(-1deg)}
30% {transform:perspective(2000px) rotateY(-25deg) skewY(-0.8deg)}
50% {transform:perspective(2000px) rotateY(20deg) skewY(0.8deg)}
70% {transform:perspective(2000px) rotateY(-10deg) skewY(-0.8deg)}
100% {transform:perspective(2000px) rotateY(0deg)}
}
</style>
<!-- Generator: Sketch 51.2 (57519) - http://www.bohemiancoding.com/sketch -->
<title>Mobile solutions</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="mobile" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="MS_HP_Usecase_Based_Page-Desktop-2A" transform="translate(-766.000000, -418.000000)" stroke="#00A0DF" stroke-width="1.25">
<g id="Asset-5" transform="translate(766.000000, 418.000000)">
<g class="flip">
<rect id="Rectangle-path" stroke-linecap="round" stroke-linejoin="round" x="12.35" y="7.41" width="15.32" height="25.33" rx="2.03"></rect>
<circle id="Oval" stroke-linecap="round" stroke-linejoin="round" cx="20.01" cy="28.72" r="1.58"></circle>
<path d="M18.43,10.72 L21.48,10.72" id="Shape" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
<circle id="Oval" cx="19.67" cy="19.67" r="19.04"></circle>
</g>
</g>
</g>
</svg>
I would like to find a workaround for this, since I want to make this animations work cross browser. I would like to eventually make it work for IE11 and Edge too.
Thanks,
So pointer-events: bounding-box seems to not be supported by most browsers.
I implemented the workaround #ccprog suggested on the comments section of my question.
I added a <rect fill="none"> element to svg, that is same dimensions than the SVG itself. I added a :hover selector for that element and sibling selector ~ to select its sibling group with the flip class inside.
See CSS:
#mobile-hover {
visibility: visible;
pointer-events: visible;
}
#mobile-hover:hover ~ .group .flip {
-moz-transform-origin:55% 50%;
-webkit-transform-origin: 55% 50%;
transform-origin:55% 50%;
-webkit-animation: flip_left 1.6s forwards;
animation: flip_left 1.6s forwards;
}
I found out I had to add pointer-events: visible to the rect element so it would detect the :hover. I added visibility: visible as a requirement to pointer-events: visible to work.
Below the full new SVG code:
<?xml version="1.0" encoding="UTF-8"?>
<svg width="40px" height="40px" viewBox="0 0 40 40" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="mobile-icon">
<style>
#mobile-hover {
visibility: visible;
pointer-events: visible;
}
#mobile-hover:hover ~ .group .flip {
-moz-transform-origin:55% 50%;
-webkit-transform-origin: 55% 50%;
transform-origin:55% 50%;
-webkit-animation: flip_left 1.6s forwards;
animation: flip_left 1.6s forwards;
}
#keyframes flip_left {
0% {transform: perspective(2000px) rotateY(90deg) skewY(-1deg)}
30% {transform:perspective(2000px) rotateY(-25deg) skewY(-0.8deg)}
50% {transform:perspective(2000px) rotateY(20deg) skewY(0.8deg)}
70% {transform:perspective(2000px) rotateY(-10deg) skewY(-0.8deg)}
100% {transform:perspective(2000px) rotateY(0deg)}
}
</style>
<!-- Generator: Sketch 51.2 (57519) - http://www.bohemiancoding.com/sketch -->
<title>Mobile solutions</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="mobile" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" >
<rect fill="none" width="40" height="40" id="mobile-hover">
</rect>
<g id="MS_HP_Usecase_Based_Page-Desktop-2A" transform="translate(-766.000000, -418.000000)" stroke="#00A0DF" stroke-width="1.25" class="group">
<g id="Asset-5" transform="translate(766.000000, 418.000000)">
<g class="flip">
<rect id="Rectangle-path" stroke-linecap="round" stroke-linejoin="round" x="12.35" y="7.41" width="15.32" height="25.33" rx="2.03"></rect>
<circle id="Oval" stroke-linecap="round" stroke-linejoin="round" cx="20.01" cy="28.72" r="1.58"></circle>
<path d="M18.43,10.72 L21.48,10.72" id="Shape" stroke-linecap="round" stroke-linejoin="round"></path>
</g>
<circle id="Oval" cx="19.67" cy="19.67" r="19.04"></circle>
</g>
</g>
</g>
</svg>
Works on Chrome, Safari and Firefox and I'm attempting to test IE11 and Edge next.
Many thanks,

Rotate SVG element like image rotate

I trying to do that the circle stood still and spun around itself, but it rotates through the page.
I just start to learn SVG and stuck at this moment.
I'll be thankful for any help.
https://jsfiddle.net/5rz82ct3/1/
document.querySelector('button').addEventListener('click', () => {
document.querySelector('.kolo svg #pomidor').classList.add('pomidor');
})
#pomidor {
transform-origin: 50% 50%;
opacity: 1;
transition: all 4s;
}
#pomidor.pomidor {
opacity: 1;
transform: rotate(-360deg);
}
<div class="kolo">
<svg width="246px" height="133px" viewBox="0 0 246 133" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="pomidor">
<g id="left">
<path d="M188.669237,2.85577743 L220.007541,78.7916793 C215.011273,80.9577555 209.503344,82.159 203.7202,82.159 C181.0712,82.159 162.6422,63.731 162.6422,41.08 C162.6422,23.7394433 173.442929,8.87328458 188.669237,2.85577743 Z" id="tomato-dark" fill="#ED2D24">
</path>
</g>
<g id="right">
<path d="M203.7202,0.0003 C198.1552,0.0003 192.8472,1.1183 188.0022,3.1323 L219.4392,79.0273 C234.3122,72.8423 244.8002,58.1653 244.8002,41.0803 C244.8002,18.4283 226.3712,0.0003 203.7202,0.0003" id="tomato-light" fill="green">
</path>
</g>
</g>
</svg>
</div>
<button> Rotate circle</button>
Set the transform-box CSS property to fill-box so that the shape rotates around its own centre rather than the viewBox.
document.querySelector('button').addEventListener('click', () => {
document.querySelector('.kolo svg #pomidor').classList.add('pomidor');
})
#pomidor {
transform-origin: 50% 50%;
transform-box: fill-box;
opacity: 1;
transition: all 4s;
}
#pomidor.pomidor {
opacity: 1;
transform: rotate(-360deg);
}
<div class="kolo">
<svg width="246px" height="133px" viewBox="0 0 246 133" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="pomidor">
<g id="left">
<path d="M188.669237,2.85577743 L220.007541,78.7916793 C215.011273,80.9577555 209.503344,82.159 203.7202,82.159 C181.0712,82.159 162.6422,63.731 162.6422,41.08 C162.6422,23.7394433 173.442929,8.87328458 188.669237,2.85577743 Z" id="tomato-dark" fill="#ED2D24">
</path>
</g>
<g id="right">
<path d="M203.7202,0.0003 C198.1552,0.0003 192.8472,1.1183 188.0022,3.1323 L219.4392,79.0273 C234.3122,72.8423 244.8002,58.1653 244.8002,41.0803 C244.8002,18.4283 226.3712,0.0003 203.7202,0.0003" id="tomato-light" fill="green">
</path>
</g>
</g>
</svg>
</div>
<button> Rotate circle</button>
you need to calibrate transform-origin to set the transform point in the middle of that circle (i used a ruler plugin in my browser to easily find it, no magic):
document.querySelector('button').addEventListener('click', () => {
document.querySelector('.kolo svg #pomidor').classList.add('pomidor');
})
#pomidor {
transform-origin: 204px 41px;
opacity: 1;
transition: all 4s;
}
#pomidor.pomidor {
opacity: 1;
transform: rotate(-360deg);
}
<div class="kolo">
<svg width="246px" height="133px" viewBox="0 0 246 133" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="pomidor">
<g id="left">
<path d="M188.669237,2.85577743 L220.007541,78.7916793 C215.011273,80.9577555 209.503344,82.159 203.7202,82.159 C181.0712,82.159 162.6422,63.731 162.6422,41.08 C162.6422,23.7394433 173.442929,8.87328458 188.669237,2.85577743 Z" id="tomato-dark" fill="#ED2D24">
</path>
</g>
<g id="right">
<path d="M203.7202,0.0003 C198.1552,0.0003 192.8472,1.1183 188.0022,3.1323 L219.4392,79.0273 C234.3122,72.8423 244.8002,58.1653 244.8002,41.0803 C244.8002,18.4283 226.3712,0.0003 203.7202,0.0003" id="tomato-light" fill="green">
</path>
</g>
</g>
</svg>
</div>
<button> Rotate circle</button>

Can I generate SVG with js?

I'm looking for a way to generate the following SVG with javascript.
I honestly have no idea on how to do it, neither I know if it's possible!
Since this is for an university project, I'm only allowed to use pure javascript, with no libraries.
EDIT: if it can help, I also have the .svg file. I can't use .png because I need to animate 2 elements within it.
<svg version="1.1" id="Livello_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 225.5 225.5" style="enable-background:new 0 0 225.5 225.5;" xml:space="preserve">
<path id="Luna1" class="st0" d="M112.8,225.5C50.5,225.5,0,175,0,112.8S50.5,0,112.8,0V225.5z"/>
<path id="Luna2" class="st1" d="M112.8,0C175,0,225.5,50.5,225.5,112.8S175,225.5,112.8,225.5V0z"/>
<circle id="Cerchio1" class="st2" cx="46.4" cy="112.8" r="18.4"/>
<circle id="Cerchio2" class="st2" cx="179.1" cy="112.8" r="18.4"/>
</svg>
<style type="text/css">
.st0{fill:#6DC06B;}
.st1{fill:#0B7660;}
.st2{fill:#17AF80;}
svg {
width: 200px;
height: 200px;
cursor: pointer;
transform: rotate(45deg);
}
.st2 {
-webkit-transition: 1s ease-in-out;
-moz-transition: 1s ease-in-out;
-o-transition: 1s ease-in-out;
transition: 1s ease-in-out;
}
</style>
Here's the full code that includes the animationon Fiddle!
I would really appreciate any help! Thank you in advance!
Let me know if you need any further details!
You can create nodes and add them to an SVG element like this:
// store namespace
let ns = "http://www.w3.org/2000/svg"
// create svg element
let svg = document.createElementNS(ns, "svg")
document.body.appendChild(svg)
// set width and height
svg.setAttribute("width", "100")
svg.setAttribute("height", "100")
// just an example to create a path
let path = document.createElementNS(ns, "path")
path.setAttributeNS(null, "stroke-width", 2)
path.setAttributeNS(null, "d", "M300 200 m-10.292521287196118 -2.2297708853450806 l-8.789109120138724 -1.6054377906297648")
// add the path to the svg
svg.appendChild(path)
EDIT
Added option to create the SVG tag as well.

Animate scale rotate svg element

Assuming I have an svg made with inkscape.
In this SVG set with a viewbox, I want to animate each element inside the SVG.
There is no problem for translate or opacity ... but when I need to rotate or scale a single element, it acting weird.
I try to correctly understand the concept of the viewbox but I need some help.
I understand that I have only one origin point when I have only one viewbox should I set multiple viewbox ?
<?xml version="1.0" encoding="UTF-8"?>
<svg id="SVGRoot" version="1.1" viewBox="0 0 700 500" xmlns="http://www.w3.org/2000/svg">
// rotate or scale acting weird
<ellipse id="path9238" cx="332.91" cy="143.85" rx="64.941" ry="67.676" style="fill-rule:evenodd;fill:#f00;stroke:#000"/>
// rotate or scale acting weird
<rect id="rect9240" x="400.59" y="270.31" width="173.63" height="177.73" style="fill-rule:evenodd;fill:#f00;paint-order:normal"/>
// rotate or scale acting weird
<path id="path9242" d="m233.79 453.52-153.64-138.25 196.55-63.937z" style="fill-rule:evenodd;fill:#f00;paint-order:normal"/>
</svg>
I'm using anime.js 3.0 or CSS or I can try anything else
In Svg, the coordinates of any figure always have an absolute value that is calculated from the upper left corner of the SVG canvas.
Therefore, when applying the scale (2) command, the coordinates of the center of the figure will also be doubled and the figure will shift to the right and down.
<svg id="SVGRoot" version="1.1" width="500" height="500" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg" style="border:1px solid grey;">
<rect id="rect9240" transform="scale(2)" x="100" y="100" width="100" height="100" style="fill-rule:evenodd;fill:#f00;> stroke:#000; paint-order:normal">
<animateTransform
xlink:href="#rect9240"
attributeName="transform"
type="scale"
values="1;2;2;1;1"
dur="8s"
fill="freeze"
repeatcount="indefinite"
/>
</rect>
<circle cx="150" cy="150" r="3" fill="black" />
<circle cx="300" cy="300" r="3" fill="dodgerblue" />
<text x="150" y="140" font-size="16px" text-anchor="middle" > Center (150,150) </text>
<text x="300" y="290" font-size="16px" text-anchor="middle" > Center (300,300) </text>
</svg>
To return the enlarged figure to its original position, you must use the command translate (X, Y)
There is a great post here by #Paul LeBeau where this is explained in detail.
CSS solution
In order not to calculate the position of the center of the figure, you can use the CSS rule transform-box: fill-box
When the value of the fill-box attribute is selected
The object bounding box is used as the reference box.
Below is an example of increasing and decreasing the size of figures:
svg {
width:50%;
}
.ellipse1, .rect1, .path1 {
transform-box: fill-box;
animation: scale 3s linear infinite alternate;
animation-direction: alternate;
transform-origin:50% 50%;
}
#keyframes scale {
0% { transform: scale(0.5); }
100% { transform: scale(1); }
}
<svg id="SVGRoot" version="1.1" viewBox="0 0 700 500" xmlns="http://www.w3.org/2000/svg">
<ellipse class="ellipse1" id="path9238" cx="332.91" cy="143.85" rx="64.941" ry="67.676" style="fill-rule:evenodd;fill:#f00;stroke:#000"/>
<rect class="rect1" id="rect9240" x="400.59" y="270.31" width="173.63" height="177.73" style="fill-rule:evenodd;fill:#f00; stroke:#000; paint-order:normal"/>
<path class="path1" id="path9242" d="m233.79 453.52-153.64-138.25 196.55-63.937z" style="fill-rule:evenodd;fill:#f00;stroke:#000; paint-order:normal"/>
</svg>
Rotation example
svg {
width:50%;
}
.ellipse1, .rect1, .path1 {
transform-box: fill-box;
animation: spin 4s linear infinite alternate;
transform-origin:50% 50%;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(359deg); }
}
<path class="path1" id="path9242" d="m233.79 453.52-153.64-138.25 196.55-63.937z" style="fill-rule:evenodd;fill:#f00;stroke:#000; paint-order:normal"/>
</svg>
<svg id="SVGRoot" version="1.1" viewBox="0 0 700 500" xmlns="http://www.w3.org/2000/svg">
<ellipse class="ellipse1" id="path9238" cx="332.91" cy="143.85" rx="64.941" ry="67.676" style="fill-rule:evenodd;fill:#f00;stroke:#000"/>
<rect class="rect1" id="rect9240" x="400.59" y="270.31" width="173.63" height="177.73" style="fill-rule:evenodd;fill:#f00; stroke:#000; paint-order:normal"/>
<path class="path1" id="path9242" d="m233.79 453.52-153.64-138.25 196.55-63.937z" style="fill-rule:evenodd;fill:#f00;stroke:#000; paint-order:normal"/>
</svg>
Increase and rotation
svg {
width:50%;
}
.ellipse1, .rect1, .path1 {
transform-box: fill-box;
animation: scale1 4s linear, spin 4s linear 4s infinite alternate;
transform-origin:50% 50%;
}
#keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
#keyframes scale1 {
0% { transform: scale(0.5);}
100% { transform: scale(1);}
}
<svg id="SVGRoot" version="1.1" viewBox="0 0 700 500" xmlns="http://www.w3.org/2000/svg">
<ellipse class="ellipse1" id="path9238" cx="332.91" cy="143.85" rx="64.941" ry="67.676" style="fill-rule:evenodd;fill:#f00;stroke:#000"/>
<rect class="rect1" id="rect9240" x="400.59" y="270.31" width="173.63" height="177.73" style="fill-rule:evenodd;fill:#f00; stroke:#000; paint-order:normal"/>
<path class="path1" id="path9242" d="m233.79 453.52-153.64-138.25 196.55-63.937z" style="fill-rule:evenodd;fill:#f00;stroke:#000; paint-order:normal"/>
</svg>
I guess you mean the transform-origin CSS propierty. It is related to the center point of the svg file. Then, you need to calculate the center point of the element to animate related to document's center point.
For the animations you can use CSS animations.
The basics of viewBox is that the 4 numbers are "x y width height”. Generally the x/y coordinates are 0 which keeps your origin in the top left corner. Something people often do is place the origin in the middle.
In order to do that you move your viewBox top left by half of the width and half the height. In your case "-350 -250 700 500".

Animated SVG, circle stroke hover

Looking to mimic the animated arrow:
http://uve.info/
On hover the stroke overlays the circle, I have the shape created in Illustrator, thats fine, positioning easy. just animating the stroke.
HTML (Inline SVG):
<svg id="right_arrow" class="direction__right direction__item" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="80px" height="80px" viewBox="0 0 80 80" xml:space="preserve">
<polygon class="ring offset-colour" points="32.5,52 47.5,40 32.5,28"/>
<path class="arrow offset-colour" d="M40,1c21.5,0,39,17.5,39,39S61.5,79,40,79S1,61.5,1,40S18.5,1,40,1 M40,0C17.9,0,0,17.9,0,40s17.9,40,40,40s40-17.9,40-40S62.1,0,40,0L40,0z"/>
</svg>
The path, is a circle already. I want another path that sits on top of the current path to emulate the uve.info site. This whole animation is done via hover. This is what the arrow should look like mid animation proving a pain.
What would be the best way to invoke the stroke?
Thanks all.
If you're targeting somewhat modern browsers, I'd suggest using svg animations.
You can animate strokes by using a stroke-dasharray that has the length of your circle (2 * PI * r) and a dash offset of equal length. Play around with the animation values of your dash length and offset to create different effects.
Here's an example of how to do so.
.circle:hover {
/* calculate using: (2 * PI * R) */
stroke-dasharray: 227;
stroke-dashoffset: 0;
animation-iteration-count: infinite;
animation-name: rotate;
animation-duration: 2s;
animation-direction: alternate;
animation-timing-function: linear;
}
#keyframes rotate {
to {
stroke-dashoffset: 227;
}
}
<svg id="right_arrow" class="direction__right direction__item" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="80px" height="80px" viewBox="0 0 80 80" xml:space="preserve">
<polygon class="ring offset-colour" points="32.5,52 47.5,40 32.5,28" />
<circle cx="40" cy="40" r="36" fill="transparent" stroke="black" stroke-width="2" />
<circle class="circle" cx="40" cy="40" r="36" fill="transparent" stroke="black" stroke-width="4" />
</svg>
Using the css animation property and #keyframes, you can do all kinds of fancy stuff. If you'd rather keep it simple, you could also try using the transition property, like in the example below. Note that I've used the svg transform attribute to change the starting point of the dashed stroke.
.another-circle {
stroke-dasharray: 227;
stroke-dashoffset: 227;
transition: stroke-dashoffset 2s linear;
}
.another-circle:hover {
stroke-dashoffset: 0;
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="80px" height="80px" viewBox="0 0 80 80" xml:space="preserve">
<polygon class="ring offset-colour" points="32.5,52 47.5,40 32.5,28" />
<circle cx="40" cy="40" r="36" fill="transparent" stroke="black" stroke-width="2" />
<circle transform="rotate(-90 40 40)" class="another-circle" cx="40" cy="40" r="36" fill="transparent" stroke="black" stroke-width="4" />
</svg>

Categories

Resources