SVG Анимация постепенного появления stroke-dasharray

110
01 октября 2021, 08:20

Спустя неоднократные попытки анимировать круг со свойством stroke-dasharray, придумал вариант вложенности circle в circle:

svg { 
	width: 300px; 
} 
 
svg circle { 
	fill: none; 
	stroke-width: 10; 
	stroke: #000; 
} 
 
svg circle.default { 
	opacity: .2; 
	stroke-dasharray: 125.6; 
} 
 
svg circle.default.inner { 
	opacity: .5; 
	stroke-dashoffset: 125.6; 
	animation: circle-default 3s infinite; 
} 
 
@keyframes circle-default { 
	to { 
		stroke-dashoffset: 0; 
	} 
} 
 
svg circle.dashed { 
	stroke-dasharray: 1px; 
}
<svg viewBox="0 0 120 120"> 
	<g> 
		<circle class="default" cx="25" cy="25" r="20"></circle> 
		<circle class="default inner" cx="25" cy="25" r="20"></circle> 
	</g> 
	<g> 
		<circle class="dashed" cx="80" cy="25" r="20"></circle> 
	</g> 
</svg>

JSFIDDLE

Если слева анимирование происходит без каких-либо проблем, то объект справа не поддаётся такому же анимированию.

Идея заключается в помещении одного circle (который по частям) в чистый circle (в котором dasharray высчитывается как в первом). Тогда удастся анимировать внешний circle и сделать загрузку кружка по частям.

Хочется сделать анимацию разворачивания блока, но чтобы всё это было по частям (см. второй объект).

Answer 1

Когда вы разбиваете круг на равные сегменты с помощью stroke-dasharray, то невозможно анимировать их появление с заполнением круга с помощью stroke-dashoffset

Так как круг уже занят полностью. Можно поиграться с изменением величины и количества сегментов, то получатся интересные варианты, но это не то, что вы хотели получить:

<svg width="360" height="360" viewBox="0 0 120 120"> 
	 
	   <circle class="background" cx="25" cy="25" r="20" fill="none" stroke="#E4E4E4" stroke-width="8" /> 
		<circle class="default" cx="25" cy="25" r="20" fill="none" stroke="black" stroke-width="8" 
		stroke-dasharray="125.6" stroke-dashoffset="125.6"> 
		 
		 <animate attributeName="stroke-dasharray" dur="7s" values="1.57, 124.03; 1.57,1.57" fill="freeze" />  
			</circle> 
	 
</svg>

Поэтому можно решить данный вопрос с помощью маски, которая будет открывать круг, заранее разбитый на сегменты, создавая иллюзию заполнения:

Маска будет двигаться с помощью stroke-dashoffset, как у вас первая окружность.

<svg width="360" height="360" viewBox="0 0 120 120"> 
	 
<defs> 
	 <mask id="msk1"> 
      <circle class="maskCircle" cx="25" cy="25" r="20" fill="none" stroke="white" stroke-width="8" stroke-dashoffset="0" stroke-dasharray="125.6"> 
	     <animate attributeName="stroke-dashoffset" dur="4s" values="125.6;0" fill="freeze" repeatCount="indefinite" /> 
	 </circle> 
	 </mask> 
</defs> 
	   <circle class="background" cx="25" cy="25" r="20" fill="none" stroke="#E4E4E4" stroke-width="8" /> 
		<circle class="default" cx="25" cy="25" r="20" fill="none" stroke="black" stroke-width="8" 
		stroke-dasharray="1.57" stroke-dashoffset="20"  mask="url(#msk1)"> 
		 
		</circle> 
	 
</svg>

Answer 2

Если поддержка браузерами позволяет, то можно так (при желании можно ещё поиграться с длиной штриха и анимацией steps(5)):

html, body, svg { 
  margin: auto; 
  display: block; 
  height: 100%; 
} 
 
.dashed { 
  fill: none; 
  stroke-width: 10; 
  stroke: black; 
  stroke-dasharray: 2.61; 
  stroke-dashoffset: -1.275; 
} 
 
.hidden { 
  fill: transparent; 
} 
 
g { 
  animation: clip 7s linear infinite; 
} 
 
