svg animationTransform from dynamic value - javascript

There is a circle. When you mouseover it increases from 1 to 2 when mouseout is reduced from 2 to 1.
With the rapid run the mouse over the visible circle of the race to widen the circle. The problem is that the animation start the circle from the values on which he managed will increase, and with the value 2. How to make so that when you mouseout the animation of reduction began with the value that had increased range.
<g transform="matrix(1 0 0 1 150 150)" id="sec7kpi">
<g transform="matrix(1 0 0 1 0 0)" id="sec7kpi-c1">
<ellipse fill="#372356" stroke="#27AE60" stroke-width="16" stroke-miterlimit="10" cx="0" cy="0" rx="71" ry="71" />
<text id="sec7text" x="-33" y="15" fill="#27AE60" font-family="LatoRegular" font-size="38.8363" pointer-events="none">KPI</text>
</g>
<defs>
<animateTransform attributeType="XML"
xlink:href="#sec7kpi-c1"
attributeName="transform"
type="scale"
dur="500ms"
from="1"
to="2"
restart="whenNotActive"
begin="mouseover"
fill="freeze"
id="c-hover"
/>
<animateTransform attributeType="XML"
xlink:href="#sec7kpi-c1"
attributeName="transform"
type="scale"
dur="500ms"
from="2"
to="1"
restart="whenNotActive"
begin="mouseout"
fill="freeze"
id="c-out"
/>

Simply delete the attribute from="2" from the second animateTransform element.
Because you are no longer providing a starting value for the mouseout animation, this has the effect of making this animation start at whatever value it has at the moment it is started, i.e. at the moment the mouse moves of the element. For example, if the user starts the initial mouseover animation by mousing over the element but then moves the mouse out when the scale has only reached 1.76, then the mouseout animation scaling will start at its current value, i.e. 1.76, not 2, and return to 1.
(To make the code you provided work in the code snippets below (at least in Firefox), I placed the minimal extra code required around your code to get it to work: i.e. I put <svg height="300"> at the top and </svg> at the bottom.)
Original problematic code with working snippet (essentially copied from your question):
<svg height="300">
<g transform="matrix(1 0 0 1 150 150)" id="sec7kpi">
<g transform="matrix(1 0 0 1 0 0)" id="sec7kpi-c1">
<ellipse fill="#372356" stroke="#27AE60" stroke-width="16" stroke-miterlimit="10" cx="0" cy="0" rx="71" ry="71" />
<text id="sec7text" x="-33" y="15" fill="#27AE60" font-family="LatoRegular" font-size="38.8363" pointer-events="none">KPI</text>
</g>
<defs>
<animateTransform attributeType="XML"
xlink:href="#sec7kpi-c1"
attributeName="transform"
type="scale"
dur="500ms"
from="1"
to="2"
restart="whenNotActive"
begin="mouseover"
fill="freeze"
id="c-hover"
/>
<animateTransform attributeType="XML"
xlink:href="#sec7kpi-c1"
attributeName="transform"
type="scale"
dur="500ms"
from="2"
to="1"
restart="whenNotActive"
begin="mouseout"
fill="freeze"
id="c-out"
/>
</svg>
Revised "fixed" code with working snippet:
<svg height="300">
<g transform="matrix(1 0 0 1 150 150)" id="sec7kpi">
<g transform="matrix(1 0 0 1 0 0)" id="sec7kpi-c1">
<ellipse fill="#372356" stroke="#27AE60" stroke-width="16" stroke-miterlimit="10" cx="0" cy="0" rx="71" ry="71" />
<text id="sec7text" x="-33" y="15" fill="#27AE60" font-family="LatoRegular" font-size="38.8363" pointer-events="none">KPI</text>
</g>
<defs>
<animateTransform attributeType="XML"
xlink:href="#sec7kpi-c1"
attributeName="transform"
type="scale"
dur="500ms"
from="1"
to="2"
restart="whenNotActive"
begin="mouseover"
fill="freeze"
id="c-hover"
/>
<animateTransform attributeType="XML"
xlink:href="#sec7kpi-c1"
attributeName="transform"
type="scale"
dur="500ms"
to="1"
restart="whenNotActive"
begin="mouseout"
fill="freeze"
id="c-out"
/>
</svg>

