SVG animate pattern from top left - javascript

I got this simple SVG animation that is transforming the pattern from dots to circles on page load.
<svg width="596" height="255">
<pattern id="pattern-circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="6" cy="6" r="3" stroke="red" stroke-width="2" fill="transparent">
<animate attributeName="r" values="0; 5" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
</circle>
</pattern>
<!-- The canvas with our applied pattern -->
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-circles)" />
</svg>
I am pretty new to SVG animation and have a hard time figure out how to achieve my goal, in addition to what I already got (dots to circles), to have this animation start/fade-in from the top left dot and end in the bottom right dot - is this even possible to achieve with this SVG pattern setup? Is there a way to isolate the dots, and stagger them in one-by-one?

Here is one idea where I will generate the circles using JS. On each iteration I increment the delay using i/j to create the to top-left to bottom-right animation. The position is trivial, the cx is based on i and cy on j
var d = 20;
var nx = 596/d; /* number of circles in a row */
var ny = 255/d; /* number of circles in a columns */
let svg = document.querySelector("svg");
for(var i=0;i<nx;i++) {
for(var j=0;j<ny;j++) {
svg.insertAdjacentHTML( 'beforeend','<circle cx="'+(6 + d*i)+'" cy="'+(6 + d*j)+'" r="0" stroke="red" stroke-width="2" fill="transparent"><animate attributeName="r" values="0; 5" dur="1s" begin="'+((i+j)/10)+'s" repeatCount="0" fill="freeze" /></circle>');
}
}
<svg width="596" height="255">
</svg>
To have a to-right animation keep only the i (same logic if you want a to-bottom one by keeping only the j)
var d = 20;
var nx = 596/d; /* number of circles in a row */
var ny = 255/d; /* number of circles in a columns */
let svg = document.querySelector("svg");
for(var i=0;i<nx;i++) {
for(var j=0;j<ny;j++) {
svg.insertAdjacentHTML( 'beforeend','<circle cx="'+(6 + d*i)+'" cy="'+(6 + d*j)+'" r="0" stroke="red" stroke-width="2" fill="transparent"><animate attributeName="r" values="0; 5" dur="1s" begin="'+(i/10)+'s" repeatCount="0" fill="freeze" /></circle>');
}
}
<svg width="596" height="255">
</svg>
An infinite animation:
var d = 20;
var nx = 596/d; /* number of circles in a row */
var ny = 255/d; /* number of circles in a columns */
let svg = document.querySelector("svg");
for(var i=0;i<nx;i++) {
for(var j=0;j<ny;j++) {
svg.insertAdjacentHTML( 'beforeend','<circle cx="'+(6 + d*i)+'" cy="'+(6 + d*j)+'" r="0" stroke="red" stroke-width="2" fill="transparent"><animate attributeName="r" values="0; 5;0" dur="2s" begin="'+((i+j)/20)+'s" repeatCount="indefinite" /></circle>');
}
}
<svg width="596" height="255">
</svg>
And why not from the center:
var d = 20;
var nx = 596/d; /* number of circles in a row */
var ny = 255/d; /* number of circles in a columns */
var ic = nx/2;
var jc = ny/2;
let svg = document.querySelector("svg");
for(var i=0;i<nx;i++) {
for(var j=0;j<ny;j++) {
svg.insertAdjacentHTML( 'beforeend','<circle cx="'+(6 + d*i)+'" cy="'+(6 + d*j)+'" r="0" stroke="red" stroke-width="2" fill="transparent"><animate attributeName="r" values="0; 5;0" dur="2s" begin="'
+( Math.sqrt((ic - i)*(ic - i)+(jc - j)*(jc - j))/20)+'s" repeatCount="indefinite" /></circle>');
}
}
<svg width="596" height="255">
</svg>

