【问题标题】:Shape "8" looped animation with SVG <animate> only仅使用 SVG <animate> 形状“8”循环动画
【发布时间】:2020-05-03 09:55:41
【问题描述】:

我非常头疼,试图找到使用组合 &lt;animate attributeName="cx"&gt;&lt;attributeName="cy"&gt; 实现“8”形动画的最佳位置。
我喜欢使用它,因为根据我的指标,它似乎是 FPS、CPU 和 GPU 使用率方面性能最高的。

“理想”运动路径的快速演示:https://codepen.io/ivancis/pen/eYmZowz

【问题讨论】:

标签: css animation svg svg-animate smil


【解决方案1】:

你没有说你真正想要什么样的动画。

因此,我将提供不同类型动画的示例,您可以选择其中的任何一个并以某种方式自己修改。

无穷大符号移动动画

<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>

Live Demo

【讨论】:

  • 哎呀,你已经超越了,谢谢!但我必须说这不是我所追求的......我需要使用 的组合来为 svg 元素设置动画(为了性能)做一个“8”形的动作,循环。
  • @ivancis 我可以按照您在附加答案中所说的再试一次吗?
  • 也许这会有所帮助:codepen.io/diazivanluis/pen/MWYzXPm 我猜答案在 keySplines 和贝塞尔曲线、持续时间或相同/相反值的某个地方......我已经尝试了很多
【解决方案2】:

由 cmets 中的改进引起的新解决方案

我需要仅使用以下组合为 svg 元素设置动画 &lt;animate attributeName="cx"&gt;&lt;animate attributeName="cy"&gt;(对于 表演)做一个“8”形运动,循环

由于作者不想使用animateMotion命令,本例 我只看到了一种实现沿无穷大符号运动的动画的方法:

需要沿无穷大符号依次选择多个点,并将它们的坐标分配给圆cx = "x"cy = "y"

选择的点越多,圆沿无穷大符号移动的轨迹越接近

在矢量编辑器中,我依次将圆圈放在无穷大符号上,并记下它们的圆心坐标。第一个圆的中心坐标为cx ="70"cy ="60"
因此,对位于无穷大符号的所有圆圈都进行了此操作。最后一个圆和第一个圆的坐标相同,从而实现了一个闭环

只需要在动画公式cxcy 中替换这些值即可

圆周运动动画cxcy,半径为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>
  

Radius 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>
  

【讨论】:

  • 您在下面的答案很棒@Alexandr_TT,尤其是“圆圈中的彩色字母”。此外,这里的这个答案(使用 Inkscape)让我了解了如何解决我在没有 Inkscape 的情况下面临的类似问题。很想听听您的想法,并希望我的回答也可以帮助其他登陆此页面的人。
  • @Alex L 我认为用不同的方法解决同一个问题是非常有成效和有用的。当我回答这个问题时,我对作者原则上不使用 animateMotion 的愿望感到非常惊讶 我的回答和您的回答重复了标准 SVG animateMotion 命令的工作,该命令本身计算坐标并沿运动路径移动对象.我们的回答有什么实际好处吗?这是一个很大的问题。人们可能不想知道细节。
  • @AlexL 他们将继续使用标准选项来解决此类问题 - animateMotion 总而言之,你做得很好你的方法当然可以使用在这样的问题中,机械设备首先从行进的路径中学习,记住坐标,然后可以自己自动重复它们
  • 感谢您的反馈!完全同意。我从未听说过&lt;animateMotion/&gt; 效率低下,但我确实响应了一个类似于 OP 的用例,根据路径仅对线的一端进行动画处理(并保持另一端锚定),为此,我使用了类似的方法(stackoverflow.com/a/60024858/9792594)。尽管我同意,但用例是有限的,&lt;animateMotion/&gt;&lt;animate/&gt; 在以预期方式使用时可以解决大多数问题:)
【解决方案3】:

我正在研究类似的事情并遇到了这个答案。 @Alexandr_TT 的回答让我想到了一种更灵活的方法来做到这一点,而无需使用图形编辑器(如 Inkscape 等)

我想出了以下想法:

  • &lt;AnimateMotion/&gt; 用于第一个循环。
  • 每隔 X 毫秒触发一次 setInterval,每次触发以捕获圆的中心点(来自 circle.getBoundingClientRect()svg.matrixTransform()
  • 将这些 x 和 y 值推送到两个数组以捕获它们
  • 当 AnimateMotion 结束时,清除当前的 setInterval 并将第一个元素也推到每个数组的末尾(以关闭循环)
  • 从 DOM 中删除 &lt;AnimateMotion/&gt; 标记
  • 将这些数组推送到 &lt;animate id="cx" attributeName="cx" values="" .../&gt;&lt;animate id="cy" attributeName="cy" values="" .../&gt; 标签的 values 属性
  • 这两个动画标签都以cx.beginElement()cy.beginElement() 开头

您可以对这种性能感到满意,或者您可以复制粘贴带有 values="..." 属性的 DOM 元素并将其保存为您的新主文件,基本上实现了 @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();
})

【讨论】:

    猜你喜欢
    • 2013-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 2021-01-07
    相关资源
    最近更新 更多