SVG object animation on a path line - javascript

I want to animate a svg path on a line. Exactly like this codepen but instead of use a polygon, I want to use a more complex svg. (path)
Codepen example
Html :
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" height="100%" viewBox="0 0 800 300" enable-background="new 0 0 800 300" xml:space="preserve">
<path d="M30,30 L770,30" />
<path d="M29.833,113.5C29.833,178.667,99,271.334,257,271.334 S475.5,81,375.5,81S305,271.334,770,271.334"/>
<polygon points="15,0 18.541,11.459 30,11.459 20.729,18.541 24.271,30 15,22.918 5.729,30 9.271,18.541 0,11.459 11.459,11.459 "/>
<polygon points="15,0 18.541,11.459 30,11.459 20.729,18.541 24.271,30 15,22.918 5.729,30 9.271,18.541 0,11.459 11.459,11.459 "/>
</svg>
Is anyone know how to replace the polygon by a svg path file ?
Thank you,

In this example I'm using a complex <symbol> element instead of the star. The <symbol> contains a path and 4 circles. I've putted this symbol in a hidden SVG element.
In the main SVG element I've changed the polygons (stars) with 2 <use> elements that are using the #cow symbol.
In the Javascript I've changed var stars = svgContainer.getElementsByTagName("polygon"); with var stars = svgContainer.getElementsByTagName("use");
You can if you want to change the name of the variable from stars to cows in all your javascript.
/* A variable to keep track of where we are on the line
0 = start, 1 = end */
var counter = 0;
/* A boolean variable to keep track of the direction we want to travel in
true = move to the left, false move to the right */
var direction = true;
/* First get a reference to the enclosing div and then to
the 2 svg paths */
var svgContainer = document.getElementById("outerWrapper");
var ns = "http://www.w3.org/2000/svg";
var svg = svgContainer.getElementsByTagNameNS(ns, "path");
/* the var 'svg' contains a reference to two paths so svg.length = 2
svg[0] is the straight line and svg[1] is the curved lines */
/* Now get the length of those two paths */
var straightLength = svg[0].getTotalLength();
var curveLength = svg[1].getTotalLength();
/* Also get a reference to the two star polygons */
var stars = svgContainer.getElementsByTagName("use");
function moveStar() {
/* Check to see where the stars are journeys to determine
what direction they should be travelling in */
if (parseInt(counter,10) === 1) {
/* we've hit the end! */
direction = false;
} else if (parseInt(counter,10) < 0) {
/* we're back at the start! */
direction = true;
}
/* Based on the direction variable either increase or decrease the counter */
if (direction) {
counter += 0.003;
} else {
counter -= 0.003;
}
/* Now the magic part. We are able to call .getPointAtLength on the tow paths to return
the coordinates at any point along their lengths. We then simply set the stars to be positioned
at these coordinates, incrementing along the lengths of the paths */
stars[0].setAttribute("transform","translate("+ (svg[0].getPointAtLength(counter * straightLength).x -15) + "," + (svg[0].getPointAtLength(counter * straightLength).y -15) + ")");
stars[1].setAttribute("transform","translate("+ (svg[1].getPointAtLength(counter * curveLength).x -15) + "," + (svg[1].getPointAtLength(counter * curveLength).y -15) + ")");
/* Use requestAnimationFrame to recursively call moveStar() 60 times a second
to create the illusion of movement */
requestAnimationFrame(moveStar);
}
requestAnimationFrame(moveStar);
body {
margin: 0;
background: #565279;
}
.outerWrapper {
width: 800px;
height: 300px;
position: relative;
}
.outerWrapper svg {
position: absolute;
}
.outerWrapper svg path {
fill:none;
stroke:#DABDD8;
stroke-width:5;
stroke-dasharray:10,10;
}
.outerWrapper svg polygon {
fill:orange;
}
<svg style="display:none">
<defs>
<symbol id="cow" viewBox= "0 0 200 200">
<path class="head" fill="gold" stroke-width="1" d="M87 110 c5 0 12-0 21-0 c9-0 26 2 33-4 c5-4 2-14 0-19 c-6-11-14-11-25-11c-10-0-15-7-8-16 c11-13 22-2 35-3 c7-0.622 15.812-11.692 5.757-14.441c-3.556-0.973-12.802 0.949-15.412-0.906c6.371 4.5 20.854-11.553 23.774-15.168 c4.103-5.079 7.713-10.561 10.987-16.205c0.678-1.169 2.928-7.366 4.133-7.882c-7.42 3.175-14.234 8.021-22.368 10.7 c-20.585 6.695-42.426 9.711-64.039 9.711c-18.865 0.045-41.083-2.831-60.479-8.723C16.774 10.2 9.1 5 0.6 1.4 C4.425 3 11 19.4 13.8 23.083c3.03 4 18.5 22.6 25.6 17.551c-2.173 1.544-10.67-0.021-13.562 0.5 c-9.382 1.672-7.292 11.8 1 14.308c12.151 3.7 23.371-8.617 35 0.611c7.217 5.7 11.1 18.941-1.428 19.4 c-10.27 0.355-20.138-1.575-26.384 8.23c-4.091 6.423-6.256 13.976-2.265 20.886c3.503 6.1 24.2 4.7 30.3 4.9 C70.382 109.8 78.7 109.9 87 109.8"/>
<circle class="eyeR" fill="#040B13" stroke-width="1" cx="117.3" cy="64.9" r="6"/>
<circle class="eyeL" fill="#040B13" stroke-width="1" cx="55.4" cy="64.9" r="6"/>
<circle class="nostrilL" fill="#446B70" stroke-width="1" cx="50.6" cy="92.9" r="9"/>
<circle class="nostrilR" fill="#446B70" stroke-width="1" cx="123.4" cy="92.9" r="9"/>
</symbol>
</defs>
</svg>
<div class="outerWrapper" id="outerWrapper">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="100%" height="100%" viewBox="0 0 800 300" enable-background="new 0 0 800 300" xml:space="preserve">
<path class="pth" d="M30,30 L770,30" />
<path class="pth" d="M29.833,113.5C29.833,178.667,99,271.334,257,271.334 S475.5,81,375.5,81S305,271.334,770,271.334"/>
<use xlink:href="#cow" width="30" height="30" />
<use xlink:href="#cow" width="30" height="30" />
</svg>
</div>

