Как анимировать SVG с помощью ванильного JS?

83
12 декабря 2021, 23:40

Только начал открывать для себя SVG изучая немногочисленные туториалы.
Разобрался, что существует три способа анимации: SMIL, CSS, JS. Если с первыми двумя всё понятно, то про третий я совершенно не могу найти информацию. Есть целая куча готовых решений-библиотек, но как они работают? Понятно, что можно переключать классы, но я так подозреваю это не всё.

Куда идти? Что прочитать?

Answer 1

Анимация - это движение, вот и подвигайте что-либо в svg в каждом кадре, в зависимости от времени:

let PI2 = Math.PI * 2; 
let bubbles = Array(33).fill(0).map((e, i) => { // создаем 33 частицы 
    let d = Math.random() * PI2; // направление 
    return { 
        dir: d, // текущее направление 
        targetDir: d, // целевое направление 
        k: Math.random(), // коэфициент колебания частицы 
        speed: 0.1 + Math.random(), // скорость 
        size: Math.random() * 5 + 9, // размер частицы 
        x: Math.random() * 560 - 280, // начальные координаты 
        y: Math.random() * 170 - 85   // -- 
    }; 
}); 
 
// добавляем круги к svg по кол-ву частиц 
background.innerHTML = bubbles.map((_, i) => `<circle></circle>`).join(''); 
let c = document.querySelectorAll('circle') // находим их все 
requestAnimationFrame(render); 
 
function render(t) {  // прорисовка кадра, тут t - время с начала анимации   
  requestAnimationFrame(render) 
   
  bubbles.forEach((p, i) => { // для каждой частицы 
   
     if (Math.random() > 0.995) // с очень небольшой вероятностью 
       p.targetDir = Math.random() * PI2; // меняем направление движения 
        
     let da = (p.targetDir - p.dir) % PI2; // считаем новое направление  
     p.dir += (2*da%PI2 - da)*0.1 + Math.sin(t/100+i)*0.05*p.k;// + колебания 
      
     p.x += Math.cos(p.dir) * p.speed; // вычисляем новое положение 
     p.y += Math.sin(p.dir) * p.speed; // с учетом скорости и направления 
      
     if (Math.abs(p.x) > 300) p.x *= -1; // при выходе за границу  
     if (Math.abs(p.y) > 100) p.y *= -1; // переносим на противоположную сторону 
      
     c[i].setAttribute('r', Math.max(0, p.size + 2*Math.sin(t/(444 + i*77)))); 
     c[i].setAttribute('cx', p.x); 
     c[i].setAttribute('cy', p.y); 
  }); 
}
body { 
    background-color: black; 
    margin: 0; 
    overflow: hidden; 
}
<svg viewbox="-280 -85 560 170"> 
    <defs> 
       <filter id="goo"> 
          <feGaussianBlur stdDeviation="5" result="blur" /> 
          <feColorMatrix in="blur" values=" 
            1 0 0 0   0   
            0 1 0 0   0   
            0 0 1 0   0   
            0 0 0 17 -5"  /> 
       </filter> 
    </defs> 
    <g id=background filter=url(#goo) fill=white></g> 
</svg>

Answer 2

Допустим вы нашли или сами написали код анимации SVG и хотите полностью сделать точно такую же анимацию средствами JS

Сравните два примера одной и той же анимации

SVG анимация увеличения радиуса окружности от 2 до 30px

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" xml:space="preserve"> 
 <circle cx='50' cy='50' r='20' fill="purple" id="crc1"> 
    <animate 
      attributeName="r" 
      from="2" 
      to="30" 
      dur="3s" 
      repeatCount="2" 
      fill="freeze" /> 
  </circle>

JS анимация увеличения радиуса окружности от 2 до 30px

var jsAnimation = document.createElementNS('http://www.w3.org/2000/svg', 'animate'); 
 
jsAnimation.setAttribute('attributeName', 'r'); 
jsAnimation.setAttribute('from', '2'); 
jsAnimation.setAttribute('to', '30'); 
jsAnimation.setAttribute('dur', '3s'); 
jsAnimation.setAttribute('repeatCount', '2'); 
jsAnimation.setAttribute('fill', 'freeze'); 
 
document.getElementById('crc1').appendChild(jsAnimation);
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" xml:space="preserve"> 
  <circle cx='50' cy='50' r='20' fill="purple" id="crc1"> 
   
  </circle> 
 
</svg>

Пока практически всё одинаково.

Но у JS намного больше ресурсов сделать анимацию SVG более богатой, чем SMIL.

Например с использованием элементов управления HTML input, button, select и т.д

Note

Но всегда нужно помнить, - чтобы заработала анимация JS, нужно полностью понимать как работает SVG.

Пример анимации заполнения полосы в зависимости от выбранного значения в Input

var slider = document.getElementById("slider"); 
var anim = document.getElementById("anim"); 
 
slider.addEventListener("change", function(evt) { 
 
  var sliderValue = evt.target.value; 
  var currentTo = anim.getAttribute("to"); 
  anim.setAttribute("from", currentTo); 
  anim.setAttribute("to", 400 - sliderValue); 
  anim.beginElement(); 
 
});
<svg width="100" height="400"> 
  <line x1="50" y1="400" x2="50" y2="0" stroke="lightgrey" stroke-width="20"/> 
  <line x1="50" y1="400" x2="50" y2="400" stroke="red" stroke-width="20"> 
    <animate id="anim" 
             attributeName="y2" 
             to="400" 
             dur="1s" 
             fill="freeze"/> 
  </line> 
</svg> 
 
<input id="slider" type="range" min="0" max="400" step="1" value="0"/>

Пример использования триггера JS с переключением id анимаций SVG #open, #close

var wrapper_svg_1 = document.getElementById("wrapper_svg_1"), 
  close = document.getElementById('close'), 
  open = document.getElementById("open"); 
 
let flag = true; 
 
wrapper_svg_1.addEventListener('click', function() { 
  if (flag == true) { 
    close.beginElement(); 
    flag = false; 
  } else { 
    open.beginElement(); 
    flag = true; 
  } 
});
* { 
  margin: 0; 
  padding: 0; 
} 
 
html, 
body { 
  width: 100vw; 
  height: 100vh; 
  background: #D74680; 
  font-size: 20px; 
} 
 
#wrapper { 
  width: 100vw; 
  height: 100vh; 
  background: transparent; 
}
<div id="wrapper"> 
  <svg id="wrapper_svg_1" viewBox="0 0 301 301" width="301" height="301"> 
   
 <path fill="none" id="icon-active" stroke="white" stroke-width="5" d="M100 65, 160 5, 195 40, 135 100, 195 160, 160 195, 100 135, 40 195, 5 160,  65 100, 5 40, 40 5z"> 
  
  <animate id="close" begin="indefinite" fill="freeze" attributeName="d" dur="0.2s"  
     to="M5 5, 195 5, 195 195, 145 195, 145 40, 125 40, 125 195, 75 195, 75 40, 55 40, 55 195, 5 195z"></animate> 
       <animate id="open" begin="indefinite" fill="freeze" attributeName="d" dur="0.2s"  
     to="M100 65, 160 5, 195 40, 135 100, 195 160, 160 195, 100 135, 40 195, 5 160,  65 100, 5 40, 40 5z"></animate> 
