Зацикленная анимация Shape 8 только с SVG ‹animate›

У меня большие головные боли, пытаясь найти золотую середину для достижения анимации формы "8", используя комбинацию только <animate attributeName="cx"> и <attributeName="cy">.
Я хотел бы сделать это, используя это, так как это судя по моим показателям, он самый производительный с точки зрения использования FPS, CPU и GPU.

Краткая демонстрация «идеального» пути движения: https://codepen.io/ivancis/pen/eYmZowz< /а>


person ivancis    schedule 16.01.2020    source источник
comment
Добро пожаловать в Stack Overflow! Вопросы, требующие справки по коду, должны включать кратчайший код, необходимый для его воспроизведения, в самом вопросе, предпочтительно в фрагменте стека. Хотя вы предоставили ссылку, если она станет недействительной, ваш вопрос не будет иметь значения для других будущих пользователей SO с той же проблемой. См. Что-то на моем веб-сайте ИЛИ в другом примере не работает, могу ли я просто вставить ссылку.   -  person Paulie_D    schedule 16.01.2020


Ответы (3)


Новое решение, вызванное уточнениями в комментариях

Мне нужно анимировать элемент svg, используя только комбинацию <animate attributeName="cx"> <animate attributeName="cy"> (для производительности), чтобы сделать движение формы «8», зацикленное

Поскольку автор не хочет использовать команду animateMotion, в данном случае я вижу только один способ реализовать анимацию движения круга по символу бесконечности:

Необходимо последовательно выделить множество точек по символу бесконечности и присвоить их координаты окружности cx = "x", cy = "y"

Чем больше точек вы выберете, тем ближе будет траектория движения круга по символу бесконечности

введите здесь описание изображения

В векторном редакторе я последовательно поставил кружочки на символ бесконечности и записал их координаты центра круга. Первая окружность имеет координаты центра cx ="70" cy ="60"
Итак, это было сделано для всех окружностей, расположенных вдоль символа бесконечности. Последний круг имеет те же координаты, что и первый, тем самым реализуя замкнутый цикл.

Осталось только подставить эти значения в формулы анимации cx, cy

Анимация кругового движения cx, cy с радиусом r="5

 <div class="group">
  
  <svg class="ball" xmlns="http://www.w3.org/2000/svg" width="50%" height="50%" viewBox="0 0 120 120">    
  
    
    <circle fill="olive" cx="70" cy="60" r="5">
      <animate
              attributeName="cx"
              attributeType="XML"
              repeatCount="indefinite"
              begin="0s"
              dur="2s"
              values="70;65;60;55;50;45;40.5;40.5;42.5;45.1;48.7;52;55;58;60;61;61;61;61;61;61;62.9;66;69;
			  73;76;79;81;80;78;74;70">
              
        </animate>
		  <animate
				  attributeName="cy"
				  attributeType="XML"
				  repeatCount="indefinite"
				  begin="0"
				  dur="2s"
				  values="60;60;60;60;60;58.3;52.5;47.9;44.4;41.8;40.3;40;41;43;47;51;55;60;65;70;74;77;79;
				  80;80;79;76;72;67;64;61;60">							 
		  </animate>   
	</circle>
	  	    
	   <path fill="none" stroke="black" stroke-dasharray="2" d="M70.5,60.5c5.5,0,10,4.5,10,10s-4.5,10-10,10s-10-4.5-10-10v-20c0-5.5-4.5-10-10-10s-10,4.5-10,10 s4.5,10,10,10H70.5z"/>
  </svg>
</div>
  