Related

How to make svg fit container in React?

See the image above. That is the svg container I have highlighted but I only want the svg to take up it's actual size (i.e. just the inner white triangle)
I've tried changing parent width and height but nothing seems to work. how do I fix this?
this is the markup:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#fff">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
the second path seems to be the exact width and height of what I need
second edit:
export const Icon = ({ name }) => {
let Icon: any = icons[name]
return (
<div
className={css`
display: flex;
align-items: center;
`}
>
<Icon height={24} fill="red" />
</div>
)
}
and icons is
import { ReactComponent as MyIcon } from "./my-icon.svg"
const icons = {
"my-icon": MyIcon
}
M12 16l-6-6h12z represents your triangle and means
move to the point with coordinates 12 16
then draw the line to -6 -6 (relatively to current point)
then draw the horizontal line with the length equals to 12 then
to go the first point
And you get your icon as the result.
There are several ways you can fit the path, but you should understand how a path works. You can read about it here.
And see examples below
First way to fit:
svg {
border: 1px solid red;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#red">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M 12 24 l -12 -24 h24 z"></path>
</svg>
Second:
svg {
border: 1px solid red;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#red">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M 12 12 l -12 -12 h24 z"></path>
</svg>
You can see from the SVG that the width and height are currently set to 24.
A good start would be to set the width and height to 1em to better match the font size. See first example in the demo below.
If you feel the triangle is too small now, you can make it appear bigger by adjusting the viewBox. At the moment it has quite a bit of padding around it. To shring that padding, increase the viewBox x and y (first two numbers) and reduce the width and height (second two numbers. In the second example below, I have shrunk the padding by 3 units all around the icon by increasing the x and y by 3, and reducing the width and height, by 2 * 3 correspondingly.
The icon is now bigger compared to the text, But it can appear a bit high now.
There are a few ways to adjust that. Including the following:
Use position: relative and top: 2px to push the <svg> down a bit.
Reduce the icon size to something like 0.7em, so that the icon matches better the X height of the font, rather than the full Em height.
Use a different vertical-align setting to adjust the position of the icon relative to the rest of the line. In example 3 below, I have set the icon to vertical-align: text-top.
Push the icon lower in the viewBox by increasing the padding at the top of the viewBox. In example 4 below I've reduced the viewBox y (second number) to increase the top padding.
body {
background-color: dodgerblue;
}
div {
font: 16px sans-serif;
color: white;
}
svg {
width: 1em;
height: 1em;
}
<div>
Show
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="#fff">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
</div>
<div>
Show
<svg xmlns="http://www.w3.org/2000/svg" viewBox="3 3 18 18" width="24" height="24" fill="#fff">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
</div>
<div>
Show
<svg xmlns="http://www.w3.org/2000/svg" viewBox="3 3 18 18" width="24" height="24" fill="#fff" style="vertical-align: text-top">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
</div>
<div>
Show
<svg xmlns="http://www.w3.org/2000/svg" viewBox="3 1 18 18" width="24" height="24" fill="#fff">
<path fill="none" d="M0 0h24v24H0z"></path>
<path d="M12 16l-6-6h12z"></path>
</svg>
</div>

SVG progress bar with image

I am trying to create a progress bar (arc) with SVG. I currently have the progress bar working, it is moving the desired amount using a value stored in a data attribute, and looks pretty good. although i am trying to get an image to move around the arc with the bar. The image should start at 0 with the bar and move around to the completion point, say 50% which will be at the top.
<div class="w-100 case-progress-bar input p-2" style="position: relative;" data-percentage="80">
<svg class='progress_bar' viewBox="0 0 100 50" >
<g fill-opacity="0" stroke-width="4">
<path d="M5 50a45 45 0 1 1 90 0" stroke="#EBEDF8"></path>
<path class="progress" d="M5 50a45 45 0 1 1 90 0" stroke="#f00" stroke-dasharray="142" stroke-dashoffset="142"></path>
</g>
<circle fill="url(#image)" id='case_progress__prog_fill' class="case_progress__prog" cx="0" cy="0" r="8" fill="#999" stroke="#fff" stroke-width="1" />
<defs>
<pattern id="image" x="0%" y="0%" height="100%" width="100%" viewBox="0 0 60 60">
<image x="0%" y="0%" width="60" height="60" xlink:href="https://via.placeholder.com/150x150"></image>
</pattern>
</defs>
</svg>
</div>
(function(){
var $wrapper = $('.case-progress-bar'),
$bar = $wrapper.find('.progress_bar'),
$progress = $bar.find('.progress'),
$percentage = $wrapper.data('percentage');
$progress.css({
'stroke-dashoffset': 'calc(142 - (0 * 142 / 100))',
'transition': 'all 1s'
});
var to = setTimeout(function(){
$progress.css('stroke-dashoffset', 'calc(142 - (' + $percentage + ' * 142 / 100))');
clearTimeout(to);
}, 500);
})();
this is what I currently have
this is what i'm trying to achieve
To solve, you need to combine two animations:
Painting half of the arc from the beginning to the middle (top)
Animation of movement of a circle with an image inside
Set the same time for both animations
<div class="w-100 case-progress-bar input p-2" style="position: relative;" data-percentage="80">
<svg class='progress_bar' viewBox="0 0 100 50" >
<g fill-opacity="0" stroke-width="4">
<path id="pfad" d="M5 50C5 44.1 6.1 38.5 8.2 33.4 10.8 26.8 14.9 20.9 20.2 16.3 28.1 9.3 38.6 5 50 5" stroke="#EBEDF8"></path>
<path d="M5 50a45 45 0 1 1 90 0" stroke="#EBEDF8"></path>
<!-- Animation to fill half an arc -->
<path class="progress" d="M5 50a45 45 0 1 1 90 0" stroke="#f00" stroke-dasharray="142" stroke-dashoffset="142">
<animate attributeName="stroke-dashoffset" from="142" to="71" dur="4s" fill="freeze" />
</path>
</g>
<defs>
<pattern id="image" x="0%" y="0%" height="100%" width="100%" viewBox="0 0 60 60">
<image x="0%" y="0%" width="60" height="60" xlink:href="https://via.placeholder.com/150x150"></image>
</pattern>
</defs>
<circle fill="url(#image)" id='case_progress__prog_fill' class="case_progress__prog" cx="0" cy="0" r="8" fill="#999" stroke="#fff" stroke-width="1" >
<!-- Animation of movement of a circle with an image -->
<animateMotion begin="0s" dur="4s" fill="freeze">
<mpath xlink:href="#pfad" />
</animateMotion>
</circle>
</svg>
</div>
This is a case where it has advantages not to use thestroke-dasharray trick.
SVG can draw a marker at the end of a path. That marker can be any sort of grafic, and its syntax is just like that of a <symbol>. The position of the marker is defined by the path d attribute, and not influenced by a dashed stroke.
The general strategy is to compute the endpoint of the path
endpoint_x = center_x - cos(percentage / 100 * 180°) * radius
endpoint_y = center_y - sin(percentage / 100 * 180°) * radius
It is possible to do so relatively seamlessly because you decided to use only a half-circle to represent 100%. I have changed the way the path data are written to make that possible:
`M5 50 A 45 45 0 ${large} 1 ${x} ${y}`
A means: draw an arc and use absolute coordinates.
45 45 0 use a rx of 45, a ry of 45, do not rotate the axis of the arc.
${large} is the important bit. It discerns arcs of less than 180° from those that have more than 180°. As soon as that value would be crossed, the flag must change from 0 to 1. But since you are never expecting values above 180°, you would not need it.
1 means looking in the direction of the path, the arc should be drawn to the left side.
${x} ${y} are the final coordinates expressed in absolute, not relative coordinates.
The <marker> element has a number of attributes that must be considered:
orient="0" means the marker will not change its direction with the direction of the path at its end. orient="auto" would make it turn around , as you would like to see with an arrow for example.
markerUnits="userSpaceOnUse" means the numbers in the other attributes are in px units of the coordinate system of the path. Default would be markerUnits="strokeWidth", which would mean a size relative to the width of the stroke.
viewBox="-8 -8 16 16" is choosen because the circle used is centered around the coordinate system origin.
markerWidth="16" markerHeight="16" is saying how large the marker should be drawn.
refX="0" refY="-10" describes how the marker should be positioned: Take a point in the coordinate system of the marker itself (slightly above its topmost point and in the middle), and align it exactly with the end of the path.
Finally, note the marker-end="url(#image)" presentation attribute for the path. This is what sets the marker, and defines that it will be at the end of the path.
(function(){
var $wrapper = $('.case-progress-bar'),
$bar = $wrapper.find('.progress_bar'),
$progress = $bar.find('.progress'),
$percentage = $wrapper.data('percentage');
function computePath (percentage) {
var x = 50 - Math.cos(percentage / 100 * Math.PI) * 45,
y = 50 - Math.sin(percentage / 100 * Math.PI) * 45,
large = percentage > 100 ? 1 : 0;
return `M5 50 A 45 45 0 ${large} 1 ${x} ${y}`;
}
var to = setTimeout(function(){
$progress.attr('d', computePath($percentage));
clearTimeout(to);
}, 500);
})();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="w-100 case-progress-bar input p-2" style="position: relative;" data-percentage="80">
<svg class='progress_bar' viewBox="0 0 100 70" >
<g fill-opacity="0" stroke-width="4">
<path d="M5 50a45 45 0 1 1 90 0" stroke="#EBEDF8"></path>
<path class="progress" d="M5 50 A 45 45 0 0 1 95 50`" stroke="#f00"
marker-end="url(#image)"></path>
</g>
<marker id="image" orient="0" markerUnits="userSpaceOnUse"
viewBox="-8 -8 16 16"
markerWidth="16" markerHeight="16"
refX="0" refY="-10">
<circle r="8" fill="#aaf" />
<path d="M-6 0h12" stroke="#000" stroke-width="2" />
</marker>
</svg>
</div>
Just use a little JavaScript since you can't do trigonometry in CSS easily yet.
Codepen: https://codepen.io/mullany/pen/02cd0773588b3d975c8443ab6a87f670
(function(){
var $wrapper = $('.case-progress-bar'),
$bar = $wrapper.find('.progress_bar'),
$progress = $bar.find('.progress'),
$percentage = $wrapper.data('percentage');
var $circpos = $('.case_progress__prog');
$progress.css({
'stroke-dashoffset': 'calc(142 - (0 * 142 / 100))',
'transition': 'all 1s'
});
var to = setTimeout(function(){
$progress.css('stroke-dashoffset', 'calc(142 - (' + $percentage + ' * 142 / 100))');
var angleInRadians = 180*(1-$percentage/100) * 0.01745329251;
var xPos = 5 + 45 * (1 + Math.cos(angleInRadians) );
var yPos = 5 + 45 * (1 - Math.sin(angleInRadians) );
$circpos.css('cx', xPos);
$circpos.css('cy', yPos);
clearTimeout(to);
}, 500);
})();
I had a very similarly shaped loading SVG. You cen see it in action here. The green one at the bottom of the main banner.
What I used was the ProgressBar.js library. It made it very easy. I just focused on adjusting my SVG in terms of shape, because of the design of my site and then just used:
<svg id="progress-bar" xmlns="http://www.w3.org/2000/svg" width="650" height="526" viewBox="0 0 650 526">
<path opacity=".55" id="progress-bar-base" fill="none" stroke="#fefefe" stroke-width="20" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M84.591 512.622S20 441.232 20 324.941 79.903 131.327 137.4 84.476 269.116 20 324.94 20s124.217 15.743 184.57 62.183 89.159 101.578 103.475 142.419c14.316 40.84 25.203 105.21 8.788 170.477-16.415 65.268-43.628 101.409-56.482 117.543"></path>
<linearGradient id="grade" gradientUnits="userSpaceOnUse" x1="592.08" y1="157.665" x2="46.149" y2="472.859">
<stop offset="0" stop-color="#2cab9a"></stop>
<stop offset="1" stop-color="#8ed1c3"></stop>
</linearGradient>
<path fill-opacity="0" id="progress-bar-indicator" stroke="url(#grade)" stroke-width="24" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" d="M84.591 512.622S20 441.232 20 324.941 79.903 131.327 137.4 84.476 269.116 20 324.94 20s124.217 15.743 184.57 62.183 89.159 101.578 103.475 142.419c14.316 40.84 25.203 105.21 8.788 170.477-16.415 65.268-43.628 101.409-56.482 117.543" style="stroke-dasharray: 1362.73, 1362.73; stroke-dashoffset: 1140.45;"></path>
</svg>
var progressBar = new ProgressBar.Path('#progress-bar-indicator', {
easing: 'easeInOut',
duration: 2500
});
progressBar.set(0); // Initiate it at zero.
progressBar.animate(0.33); // this is your percentage of load
It's a very basic example, but I think you could adjust it to your needs.
As for the image, perhaps you could add it to your own SVG at the tip of the loading curve and it should move with it I think.

Animate SVG on hover - Make a smile from dots on hover

I have an SVG file with dots in in straight line. What I want to achieve is when a user hover on the svg file the dots to become a smile like the attached image.
The transition need to be smooth.
What is the best approach here? Can I do it with only css or I should use js also?
Thanks
With a SMIL animation, this is possible without any scripting. It morphs the cubic bezier path from straight to curved and back. The animation is triggered by mouseover and mouseout events over a transparent rectangle that sits on top of the line.
The line itself uses the combination of two little tricks: You can set a pathLength as an attribute, so that stroke-dasharray then computes dash lengths according to it. And for a stroke-dasharray: 0 1 in combination with stroke-linecap, the zero-length dashes are displayed as dots with the stroke width as their diameter. Just play with the values for pathLength and stroke-width to change the distance of the dots and their size.
#line {
fill: none;
stroke: black;
stroke-width: 4;
stroke-dasharray: 0 1;
stroke-linecap: round;
}
#overlay {
opacity: 0;
}
<svg viewBox="0 0 100 100" width="150">
<path id="line" d="M 10 50 C 35 50 65 50 90 50" pathLength="15">
<animate attributeName="d" begin="overlay.mouseover" dur="0.3s"
fill="freeze" restart="whenNotActive"
from="M 10 50 C 35 50 70 50 90 50"
to="M 15 30 C 20 70 80 70 85 30"/>
<animate attributeName="d" begin="overlay.mouseout" dur="0.3s"
fill="freeze" restart="whenNotActive"
from="M 15 30 C 20 70 80 70 85 30"
to="M 10 50 C 35 50 65 50 90 50"/>
</path>
<rect id="overlay" width="100%" height="100%" />
</svg>
In the next example I'm calculating the position of the dots on the paths each at one tenth of the path's total length calculated with getTotalLength. For each circle I'm setting the value of the initial cx (in this case since the initial cy==0 I don't set it.
Also inside each circle I'm adding 2 <animate> elements that will animate the cx and the cy to the next position on the curbed path.
The animation will begin on click.
const SVG_NS = "http://www.w3.org/2000/svg";//svg namespace
const dur = .5;// the animation duration
let circles = Array.from(document.querySelectorAll("circle"))
let la = a.getTotalLength();
let lb = b.getTotalLength();
let start = {x:-100,y:0}
circles.forEach((c,i) =>{
let da = i*la/10;
let db = i*lb/10;
let pa = a.getPointAtLength(da);
let pb = b.getPointAtLength(db);
c.setAttribute("cx",pa.x);
let a1 = document.createElementNS(SVG_NS,"animate");
a1.setAttribute("attributeName","cx");
a1.setAttribute("from",pa.x);
a1.setAttribute("to",pb.x);
a1.setAttribute("dur",dur);
a1.setAttribute("begin","svg.click");
c.appendChild(a1);
let a2 = document.createElementNS(SVG_NS,"animate");
a2.setAttribute("attributeName","cy");
a2.setAttribute("from",pa.y);
a2.setAttribute("to",pb.y);
a2.setAttribute("dur",dur);
a2.setAttribute("begin","svg.click");
c.appendChild(a2);
})
svg.addEventListener("click",()=>{
circles.forEach((c,i) =>{
let db = i*lb/10
let pb = b.getPointAtLength(db);
c.setAttribute("cx",pb.x)
c.setAttribute("cy",pb.y)
})
})
svg{border:solid}
path{fill:none; stroke:black;}
<svg id="svg" viewBox="-150 -50 300 150" width="300">
<path id="a" d="M-100,0 A15000,15000 0 0 0 100,0"/>
<path id="b" d="M-100,0 A150,150 0 0 0 100,0"/>
<circle r="5" />
<circle r="5" />
<circle r="5" />
<circle r="5" />
<circle r="5" />
<circle r="5" />
<circle r="5" />
<circle r="5" />
<circle r="5" />
<circle r="5" />
<circle r="5" />
</svg>
In the example the paths used to calculate the position of the circles are stroked. You can remove the stroke from the CSS.

Can't figure out this svg animation

I have been trying to get the circles in my stage to move around the ellipses, I even copied code from someone that had a very similar setup, but no dice. Heres the codepen and the markup below.
http://codepen.io/alcoven/pen/XJXMNW
<div id="wrapper">
<div class="container">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
<!--<g>
<circle fill="#F26451" cx="17.6" cy="3.3" r="1.4"/>
</g>
<g>
<circle fill="#F26451" cx="6.4" cy="23.1" r="1.4"/>
</g>-->
<line class="line" stroke-width="1.3" stroke-miterlimit="10" x1="3.7" y1="26.1" x2="26.2" y2="3.6"/>
<path class="vertical" d="M9.9,14.9a5,14 0 1,0 10,0a5,14 0 1,0 -10,0"/>
<ellipse class="_x34_5deg" transform="matrix(0.5037 0.8639 -0.8639 0.5037 20.2917 -5.4991)" stroke-width="1.3" stroke-miterlimit="10" cx="14.9" cy="14.9" rx="5" ry="14"/>
<ellipse class="_x2D_45deg" transform="matrix(-0.5037 0.8639 -0.8639 -0.5037 35.3327 9.5202)" stroke-width="1.3" stroke-miterlimit="10" cx="14.9" cy="14.9" rx="5" ry="14"/>
<path class="circlet" d="M16.4,3.2a1.3,1.3 0 1,0 2.6,0a1.3,1.3 0 1,0 -2.6,0"/>
<path class="circleb" d="M5,23.2a1.4,1.4 0 1,0 2.8,0a1.4,1.4 0 1,0 -2.8,0"/>
</svg>
</div>
</div>
CSS
#wrapper {
top:0;
left:0;
position:absolute;
width:100%;
height:100%;
background:#F26451;
}
.container {
width:100%;
position:fixed;
top:30%;
bottom:70%;
}
svg {
width:100px;
height:auto;
margin:10% auto;
display:block;
}
line, .vertical, ellipse._x34_5deg, ellipse._x2D_45deg {
stroke:#fff;
fill:none;
stroke-width:1.3;
stroke-miterlimit:10;
}
.circlet, .circleb {
stroke:#fff;
fill:#F26451;
stroke-width:1.3;
stroke-miterlimit:10;
}
.line {
display:none;
}
JS
(function($){
$('[class^="circle-"]').animate({opacity:1},500);
// get the animation path node
var $path = $('.vertical'), path = $path[0];
var $path2 = $('.vertical'), path2 = $path2[0];
// get the animation object node
var $obj = $('.circlet');
var $obj2 = $('.cirlceb');
// get path's length
var pathLength = path.getTotalLength();
var pathLength2 = path2.getTotalLength();
// create new tween by initializing TWEEN.Tween object from 0
var tween = new TWEEN.Tween({ length: 0 })
// to path length
// in 2000 milliseconds
.to({ length: pathLength }, 1500)
// on update callback fires on every tick
.onUpdate(function(){
var point = path.getPointAtLength(this.length);
$obj.css({
'transform': 'translate('+ point.x + 'px,'+ point.y +'px)'
});
}).repeat(999999999).start();
var tween2 = new TWEEN.Tween({ length: 0 })
// to path length
// in 2000 milliseconds
.to({ length: pathLength2 }, 1500)
// on update callback fires on every tick
.onUpdate(function(){
var point2 = path2.getPointAtLength(this.length);
$obj2.css({
'transform': 'translate('+ point2.x + 'px,'+ point2.y +'px)'
});
}).repeat(999999999).start();
// animate loop
animate = function(){
requestAnimationFrame(animate)
TWEEN.update()
}
//start the animation loop
animate()
}(jQuery));
Can't quite figure out why this isn't working, not sure if it's my js or the way my svg is setup please help :]
Here's the js I copied http://codepen.io/joshy/pen/cojbD
Define an animation path, which could be a circle , ellipse or Bezier curve.
Enclose the path definition between animateMotion tags ,one pair within each moving object definition, as follows:
<?xml version="1.0"?>
<svg width="120" height="120" viewBox="0 0 120 120"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<!-- Draw the outline of the motion path in grey, along
with 2 small circles at key points -->
<path d="M10,110 A120,120 -45 0,1 110 10 A120,120 -45 0,1 10,110"
stroke="lightgrey" stroke-width="2"
fill="none" id="theMotionPath"/>
<circle cx="10" cy="110" r="3" fill="lightgrey" />
<circle cx="110" cy="10" r="3" fill="lightgrey" />
<!-- Here is a red circle which will be moved along the motion path. -->
<circle cx="" cy="" r="5" fill="red">
<!-- Define the motion path animation -->
<animateMotion dur="6s" repeatCount="indefinite">
<mpath xlink:href="#theMotionPath"/>
</animateMotion>
</circle>
No javascript or jQuery required !