</path> 
 </svg> 
 
</div>

Анимация запуска от кнопки - GO остановки - Stop с последующим запуском с того же места, где была остановлена анимация.

 var SVGDocument = null; 
  var SVGRoot = null; 
  var pauseButton = null; 
  var playButton = null; 
  var innerCircle = null; 
  var outerCircle = null; 
  var smallerCircle = null; 
 
  function Init(evt) 
  {   
     SVGDocument = evt.target.ownerDocument; 
     SVGRoot = SVGDocument.getElementById('SVGRoot') 
 
     pauseButton = SVGDocument.getElementById('stop1'); 
     playButton = SVGDocument.getElementById('gO1'); 
     innerCircle = SVGDocument.getElementById('innerCircle');; 
     outerCircle = SVGDocument.getElementById('outerCircle');; 
     smallerCircle = SVGDocument.getElementById('smallerCircle');; 
  }; 
 
  function Pause() 
  {   
     SVGRoot.pauseAnimations(); 
  }; 
 
  function Play() 
  { 
     if(SVGRoot.animationsPaused()){ 
        SVGRoot.unpauseAnimations(); 
     } 
     else{ 
         innerCircle.beginElement(); 
         outerCircle.beginElement(); 
         smallerCircle.beginElement(); 
     } 
  };
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink='http://www.w3.org/1999/xlink' width="400" height="400" viewBox="0 0 400 400" onload='Init(evt)' id = "SVGRoot"> 
  <title>animation planetary mechanism</title> 
  <defs> 
    <marker id="MarkerArrow" viewBox="0 0 20 20" refX="2" refY="5" markerUnits="userSpaceOnUse" orient="auto" markerWidth="20" markerHeight="20"> 
      <rect width="14" height="10" rx="2" fill="#22211D" /> 
    </marker> 
    <line id="line1" x1="150" y1="100" x2="60" y2="100" style=" fill:none; 
				 marker-end: url(#MarkerArrow); 
				 marker-start: url(#MarkerArrow); 
			   stroke:#22211D; stroke-width:6; "> 
    </line> 
    <marker id="MarkerArrow-s" viewBox="0 0 20 20" refX="3" refY="1.7" markerUnits="userSpaceOnUse" orient="auto" markerWidth="20" markerHeight="20"> 
      <rect width="7" height="3.5" rx="2" fill="#22211D" /> 
    </marker> 
    <line id="line-s" x1="175" y1="100" x2="202" y2="100" style=" fill:none; 
			 marker-end: url(#MarkerArrow-s1); 
			 marker-start: url(#MarkerArrow-s); 
		   stroke:#22211D; stroke-width:2; "> 
 
    </line> 
    <linearGradient id="vertical" x2="0%" y2="100%" spreadMethod="pad"> 
      <stop offset="0%" stop-color="powderblue" /> 
      <stop offset="100%" stop-color="lightgreen" /> 
    </linearGradient> 
 
  </defs> 
  <rect width="100%" height="100%" fill="url(#vertical)" /> 
  <g transform="translate(90,50)"> 
 
    <g id="wheel"> 
      <g> 
        <animateTransform attributeName="transform" type="rotate" from="0 100 100" to="360 100 100" dur="14s" id = "innerCircle" begin="click" repeatCount="indefinite" /> 
        <use xlink:href="#line1" transform="rotate(0 100 100)" /> 
        <use xlink:href="#line1" transform="rotate(120 100 100)" /> 
        <use xlink:href="#line1" transform="rotate(240 100 100)" /> 
 
 
        <circle cx="100" cy="100" r="15" style="stroke: #22211D; fill:none;   stroke-width: 4px;" /> 
        <circle cx="100" cy="100" r="50" style="stroke: #22211D; fill:none;   stroke-width: 15px;" /> 
        <circle cx="100" cy="100" r="60" style="stroke: #22211D; fill:none; stroke-dasharray: 5 6;  stroke-width: 10px;" /> 
        <circle cx="150" cy="100" r="3" style="stroke: #22211D; fill:yellow; " /> 
      </g> 
    </g> 
 
    <g id="col-small"> 
 
      <g> 
        <animateTransform attributeName="transform" type="rotate" from="0 188 100" to="-360 188 100" id = "smallerCircle" begin="click"  dur="3.5s" repeatCount="indefinite" /> 
        <use xlink:href="#line-s" transform="rotate(0 188 100)" /> 
        <use xlink:href="#line-s" transform="rotate(120 188 100)" /> 
        <use xlink:href="#line-s" transform="rotate(240 188 100)" /> 
 
        <circle cx="188" cy="100" r="8" style="stroke: #22211D; fill:none;   stroke-width: 4px;" /> 
        <circle cx="188" cy="100" r="18" style="stroke: #22211D; fill:none;   stroke-width: 7px;" /> 
        <circle cx="188" cy="100" r="24" style="stroke: #22211D; fill:none; stroke-dasharray: 5 5;  stroke-width: 10px;" /> 
        <circle cx="206" cy="100" r="3" style="stroke: #22211D; fill:yellow; " /> 
 
      </g> 
    </g> 
     
    <g id="planetar"> 
      <g> 
        <animateTransform attributeName="transform" type="rotate" from="0 100 100" to="-360 100 100" id = "outerCircle" begin="click" dur="28s" repeatCount="indefinite" /> 
 
        <circle cx="100" cy="100" r="116" style="stroke: #22211D; fill:none;   stroke-dasharray: 5 5;  stroke-width: 10px;" /> 
        <circle cx="100" cy="100" r="124" style="stroke: #22211D; fill:none;   stroke-width: 12px;" /> 
        <circle cx="224" cy="100" r="3" style="stroke: #22211D; fill:yellow; " /> 
      </g> 
    </g> 
 
    <g> 
      <use xlink:href="#col-small" transform="rotate(240 100 100)" /> 
      <use xlink:href="#col-small" transform="rotate(120 100 100)" /> 
    </g> 
 
    <g transform="translate(-10,160)"> 
      <g id="gO1" onclick='Play()'> 
        <rect x="45" y="85" height="22" width="60" rx="5" fill="#0080B8" stroke="dodgerblue" /> 
        <text x="62" y="102" font-size="16" fill="yellow">GO</text> 
      </g> 
      <g id="stop1" onclick='Pause()'> 
        <rect x="110" y="85" height="22" width="60" rx="5" fill="crimson" stroke="red" /> 
        <text x="120" y="102" font-size="16" fill="yellow">STOP</text> 
      </g> 
    </g> 
 
  </g> 
</svg>

UPDATE

Если возникнет необходимость сделать морфинг с помощью JS и нескольких path SVG и анимация будет работать не плавно, рывками, то это значит:

У двух или нескольких path SVG не совпадают количество узлов, либо команды С, Q, T кривых Безье не будут одинаковыми в одинаковых по счёту узлах, либо path имеют разрывы, либо контуры не одинарные.

Как избежать этого и добиться плавной анимации морфинга подробно, с картинками :)написано здесь: Плавный морфинг одного path в другой path

Answer 3

Морфинг и несколько вариантов запуска анимации с помощью JS

Для удобства приведу сначала вариант морфинга на pure SMIL, а затем несколько вариантов той же самой анимации с помощью JS

SMIL, запуск анимации по клику на объекте

<svg id="svg1" xmlns="http://www.w3.org/2000/svg" width="200" height="260" viewBox="0 0 75 47">
  <path fill="#5F0C2D" stroke="#5F0C2D"  d="M0.4 5.9C0.4 6.3-0.3 17.8 4.3 31.2 5.3 32.4 6.1 37.9 7.5 43 10.4 53.6 10 58.2 10 58.2 10 58.2 10 59.1 10 62.2L10 63.4 15 65.7C20.1 67.8 34 67.6 38.6 65.7L44 64 44 62.2C44 60.3 44 61.2 44 59.6 44 59.6 44 58.2 44 58.2 44 58.2 44 56 44 56 44 56.1 44.2 52.2 44.2 52.2 44.2 52.2 44.5 49.3 44.5 49.3 44 47 46.8 39.1 47.6 38.8 48.4 38.6 52.8 37.6 55.4 37 62.3 36.7 68.5 30.5 68.5 26.9 68.5 25.1 68.5 20.3 66.1 17.8 63.5 14.8 60.4 14.1 56.7 14.2 54.2 14.3 54.2 12.8 54 12.6 53.8 12.5 54 12.6 54 10.8L53.3 6.7 51.3 4.6C49.8 3.6 47.5 3 44.7 2.2 38.3 0.5 20.1 0 12.7 1.4 7 2.4 0.4 4.7 0.4 5.9ZM64.8 22.9C64.8 25.9 65.5 27.9 60.6 31.8 59.6 32.6 64 30.3 62.9 30.8 60.9 31.9 53.6 32.7 52.4 31.8 51.6 31.1 51.1 29.8 51 28 51.3 24.6 52.1 22.1 53.6 20.4 57.4 16 64.8 19.2 64.8 22.9Z" fill-rule="nonzero" >
  <animate attributeName="d"
  begin="svg1.click"
  dur="3s"
  values="
  M0.4 5.9C0.4 6.3-0.3 17.8 4.3 31.2 5.3 32.4 6.1 37.9 7.5 43 10.4 53.6 10 58.2 10 58.2 10 58.2 10 59.1 10 62.2L10 63.4 15 65.7C20.1 67.8 34 67.6 38.6 65.7L44 64 44 62.2C44 60.3 44 61.2 44 59.6 44 59.6 44 58.2 44 58.2 44 58.2 44 56 44 56 44 56.1 44.2 52.2 44.2 52.2 44.2 52.2 44.5 49.3 44.5 49.3 44 47 46.8 39.1 47.6 38.8 48.4 38.6 52.8 37.6 55.4 37 62.3 36.7 68.5 30.5 68.5 26.9 68.5 25.1 68.5 20.3 66.1 17.8 63.5 14.8 60.4 14.1 56.7 14.2 54.2 14.3 54.2 12.8 54 12.6 53.8 12.5 54 12.6 54 10.8L53.3 6.7 51.3 4.6C49.8 3.6 47.5 3 44.7 2.2 38.3 0.5 20.1 0 12.7 1.4 7 2.4 0.4 4.7 0.4 5.9ZM64.8 22.9C64.8 25.9 65.5 27.9 60.6 31.8 59.6 32.6 64 30.3 62.9 30.8 60.9 31.9 53.6 32.7 52.4 31.8 51.6 31.1 51.1 29.8 51 28 51.3 24.6 52.1 22.1 53.6 20.4 57.4 16 64.8 19.2 64.8 22.9Z;
 
 M0 6.8C0 7.2 0.9 8.5 1.9 9.6 2.9 10.8 4.9 15.9 6.3 21 9.2 31.6 12.4 36.5 17.5 38.2 21.6 39.6 21.7 39.8 18.8 42.2L16.5 44 19.5 45.3C24.6 47.4 37 47.6 41.6 45.7L45.5 44.1 43.1 42.1C41 40.4 40.9 39.9 42.1 39.1 42.8 38.6 44.2 38.2 45.1 38.2 47 38.2 52.4 33.5 53.2 31.2 53.6 29.9 53.9 29.9 55.3 31 57.5 32.9 59.1 31.6 58.4 28.6 58 27 58.3 26.1 59.1 25.8 59.9 25.6 62.6 24.8 65.2 24.2 70.7 22.7 75 19 75 15.5 75 13.7 74.1 12.7 71.3 11.6 68 10.2 67.3 10.2 63.7 11.6 61.4 12.5 59.5 13 59.3 12.8 59.1 12.7 59.9 11.3 61 9.9L63.1 7.3 60.5 5.6C59 4.6 55.5 3.2 52.7 2.4 46.3 0.7 21.5-0.1 14.1 1.3 8.4 2.3 0 5.6 0 6.8ZM70 15.6C70 18.6 65.7 22.2 62.3 22.2 61 22.2 59 22.7 57.9 23.2 55.9 24.3 55.5 23.6 56.6 20.8 56.9 19.9 57.6 19.5 58.1 19.8 58.6 20.1 60.5 18.7 62.2 16.8 66 12.4 70 11.9 70 15.6Z"
  fill="freeze"
  restart="whenNotActive" />
 </path> 
</svg>

Как видите, сделать анимацию морфинга элемента SVG не трудно.

<animate attributeName="d" values="Начальный `path`; Финальный `path`" begin="svg1.click" dur="3s" />

Основная трудность выполнить требования идентичности по параметрам начального и финального path

Запуск анимации JS по клику на объекте

var animation = document.getElementById("morphing");
 document.querySelector('#svg1').addEventListener('click', e => {
  animation.beginElement();
 })

<svg id="svg1" xmlns="http://www.w3.org/2000/svg" width="200" height="260" viewBox="0 0 75 47">
  <path fill="#5F0C2D" stroke="#5F0C2D"  d="M0.4 5.9C0.4 6.3-0.3 17.8 4.3 31.2 5.3 32.4 6.1 37.9 7.5 43 10.4 53.6 10 58.2 10 58.2 10 58.2 10 59.1 10 62.2L10 63.4 15 65.7C20.1 67.8 34 67.6 38.6 65.7L44 64 44 62.2C44 60.3 44 61.2 44 59.6 44 59.6 44 58.2 44 58.2 44 58.2 44 56 44 56 44 56.1 44.2 52.2 44.2 52.2 44.2 52.2 44.5 49.3 44.5 49.3 44 47 46.8 39.1 47.6 38.8 48.4 38.6 52.8 37.6 55.4 37 62.3 36.7 68.5 30.5 68.5 26.9 68.5 25.1 68.5 20.3 66.1 17.8 63.5 14.8 60.4 14.1 56.7 14.2 54.2 14.3 54.2 12.8 54 12.6 53.8 12.5 54 12.6 54 10.8L53.3 6.7 51.3 4.6C49.8 3.6 47.5 3 44.7 2.2 38.3 0.5 20.1 0 12.7 1.4 7 2.4 0.4 4.7 0.4 5.9ZM64.8 22.9C64.8 25.9 65.5 27.9 60.6 31.8 59.6 32.6 64 30.3 62.9 30.8 60.9 31.9 53.6 32.7 52.4 31.8 51.6 31.1 51.1 29.8 51 28 51.3 24.6 52.1 22.1 53.6 20.4 57.4 16 64.8 19.2 64.8 22.9Z" fill-rule="nonzero" >
  <animate id="morphing" attributeName="d"
  begin="indefinite"
  dur="4s"
  values="
  M0.4 5.9C0.4 6.3-0.3 17.8 4.3 31.2 5.3 32.4 6.1 37.9 7.5 43 10.4 53.6 10 58.2 10 58.2 10 58.2 10 59.1 10 62.2L10 63.4 15 65.7C20.1 67.8 34 67.6 38.6 65.7L44 64 44 62.2C44 60.3 44 61.2 44 59.6 44 59.6 44 58.2 44 58.2 44 58.2 44 56 44 56 44 56.1 44.2 52.2 44.2 52.2 44.2 52.2 44.5 49.3 44.5 49.3 44 47 46.8 39.1 47.6 38.8 48.4 38.6 52.8 37.6 55.4 37 62.3 36.7 68.5 30.5 68.5 26.9 68.5 25.1 68.5 20.3 66.1 17.8 63.5 14.8 60.4 14.1 56.7 14.2 54.2 14.3 54.2 12.8 54 12.6 53.8 12.5 54 12.6 54 10.8L53.3 6.7 51.3 4.6C49.8 3.6 47.5 3 44.7 2.2 38.3 0.5 20.1 0 12.7 1.4 7 2.4 0.4 4.7 0.4 5.9ZM64.8 22.9C64.8 25.9 65.5 27.9 60.6 31.8 59.6 32.6 64 30.3 62.9 30.8 60.9 31.9 53.6 32.7 52.4 31.8 51.6 31.1 51.1 29.8 51 28 51.3 24.6 52.1 22.1 53.6 20.4 57.4 16 64.8 19.2 64.8 22.9Z;
 
 M0 6.8C0 7.2 0.9 8.5 1.9 9.6 2.9 10.8 4.9 15.9 6.3 21 9.2 31.6 12.4 36.5 17.5 38.2 21.6 39.6 21.7 39.8 18.8 42.2L16.5 44 19.5 45.3C24.6 47.4 37 47.6 41.6 45.7L45.5 44.1 43.1 42.1C41 40.4 40.9 39.9 42.1 39.1 42.8 38.6 44.2 38.2 45.1 38.2 47 38.2 52.4 33.5 53.2 31.2 53.6 29.9 53.9 29.9 55.3 31 57.5 32.9 59.1 31.6 58.4 28.6 58 27 58.3 26.1 59.1 25.8 59.9 25.6 62.6 24.8 65.2 24.2 70.7 22.7 75 19 75 15.5 75 13.7 74.1 12.7 71.3 11.6 68 10.2 67.3 10.2 63.7 11.6 61.4 12.5 59.5 13 59.3 12.8 59.1 12.7 59.9 11.3 61 9.9L63.1 7.3 60.5 5.6C59 4.6 55.5 3.2 52.7 2.4 46.3 0.7 21.5-0.1 14.1 1.3 8.4 2.3 0 5.6 0 6.8ZM70 15.6C70 18.6 65.7 22.2 62.3 22.2 61 22.2 59 22.7 57.9 23.2 55.9 24.3 55.5 23.6 56.6 20.8 56.9 19.9 57.6 19.5 58.1 19.8 58.6 20.1 60.5 18.7 62.2 16.8 66 12.4 70 11.9 70 15.6Z
  "
  fill="freeze"
  restart="whenNotActive" />
 </path> 
 </svg>
<script>            
 var animation = document.getElementById("morphing");
 document.querySelector('#svg1').addEventListener('click', e => {
  animation.beginElement();
 })
 </script>

Запуск анимации JS по клику на кнопке

<button id ="button" onclick="showSVG()">Morphing</button> 
var animation = document.getElementById("morphing")
function showSVG(){
     animation.beginElement();
}  

<button id ="button" onclick="showSVG()">Morphing</button> 
<svg id="svg1" xmlns="http://www.w3.org/2000/svg" width="200" height="260" viewBox="0 0 75 47">
  <path fill="#5F0C2D" stroke="#5F0C2D"  d="M0.4 5.9C0.4 6.3-0.3 17.8 4.3 31.2 5.3 32.4 6.1 37.9 7.5 43 10.4 53.6 10 58.2 10 58.2 10 58.2 10 59.1 10 62.2L10 63.4 15 65.7C20.1 67.8 34 67.6 38.6 65.7L44 64 44 62.2C44 60.3 44 61.2 44 59.6 44 59.6 44 58.2 44 58.2 44 58.2 44 56 44 56 44 56.1 44.2 52.2 44.2 52.2 44.2 52.2 44.5 49.3 44.5 49.3 44 47 46.8 39.1 47.6 38.8 48.4 38.6 52.8 37.6 55.4 37 62.3 36.7 68.5 30.5 68.5 26.9 68.5 25.1 68.5 20.3 66.1 17.8 63.5 14.8 60.4 14.1 56.7 14.2 54.2 14.3 54.2 12.8 54 12.6 53.8 12.5 54 12.6 54 10.8L53.3 6.7 51.3 4.6C49.8 3.6 47.5 3 44.7 2.2 38.3 0.5 20.1 0 12.7 1.4 7 2.4 0.4 4.7 0.4 5.9ZM64.8 22.9C64.8 25.9 65.5 27.9 60.6 31.8 59.6 32.6 64 30.3 62.9 30.8 60.9 31.9 53.6 32.7 52.4 31.8 51.6 31.1 51.1 29.8 51 28 51.3 24.6 52.1 22.1 53.6 20.4 57.4 16 64.8 19.2 64.8 22.9Z" fill-rule="nonzero" >
  <animate id="morphing" attributeName="d"
  begin="indefinite"
  dur="4s"
  values="
  M0.4 5.9C0.4 6.3-0.3 17.8 4.3 31.2 5.3 32.4 6.1 37.9 7.5 43 10.4 53.6 10 58.2 10 58.2 10 58.2 10 59.1 10 62.2L10 63.4 15 65.7C20.1 67.8 34 67.6 38.6 65.7L44 64 44 62.2C44 60.3 44 61.2 44 59.6 44 59.6 44 58.2 44 58.2 44 58.2 44 56 44 56 44 56.1 44.2 52.2 44.2 52.2 44.2 52.2 44.5 49.3 44.5 49.3 44 47 46.8 39.1 47.6 38.8 48.4 38.6 52.8 37.6 55.4 37 62.3 36.7 68.5 30.5 68.5 26.9 68.5 25.1 68.5 20.3 66.1 17.8 63.5 14.8 60.4 14.1 56.7 14.2 54.2 14.3 54.2 12.8 54 12.6 53.8 12.5 54 12.6 54 10.8L53.3 6.7 51.3 4.6C49.8 3.6 47.5 3 44.7 2.2 38.3 0.5 20.1 0 12.7 1.4 7 2.4 0.4 4.7 0.4 5.9ZM64.8 22.9C64.8 25.9 65.5 27.9 60.6 31.8 59.6 32.6 64 30.3 62.9 30.8 60.9 31.9 53.6 32.7 52.4 31.8 51.6 31.1 51.1 29.8 51 28 51.3 24.6 52.1 22.1 53.6 20.4 57.4 16 64.8 19.2 64.8 22.9Z;
 
 M0 6.8C0 7.2 0.9 8.5 1.9 9.6 2.9 10.8 4.9 15.9 6.3 21 9.2 31.6 12.4 36.5 17.5 38.2 21.6 39.6 21.7 39.8 18.8 42.2L16.5 44 19.5 45.3C24.6 47.4 37 47.6 41.6 45.7L45.5 44.1 43.1 42.1C41 40.4 40.9 39.9 42.1 39.1 42.8 38.6 44.2 38.2 45.1 38.2 47 38.2 52.4 33.5 53.2 31.2 53.6 29.9 53.9 29.9 55.3 31 57.5 32.9 59.1 31.6 58.4 28.6 58 27 58.3 26.1 59.1 25.8 59.9 25.6 62.6 24.8 65.2 24.2 70.7 22.7 75 19 75 15.5 75 13.7 74.1 12.7 71.3 11.6 68 10.2 67.3 10.2 63.7 11.6 61.4 12.5 59.5 13 59.3 12.8 59.1 12.7 59.9 11.3 61 9.9L63.1 7.3 60.5 5.6C59 4.6 55.5 3.2 52.7 2.4 46.3 0.7 21.5-0.1 14.1 1.3 8.4 2.3 0 5.6 0 6.8ZM70 15.6C70 18.6 65.7 22.2 62.3 22.2 61 22.2 59 22.7 57.9 23.2 55.9 24.3 55.5 23.6 56.6 20.8 56.9 19.9 57.6 19.5 58.1 19.8 58.6 20.1 60.5 18.7 62.2 16.8 66 12.4 70 11.9 70 15.6Z
  "
  fill="freeze"
  restart="whenNotActive" />
 </path>
</svg> 
 <script>
var animation = document.getElementById("morphing")
function showSVG(){
     animation.beginElement();
}
</script>           
 

Анимация с помощью библиотеки SnapJS

var svg = document.getElementById("svg1");
var s = Snap(svg);
var simpleCup = Snap.select('#coffee-cup');
var fancyCup = Snap.select('#fancy-cup');
var simpleCupPoints = simpleCup.node.getAttribute('d');
var fancyCupPoints = fancyCup.node.getAttribute('d');

var toFancy = function(){
  simpleCup.animate({ d: fancyCupPoints }, 1000, mina.backout, toSimple);  
}
var toSimple = function(){
  simpleCup.animate({ d: simpleCupPoints }, 1000, mina.backout, toFancy); 
}

toSimple();

<h1 style="color:#5F0C2D">Наслаждайтесь своим кофе с SVG +snap.js;) </h1>
  <svg id="svg1" width="100" height="130" viewBox="0 0 75 47" xmlns="http://www.w3.org/2000/svg">
    <path id="coffee-cup" fill="#5F0C2D" stroke="#5F0C2D"  d="M0.4 5.9C0.4 6.3-0.3 17.8 4.3 31.2 5.3 32.4 6.1 37.9 7.5 43 10.4 53.6 10 58.2 10 58.2 10 58.2 10 59.1 10 62.2L10 63.4 15 65.7C20.1 67.8 34 67.6 38.6 65.7L44 64 44 62.2C44 60.3 44 61.2 44 59.6 44 59.6 44 58.2 44 58.2 44 58.2 44 56 44 56 44 56.1 44.2 52.2 44.2 52.2 44.2 52.2 44.5 49.3 44.5 49.3 44 47 46.8 39.1 47.6 38.8 48.4 38.6 52.8 37.6 55.4 37 62.3 36.7 68.5 30.5 68.5 26.9 68.5 25.1 68.5 20.3 66.1 17.8 63.5 14.8 60.4 14.1 56.7 14.2 54.2 14.3 54.2 12.8 54 12.6 53.8 12.5 54 12.6 54 10.8L53.3 6.7 51.3 4.6C49.8 3.6 47.5 3 44.7 2.2 38.3 0.5 20.1 0 12.7 1.4 7 2.4 0.4 4.7 0.4 5.9ZM64.8 22.9C64.8 25.9 65.5 27.9 60.6 31.8 59.6 32.6 64 30.3 62.9 30.8 60.9 31.9 53.6 32.7 52.4 31.8 51.6 31.1 51.1 29.8 51 28 51.3 24.6 52.1 22.1 53.6 20.4 57.4 16 64.8 19.2 64.8 22.9Z"  />
    <path opacity="0"  id="fancy-cup" fill="#5F0C2D" stroke="#5F0C2D"  d="M0 6.8C0 7.2 0.9 8.5 1.9 9.6 2.9 10.8 4.9 15.9 6.3 21 9.2 31.6 12.4 36.5 17.5 38.2 21.6 39.6 21.7 39.8 18.8 42.2L16.5 44 19.5 45.3C24.6 47.4 37 47.6 41.6 45.7L45.5 44.1 43.1 42.1C41 40.4 40.9 39.9 42.1 39.1 42.8 38.6 44.2 38.2 45.1 38.2 47 38.2 52.4 33.5 53.2 31.2 53.6 29.9 53.9 29.9 55.3 31 57.5 32.9 59.1 31.6 58.4 28.6 58 27 58.3 26.1 59.1 25.8 59.9 25.6 62.6 24.8 65.2 24.2 70.7 22.7 75 19 75 15.5 75 13.7 74.1 12.7 71.3 11.6 68 10.2 67.3 10.2 63.7 11.6 61.4 12.5 59.5 13 59.3 12.8 59.1 12.7 59.9 11.3 61 9.9L63.1 7.3 60.5 5.6C59 4.6 55.5 3.2 52.7 2.4 46.3 0.7 21.5-0.1 14.1 1.3 8.4 2.3 0 5.6 0 6.8ZM70 15.6C70 18.6 65.7 22.2 62.3 22.2 61 22.2 59 22.7 57.9 23.2 55.9 24.3 55.5 23.6 56.6 20.8 56.9 19.9 57.6 19.5 58.1 19.8 58.6 20.1 60.5 18.7 62.2 16.8 66 12.4 70 11.9 70 15.6Z"  />
</svg>
  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/snap.svg/0.5.1/snap.svg-min.js"></script>
  
  <script> 
var svg = document.getElementById("svg1");
var s = Snap(svg);
var simpleCup = Snap.select('#coffee-cup');
var fancyCup = Snap.select('#fancy-cup');
var simpleCupPoints = simpleCup.node.getAttribute('d');
var fancyCupPoints = fancyCup.node.getAttribute('d');

var toFancy = function(){
  simpleCup.animate({ d: fancyCupPoints }, 1000, mina.backout, toSimple);  
}
var toSimple = function(){
  simpleCup.animate({ d: simpleCupPoints }, 1000, mina.backout, toFancy); 
}

toSimple();
  </script>

READ ALSO
Группировка json

Группировка json

Не могли бы подсказатьУ меня несколько вопросов

115
Хранение секретных ключей в JS

Хранение секретных ключей в JS

Есть проблема: необходимо держать API ключ в Front-End JS, чтобы через код элемента никто его не узнал бы его, как это реализовать? Backend на Express, сам...

65
ReWrite в htaccess

ReWrite в htaccess

у меня формируется адрес siteru/lk/confirmation

95
Есть ли метод создания статического веб сайта, в котором полностью отсувтсвуют расширения файлов .html в ссылках?

Есть ли метод создания статического веб сайта, в котором полностью отсувтсвуют расширения файлов .html в ссылках?

Есть сайт компании с движком на MODxПланирую избавиться от движка и сделать из него полностью статический сайт

196