Радиус r = 40 как в примере автора вопроса

 <div class="group">
  
  <svg class="ball" xmlns="http://www.w3.org/2000/svg" width="50%" height="50%" viewBox="0 0 120 120">    
  
    
    <circle fill="olive" cx="70" cy="60" r="40">
      <animate
              attributeName="cx"
              attributeType="XML"
              repeatCount="indefinite"
              begin="0s"
              dur="2s"
              values="70;65;60;55;50;45;40.5;40.5;42.5;45.1;48.7;52;55;58;60;61;61;61;61;61;61;62.9;66;69;
			  73;76;79;81;80;78;74;70">
              
        </animate>
		  <animate
				  attributeName="cy"
				  attributeType="XML"
				  repeatCount="indefinite"
				  begin="0"
				  dur="2s"
				  values="60;60;60;60;60;58.3;52.5;47.9;44.4;41.8;40.3;40;41;43;47;51;55;60;65;70;74;77;79;
				  80;80;79;76;72;67;64;61;60">							 
		  </animate>   
	</circle>
	  	    
	   <path fill="none" stroke="black" stroke-dasharray="2" d="M70.5,60.5c5.5,0,10,4.5,10,10s-4.5,10-10,10s-10-4.5-10-10v-20c0-5.5-4.5-10-10-10s-10,4.5-10,10 s4.5,10,10,10H70.5z"/>
  </svg>
</div>
  

person Alexandr_TT    schedule 21.01.2020
comment
Ваш ответ ниже потрясающий @Alexandr_TT, особенно цветные буквы в кругах. Кроме того, этот ответ здесь (с Inkscape) дал мне представление о том, как решить аналогичную проблему, с которой я столкнулся, без Inkscape. Хотелось бы услышать, что вы думаете, и надеюсь, что мой ответ может также помочь другим, кто попадает на эту страницу. - person Alex L; 17.02.2020
comment
@Alex L Я думаю, что решение одной и той же проблемы разными методами очень продуктивно и полезно. Когда я отвечал на этот вопрос, меня очень удивило желание автора не использовать animateMotion в принципе. Мой ответ и ваш ответ дублируют работу стандартной SVG-команды animateMotion, которая сама вычисляет координаты и перемещает объект по траектории движения. Есть ли практическая польза от наших ответов? Это большой вопрос. Люди, вероятно, не захотят знать подробности. - person Alexandr_TT; 17.02.2020
comment
@AlexL И они будут продолжать использовать стандартный вариант решения таких задач - animateMotion В целом, вы очень хорошо поработали Ваш метод, безусловно, может быть использован в таких задачах, где механическое устройство сначала учится на пройденный путь, запоминает координаты и потом может автоматически повторить их самостоятельно - person Alexandr_TT; 17.02.2020
comment
Спасибо за ответ! Полностью согласен. Я никогда не слышал, что <animateMotion/> неэффективен, но я ответил на один вариант использования, похожий на OP, чтобы анимировать только один конец строки в соответствии с путем (и оставить другой конец привязанным), и для этого я использовал аналогичный метод (stackoverflow.com/a/60024858/9792594). Хотя я согласен, варианты использования ограничены, и <animateMotion/> и <animate/> решают большинство проблем при использовании по назначению :) - person Alex L; 17.02.2020

Вы не сказали, какую анимацию вы действительно хотите.

Поэтому я буду предлагать примеры разных видов анимации, а вы можете выбрать любые из них и как-то модифицировать под себя.

Анимация движения символа бесконечности

<svg xmlns="http://www.w3.org/2000/svg"  width="400" height="100" viewBox="0 0 100 100">
  
  <path fill="none" stroke="dodgerblue" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="#d3d3d3" stroke-width="10">  
        <animateTransform
          attributeName="transform"
          type="translate"
          values="0; 150; 0"
          begin="0s"
          dur="4s"
          repeatCount="indefinite" /> 
	</path> 
</svg>	

Вращение

Наведите указатель мыши на символ

.infinity1{
transform-box: fill-box;
 transform-origin: center center;
 transition: rotate 2s linear ;
}
.infinity1:hover {
animation: spin 2s linear infinite;
}

@keyframes spin {
100% {transform :rotate(360deg);}

}
<svg xmlns="http://www.w3.org/2000/svg"  width="100" height="100" viewBox="0 0 100 100">
  
  <path class="infinity1" fill="none" stroke="dodgerblue" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="#d3d3d3" stroke-width="10"  />  
        
	
</svg>

Вращение вокруг оси Y

.infinity1{
transform-box: fill-box;
 transform-origin: center center;
 transition: rotate 2s linear ;
 fill:transparent;
}
.infinity1:hover {
animation: spin 2s linear infinite;
}