Add an animated mask with a linearGradient.
<svg width="596" height="255">
<linearGradient id="prog-mask" x1=0% x2="100%" y1="0%" y2="100%">
<stop offset="0%" stop-color="white" stop-opacity="1" />
<stop offset="5%" stop-color="white" stop-opacity="0">
<animate attributeName="offset" values="0; 1" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
<animate attributeName="stop-opacity" values="0; 1" dur="2s" begin="2s" repeatCount="0" fill="freeze" />
</stop>
<stop offset="100%" stop-color="white" stop-opacity="0" />
</linearGradient>
<mask id="prog-render">
<rect x="0" y="0" width="100%" height="100%" fill="url(#prog-mask)"/>
</mask>
<pattern id="pattern-circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="6" cy="6" r="3" stroke="red" stroke-width="2" fill="transparent">
<animate attributeName="r" values="0; 5" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
</circle>
</pattern>
<!-- The canvas with our applied pattern -->
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-circles)" mask="url(#prog-render)"/>
</svg>

You can fade things in with linearGradients. Here, the linearGradients disappear revealing the pattern below them.
Because we have two linearGradients the animation would look faster in the middle than at the start and end (we're multiplying two opacity numbers) so I'm using keySplines to make the animation faster in the middle and slower at the start and end to counteract that.
<svg width="596" height="255">
<defs>
<pattern id="pattern-circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="6" cy="6" r="3" stroke="red" stroke-width="2" fill="none">
</circle>
</pattern>
<linearGradient id="g1" x1="100%" y1="0" x2="0" y2="0">
<stop offset="0%" stop-color="white">
<animate attributeName="stop-opacity" values="1; 0" calcMode="spline" keyTimes="0;1" keySplines="0.5 0 0.5 1" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
</stop>
<stop offset="100%" stop-color="white" stop-opacity="0" />
</linearGradient>
<linearGradient id="g2" x1="0" y1="100%" x2="0" y2="0">
<stop offset="0%" stop-color="white" stop-opacity="1">
<animate attributeName="stop-opacity" values="1; 0" calcMode="spline" keyTimes="0;1" keySplines="0.5 0 0.5 1" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
</stop>
<stop offset="100%" stop-color="white" stop-opacity="0" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="url(#pattern-circles)" />
<rect width="100%" height="100%" fill="url(#g1)"/>
<rect width="100%" height="100%" fill="url(#g2)"/>
</svg>

Related

svg fill animation with height with javascript

i want this animation gradient with JavaScript which is currently work with CSS. like gradient animation through height in both svg
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="200" height="200">
<linearGradient id="lg" x1="0.5" y1="1" x2="0.5" y2="0">
<stop offset="0%" stop-opacity="1" stop-color="royalblue"/>
<stop offset="40%" stop-opacity="1" stop-color="royalblue">
<animate attributeName="offset" values="0;1;0" repeatCount="indefinite" dur="10s" begin="0s"/>
</stop>
<stop offset="40%" stop-opacity="0" stop-color="royalblue">
<animate attributeName="offset" values="0;1;0" repeatCount="indefinite" dur="10s" begin="0s"/>
</stop>
<stop offset="100%" stop-opacity="0" stop-color="royalblue"/>
</linearGradient>
<circle cx="50" cy="50" r="45" fill="url(#lg)" stroke="crimson" stroke-width="5"/>
</svg>
<svg id="xcustom2" data-fill-gradient="true" fill-gradient-property="gt,0,2000">
<g id="_shape28570" fill=url(#_GradLFF02X02X02X0002X02X02X0NA_) fill-opacity="1" fill-rule="evenodd" stroke="rgb(0,0,0)" stroke-opacity="1" stroke-width="2" stroke-linecap="butt" stroke-linejoin="miter" stroke-miterlimit="10">
<rect x="12" y="5" width="100" height="125"/>
</g>
<defs>
<linearGradient id="_GradLFF02X02X02X0002X02X02X0NA_" x1="0" y1="0" x2="1" y2="0">
<stop offset="0.000" stop-color="rgb(255,255,0)" stop-opacity="1.0"/>
<stop offset="0.500" stop-color="rgb(0,176,80)" stop-opacity="1.0"/>
<stop offset="1.000" stop-color="rgb(255,255,0)" stop-opacity="1.0"/>
</linearGradient>
</defs>
</svg>
You need to use document.createElementNS and it will work:
var svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
svg.setAttribute('viewBox','0 0 100 100');
svg.setAttribute('width',200);
svg.setAttribute('height',200);
svg.setAttribute('xmlns','http://www.w3.org/2000/svg');
var lin = document.createElementNS("http://www.w3.org/2000/svg", 'linearGradient');
lin.setAttribute('x1',0.5);
lin.setAttribute('y1',1);
lin.setAttribute('x2',0.5);
lin.setAttribute('y2',0);
lin.setAttribute('id','lg');
var stop1 = document.createElementNS("http://www.w3.org/2000/svg", 'stop');
stop1.setAttribute('offset',0);
stop1.setAttribute('stop-color','rgb(255,255,0)');
stop1.setAttribute('stop-opacity',1);
var stop2 = document.createElementNS("http://www.w3.org/2000/svg", 'stop');
stop2.setAttribute('offset',0.5);
stop2.setAttribute('stop-color','rgb(0,176,80)');
stop2.setAttribute('stop-opacity',1);
var ani1 = document.createElementNS("http://www.w3.org/2000/svg", 'animate');
ani1.setAttribute('attributeName','offset');
ani1.setAttribute('values','0;1;0');
ani1.setAttribute('repeatCount','indefinite');
ani1.setAttribute('dur','10s');
ani1.setAttribute('begin','0s');
var stop3 = document.createElementNS("http://www.w3.org/2000/svg", 'stop');
stop3.setAttribute('offset',1);
stop3.setAttribute('stop-color','rgb(255,255,255)');
stop3.setAttribute('stop-opacity',1);
var ani2 = document.createElementNS("http://www.w3.org/2000/svg", 'animate');
ani2.setAttribute('attributeName','offset');
ani2.setAttribute('values','0;1;0');
ani2.setAttribute('repeatCount','indefinite');
ani2.setAttribute('dur','10s');
ani2.setAttribute('begin','0s');
stop2.appendChild(ani1);
stop3.appendChild(ani2);
lin.appendChild(stop1);
lin.appendChild(stop2);
lin.appendChild(stop3);
svg.appendChild(lin);
var circ = document.createElementNS("http://www.w3.org/2000/svg", 'circle');
circ.setAttribute('cx',50);
circ.setAttribute('cy',50);
circ.setAttribute('r',45);
circ.setAttribute('fill','url(#lg)');
circ.setAttribute('stroke','crimson');
circ.setAttribute('stroke-width',5);
svg.appendChild(circ);
document.body.appendChild(svg);

How to reset an svg animation to its initial state?

I have an SVG animation similar to this:
function startAnimation() {
document.querySelector('#anim-width').beginElement();
}
<svg width="100" viewBox="0 0 100 100">
<rect id="box" x="10" y="10" fill="red" width="10" height="10">
<animate
id="anim-width"
attributeName="width"
from="10"
to="80"
dur="1s"
fill="freeze"
begin="click"
/>
<animate
attributeName="height"
from="10"
to="80"
begin="anim-width.end + 0s"
dur="1s"
fill="freeze"
/>
</rect>
</svg>
<br/>
<button onclick="startAnimation()">Start</button>
What I want to achieve is the red box starts from 10 by 10, when clicking the button, the width expands from 10 to 80, then the height expands to 80 after the width animation is done.
It works fine for the first playback, but when clicking the button again the height starts from 80 instead of 10, how do I reset everthing to its intial state and replay the entire animation?
I tried adding document.querySelector('#box').setAttribute('height', '10'); in the startAnimation() function but it doesn't seem to work.
I'm adding an element <set> inside the rect and I'm starting the set element inside the function function startAnimation()
The <set> element ìs a maner of setting the value of an attribute (height in this case), like an animation with duration 0.
function startAnimation() {
document.querySelector("#set").beginElement();
document.querySelector("#anim-width").beginElement();
}
<svg width="100" viewBox="0 0 100 100">
<rect id="box" x="10" y="10" fill="red" width="10" height="10">
<animate id="anim-width" attributeName="width" from="10" to="80" dur="1s" fill="freeze" begin="click" />
<animate id="anim-height" attributeName="height" from="10" to="80" begin="anim-width.end + 0s" dur="1s" fill="freeze" />
<set id="set" attributeName="height" to="10"></set>
</rect>
</svg>
<br />
<button onclick="startAnimation()">Start</button>
Another option is just to have an <animate> element that starts the animation by resetting the height.
document.querySelector("#anim-height").addEventListener("endEvent", enableButton);
function startAnimation() {
document.querySelector("#start-btn").disabled = true;
document.querySelector("#anim-start").beginElement();
}
function enableButton()
{
document.querySelector("#start-btn").disabled = false;
}
<svg width="100" viewBox="0 0 100 100">
<rect id="box" x="10" y="10" fill="red" width="10" height="10">
<animate id="anim-start" attributeName="height" to="10" dur="0.01s" fill="freeze" begin="indefinite" />
<animate id="anim-width" attributeName="width" from="10" to="80" dur="1s" fill="freeze" begin="anim-start.end + 0s" />
<animate id="anim-height" attributeName="height" from="10" to="80" begin="anim-width.end + 0s" dur="1s" fill="freeze" />
</rect>
</svg>
<br />
<button id="start-btn" onclick="startAnimation()">Start</button>

How to calculate the position of a circle depending on the current time on the curve

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1158 696">
<defs>
<linearGradient id="grad1" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:red;stop-opacity:1" />
<stop offset="50%" style="stop-color:blue;stop-opacity:1" />
<stop offset="100%" style="stop-color:red;stop-opacity:1" />
</linearGradient>
</defs>
<g><g><g><path fill="none" stroke="url(#grad1)" stroke-miterlimit="50" stroke-width="4" d="M0 212.4c241.4 0 274.174-213.852 579-210 304.826 1.853 345.472 211 581 210"/></g></g></g>
<g>
<g opacity=".76">
<path fill="#011134" d="M.25 214.001c239.653 0 274.65-213.852 580.005-210 305.355 3.853 340.062 210 580.005 210v486H.25v-486z"/>
</g>
</g>
<g transform="translate(0, 198)" >
<filter id="blurMe">
<feGaussianBlur in="SourceGraphic" stdDeviation="2" />
</filter>
<circle filter="url(#blurMe)" cx="16" cy="16" r="12" fill="red" />
</g>
</svg>
I've tried to use a formula of Normal distribution
I need to achive a result where the circle moves along the curve depends on the current time for that i need to find a formula which represents the graph described above, but the Gaussian distribution does not give me a desirable graph, the Gaussian distribution gives me next:

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.

Smoothly change the filling of a SVG

I'm using javascript to change the color of a svg. This changes my <linearGradient> filling :
My problem is, that it is changing very rapidly.
Is there any way to have a "smooth" flow between the colors? I tried to use the jquery anim() method but it wouldn't work because of the SVG-DOM.
Edit: More source code. In the end, it's pretty simple. I get the stop elements of my svg and calculate a new rgb value. I then set the rgb value as the new stop-color of the stop element
js:
var gradient = $('#upper').children('stop');
var firstValue = 'stop-color:rgb('+top[0]+','+top[1]+','+top[2]+')';
var secondValue = 'stop-color:rgb('+bottom[0]+','+bottom[1]+','+bottom[2]+')';
gradient[0].setAttribute('style',firstValue);
gradient[1].setAttribute('style',secondValue);
html
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">
<defs>
<linearGradient id="upper" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="0%" y2="100%">
<stop style="stop-color:rgb(107,186,112);stop-opacity:1" offset="0"/>
<stop style="stop-color:rgb(107,186,112);stop-opacity:1" offset="1"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="1" height="1" fill="url(#upper)" opacity="0.6" />
</svg>
Depending on the actual scenario, this could be solved with pure SMIL animation:
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">
<defs>
<linearGradient id="upper" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="0%" y2="100%">
<stop stop-color="rgb(107,186,112)" offset="0">
<animate attributeType="CSS" attributeName="stop-color" id="animate0"
dur="2s" begin="rect0.click" fill="freeze" to="rgb(0,255,0)"/>
</stop>
<stop stop-color="rgb(107,186,112)" offset="1">
<animate attributeType="CSS" attributeName="stop-color"
dur="2s" begin="animate0.begin" fill="freeze" to="rgb(255,0,0)"/>
</stop>
</linearGradient>
</defs>
<rect x="0" y="0" width="1" height="1" fill="url(#upper)" opacity="0.6" id="rect0"/>
</svg>
I this case the animation is triggered by a click on the rectangle.
Specific colors can also be set by JavaScript and the animation can be triggered by JavaScript:
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">
<defs>
<linearGradient id="upper" gradientUnits="userSpaceOnUse" x1="0%" y1="0%" x2="0%" y2="100%">
<stop stop-color="rgb(107,186,112)" offset="0">
<animate attributeType="CSS" attributeName="stop-color" id="animate0"
dur="2s" begin="indefinite" fill="freeze"/>
</stop>
<stop stop-color="rgb(107,186,112)" offset="1">
<animate attributeType="CSS" attributeName="stop-color"
dur="2s" begin="animate0.begin" fill="freeze"/>
</stop>
</linearGradient>
</defs>
<rect x="0" y="0" width="1" height="1" fill="url(#upper)" opacity="0.6"/>
<script type="text/javascript">
var gradientColors = [[255,0,0],[255,255,0]];
var animateElements = document.getElementsByTagName("animate");
for (var i=0; i<2; i++) {
animateElements[i].setAttribute(
"to",
"rgb("+gradientColors[i].join(",")+")"
);
}
animateElements[0].beginElement();
</script>
</svg>
Whether these solutions are practical for you depends on whether the targeted browsers support SMIL animation.

Categories