@keyframes clip { 
  from { clip-path: polygon(50% 50%, 150% 50%, 150% 50%, 50% 50%, 50% 150%, 50% 150%, 50% 50%, -50% 50%, -50% 50%, 50% 50%, 50% -50%, 50% -50%, 50% 50%); } 
   25% { clip-path: polygon(50% 50%, 150% 50%, 50% 150%, 50% 50%, 50% 150%, 50% 150%, 50% 50%, -50% 50%, -50% 50%, 50% 50%, 50% -50%, 50% -50%, 50% 50%); } 
   50% { clip-path: polygon(50% 50%, 150% 50%, 50% 150%, 50% 50%, 50% 150%, -50% 50%, 50% 50%, -50% 50%, -50% 50%, 50% 50%, 50% -50%, 50% -50%, 50% 50%); } 
   75% { clip-path: polygon(50% 50%, 150% 50%, 50% 150%, 50% 50%, 50% 150%, -50% 50%, 50% 50%, -50% 50%, 50% -50%, 50% 50%, 50% -50%, 50% -50%, 50% 50%); } 
    to { clip-path: polygon(50% 50%, 150% 50%, 50% 150%, 50% 50%, 50% 150%, -50% 50%, 50% 50%, -50% 50%, 50% -50%, 50% 50%, 50% -50%, 150% 50%, 50% 50%); } 
}
<svg viewBox="-32 -32 64 64"> 
  <g> 
    <circle r="25" class="hidden" /> 
    <circle r="20" class="dashed" /> 
  </g> 
</svg>

Answer 3

Таким вот способом можно получить нечто по сложнее окружности:

let r = 80 
 
function draw(t){ 
  let a = (t/500)%(Math.PI*7) 
  let pts = []; 
  for (let i=0; i<a; i+=0.01){ 
    pts.push([Math.cos(i)*(r-i*3), Math.sin(i)*(r-i*3)]) 
  } 
  circle.setAttribute('d', 'M' + pts.join('L'))  
  requestAnimationFrame(draw) 
} 
 
requestAnimationFrame(draw)
body {margin:0;overflow:hidden}
<svg width="100vw" height="100vh" viewBox="-100 -100 200 200"> 
 <path id=circle fill="none" stroke="black" stroke-width="18" 
		   stroke-dasharray="2"></path> 
</svg>

Answer 4

Концепт оформления галереи

Использовал эффект анимации рисования сегментов из первого ответа.

При наведении курсора рисуется сегментный круг с анимацией появления изображения.
При уводе курсора сегментный круг сворачивается и прозрачность изображения уменьшается.

Подробности в комментариях к коду:

.container { 
width:75%; 
height:75%; 
}
<div class="container"> 
<svg  viewBox="0 0 120 120"> 
	 
<defs> 
	 <mask id="msk1"> 
      <circle class="maskCircle" cx="25" cy="25" r="20" fill="none" stroke="white" stroke-width="5" stroke-dashoffset="125.6" stroke-dasharray="125.6"> 
	   <!-- анимация рисования черточек -->   
		 <animate 
		   attributeName="stroke-dashoffset" 
		   begin="background.mouseover" 
		   end="background.mouseout" 
		   dur="3s" 
		   values="125.6;0" 
		   fill="freeze" 
		   repeatCount="1" />  
       <!-- анимация стирания черточек --> 	   
	  <animate 
	     attributeName="stroke-dashoffset" 
		 begin="background.mouseout" 
		 dur="1s" 
		 values="0;125.6" 
		 fill="freeze" 
		 repeatCount="1" /> 
	 </circle> 
	 </mask> 
	 
    <pattern id="image" x="0%" y="0%" height="100%" width="100%"> 
      <image x="5" y="3" width="29%" height="29%" opacity="0.1" xlink:href="https://i.stack.imgur.com/acozx.jpg"> 
	   <!-- анимация увеличения прозрачности изображения при наведении --> 
	   <animate id="an1" 
	     attributeName="opacity" 
		 begin="background.mouseover" 
		 dur="4s" 
		 values="0.05;1" 
		 fill="freeze" />  
      <!-- анимация уменьшения прозрачности изображения при уводке курсора-->	   
	  <animate 
	     attributeName="opacity" 
		 begin="background.mouseout" 
		 dur="1s" 
		 to="0.05" 
		 fill="freeze" />   
	   
	  </image> 
    </pattern> 
    
