4

У меня есть знак бесконечности в svg, у которого имеется анимация заполнения своего контура.

.black {
  stroke: black;
  fill: none;
  stroke-width: 20;
  stroke-dasharray: 923.0598754882812;
  animation: dash 5s ease 1;
}

.rainbow1 { stroke: url(#linear); fill: none; stroke-width: 20; stroke-dasharray: 923.0598754882812; animation: dash 5s ease 1; }

@keyframes dash { 0% { stroke-dashoffset: 923.0598754882812; } 50% { stroke-dashoffset: -923.0598754882812; } 100% { stroke-dashoffset: -1846.119750976562; } }

<svg width="625" height="600">
  <path class="black" d="M200 200
           Q250 100 400 200
           T600 200
           Q500 100 400 200
           T200 200"
    /> 
</svg>

<svg width="625" height="600">
  <defs>
   <linearGradient id="linear" x1="0%" y1="0%" x2="100%" y2="0%">
     <stop offset="0%"   stop-color="#05a"/>
     <stop offset="50%"  stop-color="#a55"/>
     <stop offset="100%" stop-color="#0a5"/>
   </linearGradient>
 </defs>

  <path class="rainbow1" d="M200,210 
           Q275 100 400 200
           T600 200
           Q510 100 400 200
           T200 200"
        />
</svg>

Как сделать так, чтобы контур проходил только один (а не двойной) цикл и был с и имел более плавную и четкую форму без обрубка-стыка на конце. Вот такую:

    #infinity {
      position: relative;
      width: 212px;
      height: 100px;
      box-sizing: content-box;
    }
    #infinity:before,
    #infinity:after {
      content: "";
      box-sizing: content-box;
      position: absolute;
      top: 0;
      left: 0;
      width: 60px;
      height: 60px;
      border: 20px solid black;
      border-radius: 50px 50px 0 50px;
      transform: rotate(-45deg);
    }
    #infinity:after {
      left: auto;
      right: 0;
      border-radius: 50px 50px 50px 0;
      transform: rotate(45deg);
    }
<div id="infinity"></div>
Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384
Вася
  • 4,482

3 Answers3

7

Так?

<svg width="200px" height="200px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 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-40 C88.6,30,95,43.3,95,50s-6.4,20-19.3,20C56.4,70,43.6,30,24.3,30z" stroke="#000" stroke-width="10" stroke-dasharray="0 256.588928222656257 0">
      <animate attributeName="stroke-dashoffset" values="0;256.58892822265625" dur="1" fill="freeze"></animate>
    </path>
  </svg>
Kvilios
  • 1,448
  • поставил плюсик.Также хочу спросить можно объяснения к коду? – Randall Feb 12 '19 at 09:12
  • Что именно не понятно? – Kvilios Feb 12 '19 at 09:15
  • да собственно говоря ничего. – Randall Feb 12 '19 at 09:32
  • @Kvilios спасибо! А можно делать задержку анимации, к примеру если не доскролил страницу, то она еще не отрабатывает? Или здесь уже без js никак? – Вася Feb 12 '19 at 09:37
  • 1
    @Вася добавьте к тегу animate begin="3s" и анимация начнется через 3 секунды. Через JS также можете вызвать beginElement у элемента из DOM по своему событию (будь то скролл или клик). – Kvilios Feb 12 '19 at 09:51
  • @Избытоксусликов в данном примере используется svg у которого прописан векторный путь (собственно это и есть сама иконка бесконечности). Также для анимации используется тег animate, у которого задан сам объект (обводка), значение откуда и до куда анимировать (начальная и конечная точки), длительность анимации и стоп анимации на последнем "кадре" – Kvilios Feb 12 '19 at 09:56
  • @Kvilios спасибо – Randall Feb 12 '19 at 09:59
  • @Kvilios покажу на примере https://jsfiddle.net/p2wa69qn/ При пролистывании хочу, чтобы на waypoint начала срабатывать анимация, но в моем случае это не происходит, объект появляется сразу без анимации – Вася Feb 12 '19 at 12:36
  • 1
    @Вася Оберните svg в объект, а затем можно использовать ф-ии $("#infinity-counter").show().beginElement();. Вот пример: https://jsfiddle.net/8jyx7w1u/ Из-за того, что браузер кэширует svg - анимация повторно не проигрывается. Такая обёртка помогает избежать этой проблемы. – Kvilios Feb 12 '19 at 14:00
  • @Kvilios понял, спасибо большое за помощь и объяснения! К слову, у меня даже работает без beginElement(), а на него в консоли ругается – Вася Feb 12 '19 at 14:13
  • 1
    @Вася, да, я тупанул, его надо без обёртки jQuery вызывать, можно через getElementById("infinity-counter")[0].beginElement(); И желательно убрать id из самой svg – Kvilios Feb 12 '19 at 14:18
