SVG animate pattern from top left

Viewed 110

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?

3 Answers

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>

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>

Related