</defs>  
    <g id="gr1"> 
	   <circle id="background" cx="25" cy="25" r="20" fill="url(#image)" stroke="#E4E4E4" stroke-width="5" /> 
		<circle class="default" cx="25" cy="25" r="20" fill="none" stroke="black" stroke-width="5" 
		stroke-dasharray="1.57" stroke-dashoffset="20"  mask="url(#msk1)"> 
		 
		</circle> 
	</g>   
	 
	  
</svg> 
</div>

Пример анимации из одной точки 2 линиями

.container { 
width:75%; 
height:75%; 
}
<div class="container"> 
<svg  viewBox="0 0 120 120"> 
	 
<defs> 
	 <mask id="msk1"> 
      <circle class="maskCircle" cx="25" cy="25" r="20" fill="none" stroke="white" stroke-width="5" stroke-dashoffset="-31.4" stroke-dasharray="0,62.8;0, 62.8"> 
	   <!-- анимация рисования черточек -->   
		 <animate 
		   attributeName="stroke-dasharray" 
		   begin="background.mouseover" 
		   end="background.mouseout" 
		   dur="3s" 
		   values="0, 62.8 0, 62.8;0,0 125.6,0" 
		   fill="freeze" 
		   repeatCount="1" 
		   restart="whenNotActive" />  
       <!-- анимация стирания черточек --> 	   
	  <animate 
	     attributeName="stroke-dasharray" 
		 begin="background.mouseout" 
		 dur="1s" 
		 values="0,0 125.6,0;0, 62.8 0, 62.8" 
		 fill="freeze" 
		 repeatCount="1" 
		 restart="whenNotActive" /> 
	 </circle> 
	 </mask> 
	 
    <pattern id="image" x="0%" y="0%" height="100%" width="100%"> 
      <image x="-2.8" y="0" width="38%" height="38%" opacity="0.1" xlink:href="https://i.stack.imgur.com/EyUuX.jpg"> 
	   <!-- анимация увеличения прозрачности изображения при наведении --> 
	   <animate id="an1" 
	     attributeName="opacity" 
		 begin="background.mouseover" 
		 dur="4s" 
		 values="0.05;1" 
		 fill="freeze" />  
      <!-- анимация уменьшения прозрачности изображения при убирании курсора-->	   
	  <animate 
	     attributeName="opacity" 
		 begin="background.mouseout" 
		 dur="1s" 
		 to="0.05" 
		 fill="freeze" />   
	   
	  </image> 
    </pattern> 
    
</defs>  
    <g id="gr1"> 
	   <circle id="background" cx="25" cy="25" r="20" fill="url(#image)" stroke="#E4E4E4" stroke-width="5" /> 
		<circle class="default" cx="25" cy="25" r="20" fill="none" stroke="black" stroke-width="5" 
		stroke-dasharray="1.57" stroke-dashoffset="20"  mask="url(#msk1)"> 
		 
		</circle> 
	</g>   
	 
	  
</svg> 
</div>

Answer 5

В целом что-то такое всегда можно изобразить при помощи js математики и команды arc:

let r = 80 
 
function draw(t){ 
  let a = (t/1000)%(Math.PI*2) 
  let x = Math.cos(a)*r; 
  let y = Math.sin(a)*r; 
  let largeArc = a > Math.PI ? 1 : 0; 
  circle.setAttribute('d', `M${r},0A${r},${r},0,${largeArc},1,${x},${y}`)  
  requestAnimationFrame(draw) 
} 
 
requestAnimationFrame(draw)
body {margin:0;overflow:hidden}
<svg width="100vw" height="100vh" viewBox="-100 -100 200 200"> 
 <path id=circle fill="none"stroke="black" stroke-width="18" stroke-dasharray="2"></path> 
</svg>

READ ALSO
Управление SVG-координатами `path` с помощью JavaScript

Управление SVG-координатами `path` с помощью JavaScript

У меня есть path SVG со следующими точками в атрибуте d

92
Импорт модуля Node/JS, require/import, module/commonjs

Импорт модуля Node/JS, require/import, module/commonjs

Пытаюсь достучаться до импорта модулей

171
Как правильно в данном случае поступить с функцией, как при вызове функции пропускать параметры

Как правильно в данном случае поступить с функцией, как при вызове функции пропускать параметры

Есть задачаНаписать функцию, которая принимает время (часы, минуты, секунды) и выводит его на экран в формате «чч:мм:сс»

198
JS. Как обращаться к вложенным функциям?

JS. Как обращаться к вложенным функциям?

Как обратиться к вложенной функции и вложенной в нее функции?

179