How to convert SVG CSS animation to pure JS code

I would like to convert an svg path animation, to a pure javascript one.
SVG code:
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="340px" height="333px" viewBox="0 0 340 333" enable-background="new 0 0 340 333" xml:space="preserve">
<path class="path" fill="#FFFFFF" stroke="#000000" stroke-width="4" stroke-miterlimit="10" d="M66.039,133.545c0,0-21-57,18-67s49-4,65,8
s30,41,53,27s66,4,58,32s-5,44,18,57s22,46,0,45s-54-40-68-16s-40,88-83,48s11-61-11-80s-79-7-70-41
C46.039,146.545,53.039,128.545,66.039,133.545z"/>
</svg>
CSS code:
.path {
stroke-dasharray: 10 10; /* 10px fill, 10px gap */
-webkit-animation: dash 10s linear normal infinite;
}
#-webkit-keyframes dash {
from {
stroke-dashoffset: 1000;
}
to {
stroke-dashoffset: 0;
}
}
Here is the Fiddle
I'm not sure why you need pure javascript, also this answer may not fit your needs as it actually does create inline CSS. But here it is, mostly taken from : https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/stroke-dashoffset
function dashOffset() {
var path = document.querySelector('.path');
var length = "1000";
// Clear any previous transition
path.style.transition = path.style.WebkitTransition = 'none';
// Set up the starting positions
path.style.strokeDasharray = "10 10";
path.style.strokeDashoffset = "1000";
// Trigger a layout so styles are calculated & the browser
// picks up the starting position before animating
path.getBoundingClientRect();
// Define our transition
path.style.transition = path.style.WebkitTransition = 'stroke-dashoffset 10s linear';
//listener to restart our loop
path.addEventListener('transitionend', dashOffset, false);
// Go!
path.style.strokeDashoffset = '0';
}
dashOffset();
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="340px" height="333px" viewBox="0 0 340 333" enable-background="new 0 0 340 333" xml:space="preserve">
<path class="path" fill="#FFFFFF" stroke="#000000" stroke-width="4" stroke-miterlimit="10" d="M66.039,133.545c0,0-21-57,18-67s49-4,65,8
s30,41,53,27s66,4,58,32s-5,44,18,57s22,46,0,45s-54-40-68-16s-40,88-83,48s11-61-11-80s-79-7-70-41
C46.039,146.545,53.039,128.545,66.039,133.545z" />
</svg>
There is also the requestAnimationFrame way :
function anim(){
var path = document.querySelector('.path');
path.style.strokeDasharray = "10 10";
path.style.strokeDashoffset--;
requestAnimationFrame(anim);
}
requestAnimationFrame(anim);
function anim(){
var path = document.querySelector('.path');
path.style.strokeDasharray = "10 10";
path.style.strokeDashoffset--;
requestAnimationFrame(anim);
}
requestAnimationFrame(anim);
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="340px" height="333px" viewBox="0 0 340 333" enable-background="new 0 0 340 333" xml:space="preserve">
<path class="path" fill="#FFFFFF" stroke="#000000" stroke-width="4" stroke-miterlimit="10" d="M66.039,133.545c0,0-21-57,18-67s49-4,65,8
s30,41,53,27s66,4,58,32s-5,44,18,57s22,46,0,45s-54-40-68-16s-40,88-83,48s11-61-11-80s-79-7-70-41
C46.039,146.545,53.039,128.545,66.039,133.545z" />
</svg>
Note that you may be able to reproduce it with the pure SVG animateMotion element.
There are a few javascript SVG libraries you could look into. I'm partial to Raphael (www.raphaeljs.com).
It would be something like this:
var paper = Raphael('canvas', 600, 600);
paper.path("M66.039,133.545c0,0-21-57,18-67s49-4,65,8s30,41,53,27s66,4,58,32s- 5,44,18,57s22,46,0,45s-54-40-68-16s-40,88-83,48s11-61-11-80s-79-7-70-41C46.039,146.545,53.039,128.545,66.039,133.545z")
.attr({fill:"#FFFFFF", stroke: "#000000", "stroke-width": "4", "stroke-miterlimit": "10"})
fiddle: http://jsfiddle.net/xLekbar4/
(Note: I added a div to the html as a container for the raphael "canvas", and changed the css to apply to 'path' elements, instead of elements with class '.path')
I strongly recommend you give a try to the Raphael.js library.
Using it you could easily reproduce what you want.
Additionally there's this very useful tool to convert your SVG file into Raphael.js reusable code : http://www.readysetraphael.com/
So your SVG file will become :
var rsr = Raphael('rsr', '340', '333');
var path_a = rsr.path("M66.039,133.545c0,0-21-57,18-67s49-4,65,8s30,41,53,27s66,4,58,32s-5,44,18,57s22,46,0,45s-54-40-68-16s-40,88-83,48s11-61-11-80s-79-7-70-41C46.039,146.545,53.039,128.545,66.039,133.545z");
path_a.attr({
class: 'path',
fill: '#FFFFFF',
stroke: '#000000',
"stroke-width": '4',
"stroke-miterlimit": '10',
'stroke-opacity': '1'
}).data('id', 'path_a');
var rsrGroups = [];
Then you just have to add a <div id="rsr"></div> to your document and also replace the CSS selector .path to path.
Et voilà !
Check this Fiddle to see the it in action :
http://jsfiddle.net/47nkqgmn/

Categories