7

Анимация симметричного заполнения фигур из одной точки

Подробно рассказано об этой оригинальной технике здесь. Смотрите вторую половину ответа.

Патч взял из ответа @Kvilios
Максимальная длина его - 257px. Половина - 128.5px Четверть пути - 64.2px. Эти цифры понадобятся для сдвига начала path командой stroke-dashoffset="128.5"

Пример заполнения фигуры из середины path

Обратите внимание, что фигура одновременно, симметрично заполняется из центра, пока края не сомкнутся.

body {background:#2F2F2F;}
.container 
{
 width:50%;
 height:50%;

}

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

 <path id="path1" 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="greenyellow" stroke-width="10" stroke-dashoffset="31.1" stroke-dasharray="0 128.5" >  
      <animate
     xlink:href="#path1"
  attributeName="stroke-dasharray"
  values="0 128.5 0 128.5;0 0 257 0"
  begin="btn.click"
  dur="4s"
  fill="freeze"/> 
 </path> 

 <g id="btn" transform="translate(12 0)">
      <rect x="20" y="84" width="35" height="15" rx="5" fill="none" stroke="dodgerblue"/>
      <text x="24" y="94" font-size="10" fill="crimson" >Центр</text>
    </g>
</svg>
</div>

Комплексный пример заполнения

Начать заполнение можно из любой точки фигуры, для этого надо сдвинуть начало рисования линии от начала с помощью изменения числового значения атрибута stroke-dashoffset

В приложении кнопка L, - запускает рисование с левого края

R - справа (right)

T - сверху (top)

C - центр (center)

B - снизу (bottom)

На эти обозначения ориентируйтесь при разборе кода id="btn_c", <path id="center"

.container
{
width:40%;
height:40%;  
 background:black;
}
<div class="container">
<svg xmlns="http://www.w3.org/2000/svg"  viewBox="0 25 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" />
<!-- Средняя точка начала анимации в центре слева 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> <!-- Средняя точка слева 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>

&lt;!-- Средняя точка слева сверху stroke-dashoffset="128.5" --&gt;
 &lt;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" &gt;  
  &lt;animate
    attributeName="stroke-dasharray"
    values="0 128.5 0 128.5;0 0 257 0"
    begin="btn_T.click"
    dur="4s"
    restart="whenNotActive" /&gt; 

</path> <!-- Средняя точка справа внизу 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>

    &lt;!-- Средняя точка справа   stroke-dashoffset="223.9" --&gt;

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

 &lt;g id="btn_T" transform="translate(23 0)" &gt;
  &lt;rect x="20" y="84" width="15" height="15" rx="7.5" fill="none" stroke="dodgerblue"/&gt;
  &lt;text x="24" y="95" font-size="10" fill="orange" &gt;T&lt;/text&gt;
    &lt;/g&gt;  

<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

Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384
  • супер! Крутое объяснение! Поставил бы вам много плюсов или дополнительно к ответу выше засчитал, как еще один правильный ответ, но так уже нельзя.. Значит 100 плюсов вам в карму, всех благ! – Вася Feb 14 '19 at 16:29
  • по ссылке перешел и так и сделал! Буду внимательно читать как все это правильно делается. Если встречу еще ваши хорошие ответы, то и там непременно сделаю тоже самое) – Вася Feb 14 '19 at 16:36
  • @Вася Радуйся, если получил ответ Александра, ибо тебе никогда не придется задавать вопросы по этой теме :) – Ver Nick Feb 15 '19 at 14:05
4

Почти не менял оригинальный код...

#infinity {
  position: relative;
  width: 212px;
  height: 100px;
  box-sizing: content-box;
  animation: clip-grow 4s;
}
#infinity:before,
#infinity:after {
  content: "";
  box-sizing: content-box;
  position: absolute;
  top: 0;
  left: 0;
  width: 60px;
  height: 60px;
  border: 20px solid black;
  border-radius: 50px 50px 0 50px;
  transform: rotate(-45deg);
}
#infinity:after {
  left: auto;
  right: 0;
  border-radius: 50px 50px 50px 0;
  transform: rotate(45deg);
}

@keyframes clip-grow { from{clip-path: circle(0px at 106px 50px);} to {clip-path: circle(200px at 106px 50px);} }

<div id="infinity"></div>