I am trying to animate an svg element as following
a. I want to increase the width of the rect and control the rate of animation speed with a cubic-beziervalue, in this case it is cubic-bezier(0, 0, 0.58, 1).
b. There are also 3 lines which I also want to rotate, each of them exactly when the width of green rect reaches them at it's animation cycle and not before that.
What I tried so far
I came across this post and my understanding was that in the bezier curve graph, for a given output ratio (progression of animation -plotted on y axis), the time ratio (time ratio plotted on x axis) can be calculated, by solving the following equation.
Since, I can already calculate, at what phase of the animation the green rect is going to come across those lines
{50/125, 80/125, 125/125},
they become the Y value and I can use them to solve for t in y(t) and pass on that t value to solve x by passing on t value in x(t), which becomes my time ratio and I can use that to calculate the delay of individual lines.
This is what the code looks like, which unfortunately does not do what I wanted.
I am not sure whether the math has gone wrong here or something else needs to be done. I can feel that I am close, but not sure what needs to be done here for the exact output.
const svg = document.querySelector("svg");
//max px to increase the width animation
var mxWidth = 125;
//animation duration of the green rect
var dur = 2000;
//set style of the green rect
var x = document.querySelector("[class=hitter");
x.style.setProperty('--v2', `${(mxWidth/svg.viewBox.baseVal.width)*100}%`);
x.style.setProperty('--dur', `${dur}ms`)
//what are the x corodinates of the lines
var lnX = [50, 80, 125];
//at what output ratio (width increase) the green rect is likely to
//come across the lines
var progPct = lnX.map(x => x / mxWidth);
//what is the desired cubic bez
var cubicBezCurvVal = "0, 0, 0.58, 1"
//split bezier curve value
var cleanVal = cubicBezCurvVal.split(',');
//clean space with map -retunrns new array with the function, original array unchnaged
var cleanVal = cleanVal.map((x) => parseFloat(x.replace(/ /g, '')));
//p0
const p0 = { x: 0, y: 0 };
//p3
const p3 = { x: 1, y: 1 };
//p1
const p1 = { x: cleanVal[0], y: cleanVal[1] };
//p2
const p2 = { x: cleanVal[2], y: cleanVal[3] };
const x0 = p0.x;
const y0 = p0.y;
const x1 = p1.x;
const y1 = p1.y;
const x2 = p2.x;
const y2 = p2.y;
const x3 = p3.x;
const y3 = p3.y;
const coefficient_a_y = y3 - 3 * y2 + 3 * y1 - y0;
const coefficient_b_y = 3 * (y2 - 2 * y1 + y0);
const coefficient_c_y = 3 * (y1 - y0);
const coefficient_d_y = y0 - y1;
function getY(prgRatio) {
const outputRatio = prgRatio /*y value of cubic bezier curve */
//get all the t values of y of the cubic equation
var solution_t_y = [
{ sol: 1, cubicBezierProgressRatio_y: outputRatio - coefficient_d_y },
{ sol: 2, cubicBezierProgressRatio_y: (-1 * coefficient_b_y + Math.sqrt(Math.pow(coefficient_b_y, 2) - (4 * coefficient_a_y * coefficient_c_y))) / (2 * coefficient_a_y) },
{ sol: 3, cubicBezierProgressRatio_y: (-1 * coefficient_b_y - Math.sqrt(Math.pow(coefficient_b_y, 2) - (4 * coefficient_a_y * coefficient_c_y))) / (2 * coefficient_a_y) }
]
var solTY = solution_t_y.filter(x => x.cubicBezierProgressRatio_y > 0 && x.cubicBezierProgressRatio_y < 1);
function getX(t) {
return Math.pow(1 - t, 3) * x0 +
3 * Math.pow(1 - t, 2) * t * x1 +
3 * (1 - t) * Math.pow(t, 2) * x2 +
Math.pow(t, 3) * x3;
}
solTY.forEach(
(a, i) => {
a.cubicBezierTimeRatio_x = getX(a.cubicBezierProgressRatio_y)
}
)
return solTY;
}
var timeRatio = [];
progPct.forEach(
(a) => timeRatio.push(getY(a))
)
//get all the receivers
var impacter = document.querySelectorAll("[class^='axisLines']");
timeRatio.forEach(
(a, i) => {
(i == 2) ? impacter[i].style.setProperty('--del', `${dur}ms`): impacter[i].style.setProperty('--del', `${(a[0].cubicBezierTimeRatio_x)*dur}ms`)
}
)
.hitter {
visibility: hidden;
/*hide default*/
animation-name: moveWidth;
animation-delay: 0s;
animation-duration: var(--dur);
animation-iteration-count: 1;
animation-timing-function: cubic-bezier(0, 0, 0.58, 1);
animation-direction: normal;
animation-fill-mode: forwards;
}
[class^="axisLine"] {
transform-box: fill-box;
transform-origin: bottom;
stroke-width: 1;
color: green;
animation: rotate 1s cubic-bezier(0, 0, 0.58, 1) var(--del) 1 alternate forwards;
/*rotate 1s cubic-bezier(0, 0, 0.58, 1) var(--del) 1 alternate forwards;*/
}
#keyframes moveWidth {
0% {
visibility: visible;
/*show again*/
width: 0%;
}
100% {
visibility: visible;
/*Edit-2 maintains visibility after animation overs*/
width: var(--v2);
}
}
#keyframes rotate {
0% {
transform: rotate(0deg);
}
50% {
transform: rotate(7.5deg);
}
}
<!DOCTYPE html>
<html>
<body>
<link rel="stylesheet" href="style.css">
</link>
<svg class="layer1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 200">
<rect class="bg" id="bg" width="200" height="100" fill="#e6e6e6"></rect>
<rect class="hitter" id="hitter" x="0" y="40" width="0" height="5" fill="#92D050" stroke="#92D050"
stroke-width="0.1" ></rect>
<line class="axisLines0" id="axisLines0" x1="50" x2="50" y1="0" y2="100" stroke="#ED7D31" stroke-width=".5"
></line>
<line class="axisLines1" id="axisLines1" x1="80" x2="80" y1="0" y2="100" stroke="#548235" stroke-width=".5"
></line>
<line class="axisLines2" id="axisLines2" x1="125" x2="125" y1="0" y2="100" stroke="#00B0F0" stroke-width=".5">
</line>
</svg>
<script src="index3.js"></script>
</body>
</html>
When solving such problems, when several animations are interconnected, it is better to use SMIL SVG (in my opinion).
Since smil has a powerful tool that allows you to flexibly set the conditions for sequential start, pause, stop animations.
If there is a need to further change the logic of the work of many animations, then it is enough to change the logical chains in the begin attribute.
begin="dash1.end+1s"
The example below runs multiple animations one after the other, but we don't need to count the timings and delays for each animation.
Animation starts after clicking on the canvas
#tube {
stroke:red;
}
.axisLine {
stroke:green;
}
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 400 200" >
<rect x="20" y="50" width="200" height="50" fill="none" stroke="black" />
<path id="tube" stroke-dasharray="0,200" d="M20,75 220,75" >
<animate id="dash1" attributeName="stroke-dasharray" begin="svg1.click" dur="1s" values="0 200;50 150" fill="freeze" restart="never" />
<animate id="dash2" attributeName="stroke-dasharray" begin="dash1.end+1s" dur="1s" values="50 150;100 100" fill="freeze" />
<animate id="dash3" attributeName="stroke-dasharray" begin="dash2.end+1s" dur="1s" values="100 100;150 50" fill="freeze" />
<animate id="dash4" attributeName="stroke-dasharray" begin="dash3.end+1s" dur="1s" values="150 50;200 0" fill="freeze" />
</path>
<!-- vertical green lines -->
<path id="line1" class="axisLine" d="M70,50 70,100"/>
<path id="line2" class="axisLine" d="M120,50 120,100"/>
<path id="line3" class="axisLine" d="M170,50 170,100" />
<path id="line4" class="axisLine" d="M220,50 220,100" />
</svg>
The example below runs multiple animations one after the other, but we don't need to count the timings and delays for each animation:
The first animation of the growth of the horizontal line -
id="dash1" starts after clicking on the canvas begin="svg1.click"
The second green vertical line rotation animation starts after the
end of the first animation begin="dash1.end"
The third animation in order - the horizontal growth of the line will
begin after the end of the rotation of the green line
#tube {
stroke:brown;
}
.axisLine {
stroke:green;
}
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 400 200" >
<rect x="20" y="50" width="200" height="50" fill="none" stroke="black" />
<path id="tube" stroke-dasharray="0,200" d="M20,75 220,75" >
<animate id="dash1" attributeName="stroke-dasharray" begin="svg1.click" dur="1s" values="0 200;50 150" fill="freeze" restart="never" />
<animate id="dash2" attributeName="stroke-dasharray" begin="dash1.end+1s" dur="1s" values="50 150;100 100" fill="freeze" />
<animate id="dash3" attributeName="stroke-dasharray" begin="dash2.end+1s" dur="1s" values="100 100;150 50" fill="freeze" />
<animate id="dash4" attributeName="stroke-dasharray" begin="dash3.end+1s" dur="1s" values="150 50;200 0" fill="freeze" />
</path>
<!-- Vertical line animation-->
<path id="line1" class="axisLine" d="M70,50 70,100">
<animateTransform id="aT1"
attributeName="transform"
type="rotate"
begin="dash1.end" dur="1s"
values="
0 70 100;
14 70 100;
0 70 100"
/>
</path>
<path id="line2" class="axisLine" d="M120,50 120,100"/>
<path id="line3" class="axisLine" d="M170,50 170,100" />
<path id="line4" class="axisLine" d="M220,50 220,100" />
</svg>
Below is the complete code:
#tube {
stroke:brown;
}
.axisLine {
stroke:green;
}
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 400 200" >
<rect x="20" y="50" width="200" height="50" fill="none" stroke="black" />
<path id="tube" stroke-dasharray="0,200" d="M20,75 220,75" >
<!-- Line Growth Animation -->
<animate id="dash1" attributeName="stroke-dasharray" begin="svg1.click" dur="1s" values="0 200;50 150" fill="freeze" restart="never" />
<animate id="dash2" attributeName="stroke-dasharray" begin="aT1.end+0.2s" dur="1s" values="50 150;100 100" fill="freeze" />
<animate id="dash3" attributeName="stroke-dasharray" begin="aT2.end+0.2s" dur="1s" values="100 100;150 50" fill="freeze" />
<animate id="dash4" attributeName="stroke-dasharray" begin="aT3.end+0.2s" dur="1s" values="150 50;200 0" fill="freeze" />
</path>
<!-- Animation of rotation of vertical lines -->
<path id="line1" class="axisLine" d="M70,50 70,100">
<animateTransform id="aT1"
attributeName="transform"
type="rotate"
begin="dash1.end" dur="1s"
values="
0 70 100;
14 70 100;
0 70 100"
/>
</path>
<path id="line2" class="axisLine" d="M120,50 120,100">
<animateTransform id="aT2"
attributeName="transform"
type="rotate"
begin="dash2.end" dur="1s"
values="
0 120 100;
14 120 100;
0 120 100"
/>
</path>
<path id="line3" class="axisLine" d="M170,50 170,100" >
<animateTransform id="aT3"
attributeName="transform"
type="rotate"
begin="dash3.end" dur="1s"
values="
0 170 100;
14 170 100;
0 170 100"
/>
</path>
<path id="line4" class="axisLine" d="M220,50 220,100" >
<animateTransform id="aT4"
attributeName="transform"
type="rotate"
begin="dash4.end" dur="1s"
values="
0 220 100;
14 220 100;
0 220 100"
/>
</path>
</svg>
In the following example, I will double the time of 1 and 3 of the line growth animation - dur="2s"
Note: that the logic of the application will not break, the animations will still go one after another
If CSS animations were used, then timings and delays would have to be recalculated for each animation.
Animation starts after clicking on the canvas
#tube {
stroke:brown;
}
.axisLine {
stroke:green;
}
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 400 200" >
<rect x="20" y="50" width="200" height="50" fill="none" stroke="black" />
<path id="tube" stroke-dasharray="0,200" d="M20,75 220,75" >
<!-- Line Growth Animation -->
<animate id="dash1" attributeName="stroke-dasharray" begin="svg1.click" dur="2s" values="0 200;50 150" fill="freeze" restart="never"/>
<animate id="dash2" attributeName="stroke-dasharray" begin="aT1.end+0.2s" dur="1s" values="50 150;100 100" fill="freeze" />
<animate id="dash3" attributeName="stroke-dasharray" begin="aT2.end+0.2s" dur="2s" values="100 100;150 50" fill="freeze" />
<animate id="dash4" attributeName="stroke-dasharray" begin="aT3.end+0.2s" dur="1s" values="150 50;200 0" fill="freeze" />
</path>
<!-- Animation of rotation of vertical lines -->
<path id="line1" class="axisLine" d="M70,50 70,100">
<animateTransform id="aT1"
attributeName="transform"
type="rotate"
begin="dash1.end" dur="1s"
values="0 70 100;14 70 100;0 70 100"
/>
</path>
<path id="line2" class="axisLine" d="M120,50 120,100">
<animateTransform id="aT2"
attributeName="transform"
type="rotate"
begin="dash2.end" dur="1s"
values="0 120 100;14 120 100;0 120 100"
/>
</path>
<path id="line3" class="axisLine" d="M170,50 170,100" >
<animateTransform id="aT3"
attributeName="transform"
type="rotate"
begin="dash3.end" dur="1s"
values="0 170 100;14 170 100;0 170 100"
/>
</path>
<path id="line4" class="axisLine" d="M220,50 220,100" >
<animateTransform id="aT4"
attributeName="transform"
type="rotate"
begin="dash4.end" dur="1s"
values="0 220 100;14 220 100;0 220 100"
/>
</path>
</svg>
Related
I try changing an SVG motion path according to a html select value using JS. The path updates as expected, but the element, which uses the path as a motion path continues to move along the original path. What am I missing?
function changepath(selectObject) {
let value = selectObject.value;
let path = document.getElementById("planePath");
let plane = document.getElementById("animPath");
let rotation = "rotate(" + value + ")";
path.setAttribute("transform", rotation);
plane.setAttribute("transform", rotation);
}
body {
background: #eee;
}
.planePath {
opacity: 0.8;
stroke: darkslategrey;
stroke-width: 2px;
fill: none;
}
.plane {
transform: scale(0.15);
}
select {
margin-left: 2em;;
}
<svg viewBox="0 0 2000 200">
<!-- <path class="planePath" id="planePath" d="M 0 0 C 200 250 250 50 550 150 C 850 250 700 180 1000 200 " /> -->
<path class="planePath" id="planePath" d="M 50 100 c 14 -3 736 -115 1900 0" />
<g id="plane" class="plane">
<rect x="0" y="0" width="100" height="100"/>
</g>
<animateMotion xlink:href="#plane" dur="6s" repeatCount="indefinite" rotate="auto">
<mpath id="animPath" xlink:href="#planePath" />
</animateMotion>
</svg>
<select name="route" id="route" onchange="changepath(this)">
<option value="0">0°</option>
<option value="1">1°</option>
<option value="2">2°</option>
<option value="3">3°</option>
<option value="4">4°</option>
</select>
As Danny mentioned in his answer the mpath is using the untransformed path. If you need it to be transformed you can wrap both the path and the animated rect in a group and transform the group.
<svg viewBox="0 0 2000 200">
<g transform="rotate(5)">
<path id="path" fill="none" stroke="red" stroke-width="5"
d="M 50 100 c 14 -3 736 -115 1900 0" />
<rect id="block" x="0" y="0" width="20" height="20"/>
<animateMotion href="#block" dur="2s" repeatCount="indefinite"
rotate="auto" restart="always">
<mpath href="#path" />
</animateMotion>
</g>
</svg>
Looks like animateMotion mpath can't handle the transform
<svg viewBox="0 0 2000 200">
<path id="NO_rotate" fill="none" stroke="green" stroke-width="5"
d="M 50 100 c 14 -3 736 -115 1900 0"/>
<path id="path" fill="none" stroke="red" stroke-width="5"
d="M 50 100 c 14 -3 736 -115 1900 0"
transform="rotate(5)"/>
<rect id="block" x="0" y="0" width="20" height="20"/>
<animateMotion href="#block" dur="2s" repeatCount="indefinite"
rotate="auto" restart="always">
<mpath href="#path" />
</animateMotion>
</svg>
I would like to fill a custom SVG to a specific percentage.
Here is my initial SVG
<svg width="202" height="195" viewBox="0 0 202 195" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.1" d="M96.8166 4.06964C16.0794 8.40606 -20.4645 94.8546 20.2957 157.019C54.6867 204.16 143.361 202.123 184.273 150.807C226.464 97.5789 163.505 0.38025 96.8166 4.06964Z" stroke="#313848" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
Suppose there is a progress of x% so I would like to fill this SVG like
<svg width="207" height="203" viewBox="0 0 207 203" fill="none" xmlns="http://www.w3.org/2000/svg">
<path opacity="0.1" d="M99.8166 12.0696C19.0794 16.4061 -17.4645 102.855 23.2957 165.019C57.6867 212.16 146.361 210.123 187.273 158.807C229.464 105.579 166.505 8.38025 99.8166 12.0696Z" stroke="#313848" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M99.8142 12.0736C166.502 8.38527 229.463 105.585 187.273 158.812" stroke="#EA7052" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M96.1683 2.4287C88.1789 2.85671 84.5529 11.2658 88.579 17.3074C91.9765 21.8887 100.751 21.6836 104.805 16.6905C108.986 11.5113 102.768 2.06471 96.1683 2.4287Z" fill="#EDEDEE" stroke="#EA7052" stroke-width="4.76054" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M171.545 162.236C169.583 169.548 177.007 175.33 184.329 173.522C189.985 171.84 192.408 163.889 188.57 158.747C184.582 153.434 173.156 156.193 171.545 162.236Z" fill="#EDEDEE" stroke="#EA7052" stroke-width="4.76054" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
I am not able to figure out how to achieve this.
I want it to be dynamic so that I can make it for any percentage.
Also, I need to animate it from the starting point to the endpoint in a circular motion.
Any help would be highly appreciatable.
As I've commented:
You can calculate the length of the path using the getTotalLength() method. This represents 100%.
Next you can get the length representing the x% (xperc in the code).
Now you can use stroke-dasharray to represent the partial path.
You can calculate the position of the last point using the getPointAtLength() method.
Please read the comments in my code.
//the desired percentege
let xperc = .35;
//the total length of the path
let tl = base.getTotalLength();
//the partial length at the given percentage xperc
let partial = tl * xperc;
//set the stroke-dasharray of the second use element
perc.setAttribute("stroke-dasharray", `${partial} ${tl -partial}`)
//calculate the position of the point marking the end position
let theEnd = base.getPointAtLength(partial);
// set the cx and the cy attributes for the end point
end.setAttribute("cx", theEnd.x);
end.setAttribute("cy", theEnd.y);
circle {
stroke: red;
fill:white;
stroke-width: 6.87634;
}
<svg width="207" height="203" viewBox="0 0 207 203" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="base" d="M99.8166,12.0696L99.8166,12.0696C166.505,8.38025 229.464,105.579 187.273,158.807C146.361,210.123 57.6867,212.16 23.2957,165.019C-17.4645,102.855 19.0794,16.4061 99.8166,12.0696Z" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</defs>
<use xlink:href="#base" stroke="silver" />
<use xlink:href="#base" stroke="red" id="perc" />
<circle cx="99.8166" cy="12.0696" r="10" />
<circle id="end" r="10" />
</svg>
OBSERVATION: since your path goes counter clockwise I had to reverse the path to get the desired result
And this is an example where I'm using an input type range to change the percent value:
let xperc = itr.value;
onInput();
itr.addEventListener("input", onInput)
function onInput() {
xperc = itr.value;
let tl = base.getTotalLength();
let partial = tl * xperc;
perc.setAttribute("stroke-dasharray", `${partial} ${tl - partial}`);
let theEnd = base.getPointAtLength(partial);
end.setAttribute("cx", theEnd.x);
end.setAttribute("cy", theEnd.y);
}
circle {
stroke: red;
fill:white;
stroke-width: 6.87634;
}
<input id="itr" type="range" min="0" max="1" step=".001" value=".35" /><br>
<svg width="207" viewBox="-5 -5 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="base" d="M99.8166,12.0696L99.8166,12.0696C166.505,8.38025 229.464,105.579 187.273,158.807C146.361,210.123 57.6867,212.16 23.2957,165.019C-17.4645,102.855 19.0794,16.4061 99.8166,12.0696Z" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</defs>
<use xlink:href="#base" stroke="silver" />
<use xlink:href="#base" stroke="red" id="perc" />
<circle cx="99.8166" cy="12.0696" r="10" />
<circle id="end" r="10" />
</svg>
And another demo where I'm using javascript to animate it from 0 to 1:
//the animation begins at 0
let xperc = 0;
//get the total length of the path
let tl = base.getTotalLength();
//the request animation id
let rid = null;
function Animation() {
rid = window.requestAnimationFrame(Animation);
// while xperc < 1 increase it's value by 0.001. Else stop the animation
if (xperc < 1) {
xperc += 0.001;
}else{window.cancelAnimationFrame(rid)}
//the same as in the first example
let partial = tl * xperc;
perc.setAttribute("stroke-dasharray", `${partial} ${tl - partial}`);
let theEnd = base.getPointAtLength(partial);
end.setAttribute("cx", theEnd.x);
end.setAttribute("cy", theEnd.y);
}
Animation();
circle {
stroke: red;
fill:white;
stroke-width: 6.87634;
}
<svg width="207" viewBox="-5 -5 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<defs>
<path id="base" d="M99.8166,12.0696L99.8166,12.0696C166.505,8.38025 229.464,105.579 187.273,158.807C146.361,210.123 57.6867,212.16 23.2957,165.019C-17.4645,102.855 19.0794,16.4061 99.8166,12.0696Z" stroke-width="6.87634" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round"/>
</defs>
<use xlink:href="#base" stroke="silver" />
<use xlink:href="#base" stroke="red" id="perc" />
<circle cx="99.8166" cy="12.0696" r="10" />
<circle id="end" r="10" />
</svg>
I'd like to animate(move) the text element from point A to B while its content is changing
SVG :
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="300 160 350 150" height="80px" width="100%" xml:space="preserve">
<text id="text_animated" x="422" y="280" fill="white" font-size="17">
<animate attributeName="y" from="150" to="250" begin="3s" dur="5.5s" repeatCount="1" fill="freeze"/>
</text>
<circle cx="422" cy="280" fill="red" r="5">
<animate id="animation_depth_circle" attributeName="cy" from="150" to="250" begin="indefinite" dur="1.5s" repeatCount="1" fill="freeze" onend="endAnimate()" />
</circle>
</svg>
JS:
var counter=0;
var test_t = setInterval(function(){
document.getElementById('text_animated').textContent = ""+counter;
if(counter===120){
clearInterval(test_t);
}
counter=counter+1;
},10);
My goal is to move the text (inside a circle) as the text is changing. The problem is that the text doesn't move. Only the circle does move.
btw. I can't use
document.getElementById('text_animated').setAttribuite('y',something);
because it is not synchronized with the SVG animation (if network bottleneck issues occur). I'm using Chrome.
EDIT :
I managed to move my text using dy as so :
<text x="422" y="280" fill="white" >
<animate attributeName="dy" from="0" to="250" dur="1.5s" repeatCount="indefinite"/>
</text>
The problem is that It doesn't move if I change the text with my javascript. So It's either change or move.
When you do textContent = "" + counter; you remove the animation from inside the text element. However you can declare the animation outside the animated element (the text in this case) and give an explicit target element for the animation by using the xlink:href attribute to reference the target's id: xlink:href="#text_animated".
Also you are animating the cy attribute. I prefer to use animateTransform and animate a translation instead
var counter = 0;
var test_t = setInterval(function() {
document.getElementById("text_animated").textContent = "" + counter;
if (counter === 120) {
clearInterval(test_t);
}
counter = counter + 1;
}, 10);
svg{background:black}
<svg viewBox="350 120 150 250" width="200">
<text id="text_animated" x="422" y="150" fill="white" font-size="17" transform="translate(0,0)">
</text>
<animateTransform
attributeType="XML"
attributeName="transform"
type="translate"
values="0,0; 0,100"
begin="3s"
dur="5.5s"
repeatCount="1" fill="freeze"
xlink:href="#text_animated" />
<circle cx="422" cy="280" fill="red" r="5">
<animateTransform id="animation_depth_circle"
attributeType="XML"
attributeName="transform"
type="translate"
values="0,0; 0,100"
begin="3s"
dur="1.5s"
repeatCount="1" fill="freeze"/>
</circle>
</svg>
Yet another solution would have been puting the text inside a tspan element <tspan id="text_animated"></tspan>
var counter = 0;
var test_t = setInterval(function() {
document.getElementById("text_animated").textContent = "" + counter;
if (counter === 120) {
clearInterval(test_t);
}
counter = counter + 1;
}, 10);
svg{background:black}
<svg viewBox="350 120 150 250" width="200">
<text x="422" y="150" fill="white" font-size="17" transform="translate(0,0)">
<animateTransform
attributeType="XML"
attributeName="transform"
type="translate"
values="0,0; 0,100"
begin="3s"
dur="5.5s"
repeatCount="1" fill="freeze"
/>
<tspan id="text_animated"></tspan>
</text>
<circle cx="422" cy="280" fill="red" r="5">
<animateTransform id="animation_depth_circle"
attributeType="XML"
attributeName="transform"
type="translate"
values="0,0; 0,100"
begin="3s"
dur="1.5s"
repeatCount="1" fill="freeze"/>
</circle>
</svg>
I've chanved the viewBox value because I wanted to see what I am doing. You can use what you want.
I'm struggling with something that should be easy, let me explain :
I have a SVG using the animateTransform tag I use JavaScript to
change the form attribut
then I use the beginElement()
The SVG
<svg version="1.2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%">
<defs>
<path id="right" fill="#F7931E" d="M55.002,30c0-13.808-11.192-25-25-25v6c10.477,0,19,8.523,19,19s-8.523,19-19,19v6 C43.81,55,55.002,43.808,55.002,30z">
<animateTransform id="rotate" attributeName="transform" type="rotate" from="0 30 30" to="180 30 30" begin="0s" fill="freeze"/>
</path>
<path id="left" fill="#F7931E" d="M11.002,30c0-10.477,8.523-19,19-19V5c-13.808,0-25,11.192-25,25s11.192,25,25,25v-6 C19.526,49,11.002,40.477,11.002,30z">
<animateTransform id="rotate2" attributeName="transform" type="rotate" from="0 30 30" to="180 30 30" begin="0s" fill="freeze"/>
</path>
<mask id="hide-left">
<rect x="0" y="0" width="30" height="60" fill="white"/>
</mask>
<mask id="hide-right">
<rect id="super" x="30" y="0" width="30" height="60" fill="white"/>
</mask>
</defs>
<!-- Layout -->
<use xlink:href="#right" mask="url(#hide-right)"/>
<use xlink:href="#left" mask="url(#hide-left)"/>
</svg>
The JS
var updateCircle= function (pourcentage) {
if (pourcentage < 0.5) {
var angle = pourcentage * 2;
var angle_vert = 180 * angle;
var angle_rouge = 0;
}
else {
var angle_vert = 180;
var angle = (1 - pourcentage) * 2;
var angle_rouge = 180 - (180 * angle);
}
$("#mod_countdown #right #rotate").attr('from', angle_vert + ' 30 30');
$("#mod_countdown #left #rotate2").attr('from', angle_rouge + ' 30 30');
$("#mod_countdown #right #rotate")[0].beginElement();
$("#mod_countdown #left #rotate2")[0].beginElement();
};
The example to play with : http://jsfiddle.net/oandcf08/2/
My problem is : it's not working on firefox and I don't know why...
[edit]
Fun fact : when I add the attribute transform : <path transform="scale(5)" ... it's working in firefox check it out : http://jsfiddle.net/oandcf08/3/ , it's working but I don't understand why and it don't seem to be a reliable workaround
I'm trying to draw a line animation of a circle. I need this to work on mobile so opted for SVG. I have a perfect working example, but it is very inefficient and makes the other animations on the page stutter.
This is what I currently have and what I'm trying to achieve: http://jsfiddle.net/sj76ysqs/
<svg class="bean-halo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
viewBox="0 0 500 500"
preserveAspectRatio="xMidYMid"
style="width:100%; height:100%; position:absolute; top:0; left:0;">
<path d="M200,200 " id="bean-halo" fill="none" stroke="#FF0000" stroke-linecap="round" stroke-width="2.5" stroke-dasharray="0.1,10" />
</svg>
(function() {
var i = 0,
circle = document.getElementById('bean-halo'),
angle = 0,
radius = 167,
interval = 20,
d, radians, x, y, e;
window.timer = window.setInterval(function() {
angle -= 5;
angle %= 360;
radians = (angle / 180) * Math.PI;
x = 250 + Math.cos(radians) * radius;
y = 250 + Math.sin(radians) * radius;
e = circle.getAttribute('d');
d = e + (i === 0 ? ' M ' : ' L ') + x + ' ' + y;
if (angle === -5 && i !== 0) {
window.clearInterval(window.timer);
this.beanHaloisDrawn = 1;
}
circle.setAttribute('d', d);
i++;
}.bind(this), interval);
})()
I'd like to use the following technique or something similar, but don't know enough about SVGs to do this: http://css-tricks.com/svg-line-animation-works/
I've also though about having a static dotted line that is masked by an animated line that reveals it, but again, I don't know how to do this.
Any help would be appreciated.
UPDATE: The solution has to work on an element with a image as background.
How about just manipulating stroke-dasharray of a circle
<svg class="bean-halo" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
viewBox="0 0 500 500"
preserveAspectRatio="xMidYMid"
style="width:100%; height:100%; position:absolute; top:0; left:0;">
<circle cx="200" cy="200" r="167" id="bean-halo" fill="none" stroke="#FF0000" stroke-linecap="round" stroke-width="2.5" stroke-dasharray="0.1,20000" />
</svg>
together with something like this...
(function() {
var angle = 0;
var circle = document.getElementById('bean-halo');
var dash="0.1,10 ";
var interval = 20;
window.timer = window.setInterval(function() {
circle.setAttribute("stroke-dasharray", dash + " 0, 20000");
dash = dash + "0.1,10 ";
if (angle >= 360) window.clearInterval(window.timer);
angle += 10.1/360;
}.bind(this), interval);
})()
If you don't want to use javascript you'll have to do the interpolation yourself by creating a huge animate with all the intermediate steps. I've done 4 below but you get the gist. You could create the attribute using javascript and a loop though.
<svg class="bean-halo" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1"
viewBox="0 0 500 500"
preserveAspectRatio="xMidYMid"
style="width:100%; height:100%; position:absolute; top:0; left:0;">
<circle cx="200" cy="200" r="167" stroke-width="1" stroke="red" fill="white">
<animate attributeName="stroke-dasharray"
values="1,10,0,20000;1,10,1,10,0,20000;1,10,1,10,1,10,0,20000;1,10,1,10,1,10,1,10,0,20000"
dur="4s" repeatCount="1" fill="freeze" />
</circle>
</svg>
Animation trick using two circles, no coding required:-
<svg width="400" height="400" viewBox="0 0 400 400" >
<circle cx="200" cy="200" r="167" stroke-dasharray="1,6" stroke-width="1" stroke="red" fill="white" />
<circle cx="200" cy="200" r="167" stroke-dasharray="1200,1200 " stroke-width="3" stroke-dashoffset="0" stroke="white" fill="none">
<animate attributeType="XML" attributeName="stroke-dashoffset" from="0" to="1200" dur="4s" repeatCount="1" fill="freeze" />
</circle>
</svg>