@keyframes spin {
100% {transform :rotateY(360deg);}

}
<svg xmlns="http://www.w3.org/2000/svg"  width="100" height="100" viewBox="0 0 100 100" >
  
  <path class="infinity1"  stroke="dodgerblue" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="#d3d3d3" stroke-width="10"  />  
        
	
</svg>	

Анимация заполнения символа бесконечности путем изменения атрибута stroke-dasharray

Нажмите на цветные буквы в кругах

.container {
width:40%;
height="40%";  
  
  background:black;
}
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg"  viewBox="0 20 100 100">
  
  <path fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="#d3d3d3" stroke-width="10"  />  
       <!-- The midpoint of the beginning of the animation in the center of the figure. stroke-dashoffset="31.1" -->
	<path id="center" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="crimson" stroke-width="10" stroke-dashoffset="31.1" stroke-dasharray="0 128.5" >  
      <animate  attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_C.click" dur="4s" restart="whenNotActive" /> 
	</path> 
	  <!-- Middle point on the left stroke-dashoffset="-159.5" -->
	    <path id="Left" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="yellowgreen" stroke-width="10" stroke-dashoffset="-159.5" stroke-dasharray="0 128.5" >  
      <animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_L.click" dur="4s"  restart="whenNotActive" /> 
	</path>  
	
	   <!-- Midpoint left top stroke-dashoffset="128.5" -->
	    <path id="Top" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="gold" stroke-width="10" stroke-dashoffset="128.5" stroke-dasharray="0 128.5" >  
      <animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_T.click" dur="4s"  restart="whenNotActive" /> 
	</path> 
	    <!-- Midpoint lower right  stroke-dashoffset="192.7" -->
	 <path id="Bottom" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="dodgerblue" stroke-width="10" stroke-dashoffset="192.7" stroke-dasharray="0 128.5" >  
      <animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_B.click" dur="4s"  restart="whenNotActive" /> 
	</path>   
	
	       <!-- Middle point on the right   stroke-dashoffset="223.9" -->
	 <path id="Bottom" fill="none" d="M24.3 30C11.4 30 5 43.3 5 50s6.4 20 19.3 20c19.3 0 32.1-40 51.4-40C88.6 30 95 43.3 95 50s-6.4 20-19.3 20C56.4 70 43.6 30 24.3 30z" stroke="purple" stroke-width="10" stroke-dashoffset="223.9" stroke-dasharray="0 128.5" >  
      <animate attributeName="stroke-dasharray" values="0 128.5 0 128.5;0 0 257 0" begin="btn_R.click" dur="4s"  restart="whenNotActive" /> 
	</path> 
	
	
	
 <g id="btn_L" transform="translate(-17 0)" >
      <rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
	     <text x="25" y="95" font-size="10" fill="green" >L</text>
    </g> 	
	<g id="btn_C" transform="translate(3 0)">
      <rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
	     <text x="24" y="95" font-size="10" fill="crimson" >C</text>
    </g> 
	   
	    <g id="btn_T" transform="translate(23 0)">
      <rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
	     <text x="24" y="95" font-size="10" fill="orange" >T</text>
        </g>  
  <g id="btn_B" transform="translate(43 0)">
	<rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
	 <text x="25" y="95" font-size="10" fill="dodgerblue" >B</text>
  </g>	  
      <g id="btn_R" transform="translate(63 0)">
	    <rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="#B2B2B2"/>
	      <text x="25" y="95" font-size="10" fill="purple" >R</text>
    </g>	
</svg>
</div>

Текущая демонстрация

person Alexandr_TT    schedule 16.01.2020
comment
Ах, мужики, вы сделали все возможное, спасибо! Но я должен сказать, что это было не то, что я хотел ... Мне нужно анимировать элемент svg ‹circle›, используя только комбинацию ‹animate attributeName=cx› ‹animate attributeName=cy› (для производительности), чтобы сделать 8 движение формы, зацикленное. - person ivancis; 20.01.2020
comment
@ivancis Могу ли я попробовать еще раз сделать, как вы сказали в дополнительном ответе? - person Alexandr_TT; 20.01.2020
comment
Может быть, это поможет: codepen.io/diazivanluis/pen/MWYzXPm Думаю, ответ где-то позади keySplines и кривая Безье, или продолжительность, или одинаковые / противоположные значения ... Я так много пробовал - person ivancis; 20.01.2020