Related

How to combine two animateTransformation not silmutaneously but consecutively?

I have the following transformations
<svg height="205" width="365" xmlns="http://www.w3.org/2000/svg"> <!-- Created with Method Draw - http://github.com/duopixel/Method-Draw/ --> <g>
<title></title>
<rect fill="none" height="207" id="canvas_background" width="367" x="-1" y="-1"></rect> </g> <g>
<title></title>
<g id="svg_3"> <path class="hinh1" d="m22,88.5a67.5,67.5 0 0 1 135,0l-135,0z" fill="none" id="svg_1" stroke="#000" stroke-width="1.5" style="fill: rgb(92, 188, 214);stroke-linejoin: round;/* x: 100; *//* y: 100px; */"></path> <g transform="translate(-132.281 0)"><path class="hinh2" d="m207.0027,88.5a67.5,67.5 0 0 1 135,0l-135,0z" fill="none" id="svg_2" stroke="#000" stroke-width="1.5" transform="rotate(180 274.503 54.7665) translate(0 70.8138) translate(0 -82.7702)" style="fill: rgb(214, 92, 188); stroke-linejoin: round;">
</path>
<animateTransform attributeName="transform" attributeType="XML" type="translate" from="0 0" to="0 56" begin="0s" dur="5s" repeatCount="indefinite" id="one">
</animateTransform>
<animateTransform attributeName="transform" attributeType="XML" type="translate" from="0 0" to="-185 0" begin="one.end" dur="5s" repeatCount="indefinite">
</animateTransform>
</g> </g> </g> </svg>
I want the right semicircle to go down THEN go left (not simultaneously) to snap to the left semicircle.
I found that, the following code does not work as I expect, only the second one is triggered. Isn't it the case that one.end has no meaning here?
<animateTransform attributeName="transform" attributeType="XML" type="translate" from="0 0" to="0 56" begin="0s" dur="5s" repeatCount="indefinite" id="one">
</animateTransform>
<animateTransform attributeName="transform" attributeType="XML" type="translate" from="0 0" to="-185 0" begin="one.end" dur="5s" repeatCount="indefinite">
</animateTransform>
In addition, at the end of the animations, I want the right to stop for about 3s before repeating, how to do so?
Instead of using the to and for attributes you can animate between several values by using the values attribute where the values for the transformation are separates by semicolons:
<svg height="205" width="365" xmlns="http://www.w3.org/2000/svg">
<g>
<rect fill="none" height="207" id="canvas_background" width="367" x="-1" y="-1"></rect>
</g>
<g id="svg_3">
<path class="hinh1" d="m22,88.5a67.5,67.5 0 0 1 135,0l-135,0z" fill="none" id="svg_1" stroke="#000" stroke-width="1.5" style="fill: rgb(92, 188, 214);stroke-linejoin: round;"></path>
<g>
<path class="hinh2" d="m207.0027,88.5a67.5,67.5 0 0 1 135,0l-135,0z" fill="none" id="svg_2" stroke="#000" stroke-width="1.5" transform="rotate(180 274.503 54.7665) translate(0 70.8138) translate(0 -82.7702)" style="fill: rgb(214, 92, 188); stroke-linejoin: round;">
</path>
<animateTransform attributeName="transform" attributeType="XML" type="translate" values="0 0;0 56;-185,56" begin="0s" dur="5s" repeatCount="1" fill="freeze" id="one">
</animateTransform>
</g>
</g>
</svg>

SVG animate(move) text from point A to B while its content is changing

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.

Will hidden / obscured SVG animations still cause browser repaints and or performance issues?