Я искал аналогичную вещь и наткнулся на этот ответ. Ответ @Alexandr_TT заставил меня задуматься о более гибком способе сделать это без использования графического редактора (например, Inkscape и т. д.).

Мне пришла в голову следующая идея:

  • Используйте <AnimateMotion/> для первого цикла.
  • Запускайте setInterval каждые X миллисекунд и каждый раз, когда он срабатывает, чтобы захватить центральную точку круга (из circle.getBoundingClientRect() и svg.matrixTransform())
  • Поместите эти значения x и y в два массива, чтобы зафиксировать их.
  • Когда AnimateMotion заканчивается, очистите текущий setInterval и поместите первый элемент также в конец каждого из массивов (чтобы закрыть цикл).
  • Удалите тег <AnimateMotion/> из DOM.
  • Поместите эти массивы в атрибут values ​​тегов <animate id="cx" attributeName="cx" values="" .../> и <animate id="cy" attributeName="cy" values="" .../>.
  • начинайте оба этих анимированных тега с cx.beginElement() и cy.beginElement()

Вы могли бы просто быть довольны этим с точки зрения производительности, или вы могли бы скопировать и вставить элементы DOM с их атрибутами values="..." и сохранить это как ваш новый мастер-файл, по сути достигнув того, что @Alexandr_TT сделал с графическим редактором. Конечно, этот метод, который я показываю, является гибким, если вы решите изменить свой путь и т. д.

Демонстрация: https://codepen.io/Alexander9111/pen/VwLaNEN

HTML:

<circle id="circle" class="circle" cx="0" cy="00" r="125">          
      <animateMotion
           path="M162.9,150c6.8-0.2,12.1-5.7,12.1-12.5c0-6.9-5.6-12.5-12.5-12.5c-6.8,0-12.3,5.4-12.5,12.2v25.7 c-0.2,6.8-5.7,12.2-12.5,12.2c-6.9,0-12.5-5.6-12.5-12.5c0-6.8,5.4-12.3,12.1-12.5L162.9,150z"
           dur="4s" begin="0s"
           epeatCount="1" fill="freeze"
           calcMode="linear"
           fill="freeze">
      </animateMotion>
      <animate id="cx" attributeName="cx" values="" dur="4s" repeatCount="indefinite" begin="indefinite"/>
      <animate id="cy" attributeName="cy" values="" dur="4s" repeatCount="indefinite" begin="indefinite"/>
</circle>

JS:

const svg = document.querySelector('svg');
const animateElem = document.querySelector('animateMotion');
const circle = document.querySelector('#circle');
const cx = document.querySelector('#cx');
const cy = document.querySelector('#cy');
let myInterval;
let valuesX = [];
let valuesY = [];

function startFunction() {
  const box = circle.getBoundingClientRect();
  var pt = svg.createSVGPoint();
  pt.x = (box.left + box.right) / 2;
  pt.y = (box.top + box.bottom) / 2;
  var svgP = pt.matrixTransform(svg.getScreenCTM().inverse());
  console.log(svgP.x,svgP.y)
  valuesX.push(svgP.x);
  valuesY.push(svgP.y);
}

function endFunction() {  
  animateElem.parentNode.removeChild(animateElem);
  clearInterval(myInterval)
  valuesX.push(valuesX[0]);
  valuesY.push(valuesY[0]);
  cx.setAttribute('values', valuesX.join('; '));
  cy.setAttribute('values', valuesY.join('; '));
  circle.setAttribute('cx', 0);
  circle.setAttribute('cy', 0);  
  cx.beginElement();
  cy.beginElement();
}

animateElem.addEventListener('beginEvent', () => {
  console.log('beginEvent fired');
  myInterval = setInterval(startFunction, 50);
})

animateElem.addEventListener('endEvent', () => {
  console.log('endEvent fired');
  endFunction();
})
person Alex L    schedule 17.02.2020