I'm considering putting an SVG spinner animation behind some images I'm loading and then having the image stacked on top of the spinner so that the spinner is obscured when the image is loaded. Planning to apply this to a list view with potentially hundreds of items.
First question is, will the obscured SVG spinner (once its respective image loads) continue to cause the browser to repaint? (that sounds expensive)
If yes, the next question would be, if I hide (display: none) the spinner when the image loads, will the hidden spinner continue to cause repainting?
Any other performance thoughts are very welcome.
FWIW, this is an Electron app, so Chromium (a relatively up to date version) is the only browser we're concerned with.
This got me interested... using an example SVG I whipped together this test (excuse how scrummy the code is):
document.onclick = function() {
output.innerHTML += (mySVG.innerHTML + output.innerHTML).replace(/<circle /g,"<circle style='opacity: 0' ");
preview.innerHTML += mySVG.innerHTML;
}
<div id="mySVG">
<svg width="30px" height="30px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-dual-ring">
<circle cx="50" cy="50" fill="none" stroke-linecap="round" r="40" stroke-width="4" stroke="#facd9e" stroke-dasharray="62.83185307179586 62.83185307179586" transform="rotate(115.488 50 50)">
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
</circle>
<circle cx="50" cy="50" fill="none" stroke-linecap="round" r="35" stroke-width="4" stroke="#389798" stroke-dasharray="54.97787143782138 54.97787143782138" stroke-dashoffset="54.97787143782138" transform="rotate(-115.488 50 50)">
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;-360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
</circle>
</svg>
</div>
<div id="output"></div>
<div id="preview"></div>
As you can see, when you click lots on the document, all the fully transparent spinning circles start to slow down the browser rendering. Looks like the animations do still trigger.
In this example with display: none; we get the same result, the div#preview is noticeably slower:
document.onclick = function() {
output.innerHTML += (mySVG.innerHTML + output.innerHTML).replace(/<circle /g,"<circle style='display: none' ");
preview.innerHTML += mySVG.innerHTML;
}
<div id="mySVG">
<svg width="30px" height="30px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" preserveAspectRatio="xMidYMid" class="lds-dual-ring">
<circle cx="50" cy="50" fill="none" stroke-linecap="round" r="40" stroke-width="4" stroke="#facd9e" stroke-dasharray="62.83185307179586 62.83185307179586" transform="rotate(115.488 50 50)">
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
</circle>
<circle cx="50" cy="50" fill="none" stroke-linecap="round" r="35" stroke-width="4" stroke="#389798" stroke-dasharray="54.97787143782138 54.97787143782138" stroke-dashoffset="54.97787143782138" transform="rotate(-115.488 50 50)">
<animateTransform attributeName="transform" type="rotate" calcMode="linear" values="0 50 50;-360 50 50" keyTimes="0;1" dur="1s" begin="0s" repeatCount="indefinite"></animateTransform>
</circle>
</svg>
</div>
<div id="output"></div>
<div id="preview"></div>

Trying to animate SVG gradient

I am using SVG to try and animate a gradient path - to create a tail / shooting star effect. In doing this, I want to animate an object at the front of the tail, which I have shown in my example below (the circles).
The example works barely in Google Chrome, haven't checked others... You can see I've got 5 circles/paths and only 1 of them is working properly. The curved one animates the gradient at a different speed to the object and the others don't work properly at all except for the "almost-horizontal" one.
Can someone please provide some insight into why this doesn't work, recommend a way I could do this and provide an example if possible?
I'm almost to the point where i'll just write my own render code in canvas and using a JS library... :(
<svg style="height: 400px; width: 100%" viewBox="0 0 500 200">
<path id="circlePath1" stroke-width="2" d="M10 100 Q 100 10, 150 80 T 300 100" stroke="url(#grad)" fill="transparent"></path>
<path id="circlePath2" stroke-width="2" d="M30 20 L 130 19" stroke="url(#grad)" fill="transparent"></path>
<path id="circlePath3" stroke-width="2" d="M30 10 L 130 10" stroke="url(#grad)" fill="transparent"></path>
<path id="circlePath4" stroke-width="2" d="M10 10 L 10 110" stroke="url(#grad)" fill="transparent"></path>
<path id="circlePath5" stroke-width="2" d="M10 20 L 20 110" stroke="url(#grad)" fill="transparent"></path>
<linearGradient id='grad'>
<stop stop-opacity="0" stop-color='#800'>
<animate attributeName="offset" dur="2s" values='-0.20;0.80' repeatCount="indefinite" ></animate>
</stop>
<stop stop-color='#800' stop-opacity=".5">
<animate attributeName="offset" dur="2s" values='-0.02;0.98' repeatCount="indefinite" ></animate>
</stop>
<stop stop-opacity="0.5" stop-color='#800'>
<animate attributeName="offset" dur="2s" values='-0;1' repeatCount="indefinite" ></animate>
</stop>
<stop stop-opacity="0" stop-color='#800'>
<animate attributeName="offset" dur="2s" values='-0;1' repeatCount="indefinite" ></animate>
</stop>
</linearGradient>
<circle id="c1" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath1"></mpath>
</animateMotion>
</circle>
<circle id="c2" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath2"></mpath>
</animateMotion>
</circle>
<circle id="c3" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath3"></mpath>
</animateMotion>
</circle>
<circle id="c4" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath4"></mpath>
</animateMotion>
</circle>
<circle id="c5" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath5"></mpath>
</animateMotion>
</circle>
</svg>
In this attempt I use a radial gradient that is masked by the line to create the trail, then a separate dot.
<svg width="500" height="300" viewBox="0 0 500 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="motion-path" stroke-width="2" d="M202.4 58.3c-13.8.1-33.3.4-44.8 9.2-14 10.7-26.2 29.2-31.9 45.6-7.8 22.2-13.5 48-3.5 70.2 12.8 28.2 47.1 43.6 68.8 63.6 19.6 18.1 43.4 26.1 69.5 29.4 21.7 2.7 43.6 3.3 65.4 4.7 19.4 1.3 33.9-7.7 51.2-15.3 24.4-10.7 38.2-44 40.9-68.9 1.8-16.7 3.4-34.9-10.3-46.5-9.5-8-22.6-8.1-33.2-14.1-13.7-7.7-27.4-17.2-39.7-26.8-5.4-4.2-10.4-8.8-15.8-12.9-4.5-3.5-8.1-8.3-13.2-11-6.2-3.3-14.3-5.4-20.9-8.2-5-2.1-9.5-5.2-14.3-7.6-6.5-3.3-12.1-7.4-19.3-8.9-6-1.2-12.4-1.3-18.6-1.5-10.2-.3-20.2-1.5-30.3-1" stroke="#666" fill="none"/>
<mask id="path-mask">
<use xlink:href="#motion-path" stroke="#666"/>
</mask>
<symbol id="ball">
<circle id="ball" r="2.5" fill="#800">
<animateMotion dur="5s" repeatCount="indefinite">
<mpath xlink:href="#motion-path"/>
</animateMotion>
</circle>
</symbol>
<symbol id="trail">
<circle r="30" fill="url(#grad)">
<animateMotion dur="5s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#motion-path"/>
</animateMotion>
</circle>
</symbol>
<linearGradient id="grad">
<stop offset="0" stop-opacity="0" stop-color="#800"/>
<stop offset=".5" stop-opacity=".8" stop-color="#800"/>
<stop offset=".5" stop-opacity="0" stop-color="#800"/>
</linearGradient>
</defs>
<use xlink:href="#ball"/>
<use xlink:href="#trail" mask="url(#path-mask)"/>
</svg>
It has its limitations (if the path gets too close you get the trail showing on both bits) but hopefully this gives you an idea to play with.

How to get coordinates of element during animation in SVG

I have such code:
<defs>
<g id="g0">
<circle cx="0" cy="1" r="16" fill="blue" id="pr0" stroke-width="0" stroke="#2986dc"/>
<circle cx="0" cy="0" r="14" fill="red" id="pp0" />
</g>
</defs>
<use x="603" y="484" id="u0" href="#g0">
<animateTransform attributeName="transform" attributeType="XML" type="rotate" id="t0" dur="30s" repeatCount="indefinite" from="0 494 494" to="360 494 494"/>
</use>
How can i get coordinates of group during animation using JavaScript?
The SVG 1.2 Micro DOM provides the get*PresentationTrait*() class of methods for accessing animated values, but I don't think they are supported yet by browsers. So maybe the easiest way would be to resort to JavaScript animation.

Categories