5

Как на SVG создать круглый бордюр с заливкой от низа к верху.

Ниже, как это реализовано с помощью CSS:

$('input[type="range"]').bind('change mousemove',function(){
  $('.border-bg').height($(this).val()+'%');
});
.border {
  display: block;
  width: 160px;
  height: 160px;
  border-radius: 100%;
  overflow: hidden;
  background: rgba(255,0,0,.25);
  position: relative;
}

.border::after { content: ''; display: block; width: calc(100% - 20px); height: calc(100% - 20px); margin: 10px; background: #fff; border-radius: 100%; position: absolute; left: 0; top: 0; right: 0; bottom: 0; }

.border-bg { display: block; width: 100%; height: 30%; background: red; position: absolute; left: 0; right: 0; top: auto; bottom: 0; }

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="border">
  <div class="border-bg"></div>
</div>

<input type="range" min="0" value="30" max="100" step="1">

Доп. вопрос:
Как использовать не только бордюр, а допустим слой-маску.

Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384
De.Minov
  • 24,026

2 Answers2

8

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

.container{
width:50%;
height:50%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; mask:url(#msk1); }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>
    <mask id="msk1" >
    <rect fill="white" width="100%" height="100%" />    
    <rect fill="red" x="0" y="0" width="200" height="200" >
      <animate attributeName="y" dur="4s" begin="0s" values="0;-200" fill="freeze" />
   </rect> 
  </mask> 
  </defs>
  <circle id="crc1"  cx="100" cy="100" r="90" />
</svg>

</div>

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

Рисование сверху вниз
Вместо значения - values="0;-200" устанавливаем values="0;200"

.container{
width:50%;
height:50%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; mask:url(#msk1); }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>
    <mask id="msk1" >
    <rect fill="white" width="100%" height="100%" />    
    <rect fill="red" x="0" y="0" width="200" height="200" >
      <animate attributeName="y" dur="4s" begin="0s" values="0;200" fill="freeze" />
   </rect> 
  </mask> 
  </defs>
  <circle id="crc1"  cx="100" cy="100" r="90" />
</svg>

</div>

Слева- направо

В этом случае будем анимировать координату "x" прямоугольника маски

<animate attributeName="x" dur="4s" begin="0s" values="0;200" fill="freeze" />  

.container{
width:50%;
height:50%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; mask:url(#msk1); }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>
    <mask id="msk1" >
    <rect fill="white" width="100%" height="100%" />    
    <rect fill="red" x="0" y="0" width="200" height="200" >
      <animate attributeName="x" dur="4s" begin="0s" values="0;200" fill="freeze" />
   </rect> 
  </mask> 
  </defs>
  <circle id="crc1"  cx="100" cy="100" r="90" />
</svg>

</div>

Движение в левую сторону

.container{
width:50%;
height:50%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; mask:url(#msk1); }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>
    <mask id="msk1" >
    <rect fill="white" width="100%" height="100%" />    
    <rect fill="red" x="0" y="0" width="200" height="200" >
      <animate attributeName="x" dur="4s" begin="0s" values="0;-200" fill="freeze" />
   </rect> 
  </mask> 
  </defs>
  <circle id="crc1"  cx="100" cy="100" r="90" />
</svg>

</div>
Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384
  • Как всегда понятный и развёрнутый ответ. Спасибо! Ещё вопрос, как использовать какую-нибудь маску с таким же эффектом заполнения? – De.Minov Jan 21 '19 at 11:45
  • 1
    @CbIPoK2513 да не за что, не понял только вопрос, разве в моем ответе не использована маска? Если не трудно задай тогда дополнительный вопрос, чтобы я понял, что вы хотите получить на выходе. Можно в чате. – Alexandr_TT Jan 21 '19 at 11:49
  • В теге можно использовать "что угодно"? – De.Minov Jan 21 '19 at 12:29
  • 1
    @CbIPoK2513 да, все основные фигуры svg Почитай мою статью по маскам. Пытался донести отдельные принципы максимально просто и доходчиво https://ru.stackoverflow.com/q/919186/28748 – Alexandr_TT Jan 21 '19 at 12:32
3

Использование stroke-dasharray

Все привыкли к стандартному использованию рисования из начала или конца отрезка в сторону увеличения или уменьшения линии.

Поэтому понять сразу, как можно использовать этот атрибут для одновременного рисования из одной точки в разные стороны, довольно сложно. Начнём с самых простых основ.

Допустим у нас окружность R="90px" Длина окружности будет равна C=2πr = 2 * 3.14 * 90 ≈ 565.2

Половина окружности равна 565,2 / 2 ≈ 282.6

  • stroke-dasharray="282.6 282.6" первая цифра длина черты (полокружности), вторая цифра пробел

.container{
width:100%;
height:100%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; }

#bg{ fill:none; stroke:#D5D5D5; stroke-width:10; }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>

  </defs>
   <circle id="bg"  cx="100" cy="100" r="90" />
  <circle id="crc1"   cx="100" cy="100" r="90"  stroke-dasharray="282.6 282.6"/>

  </svg>
</div>

Пока всё понятно - первая половина окружности закрашена, вторая пробел

  • Теперь установим stroke-dasharray="282.6 0"

    Первая половина окружности закрашивается, вторая половина окружности пропускается, снова закрашивается половина окружности, это уже вторая половина и снова пропуск. В итоге закрашивается вся окружность.

.container{
width:100%;
height:100%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; }

#bg{ fill:none; stroke:#D5D5D5; stroke-width:10; }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>

  </defs>
   <circle id="bg"  cx="100" cy="100" r="90" />
  <circle id="crc1"   cx="100" cy="100" r="90"  stroke-dasharray="282.6 0"/>

  </svg>
</div>
  • Теперь установим stroke-dasharray="0 282.6"

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

    .container{
    width:100%;
    height:100%;
    }
    

    #crc1{ fill:none; stroke:red; stroke-width:10; }

    #bg{ fill:none; stroke:#D5D5D5; stroke-width:10; }

    <div class="container">
    <svg height="200" width="200" viewBox="0 0 200 200">
      <defs>
    
      </defs>
       <circle id="bg"  cx="100" cy="100" r="90" />
      <circle id="crc1"   cx="100" cy="100" r="90"  stroke-dasharray="0 282.6"/>
    
      </svg>
    </div>

Используя эти данные напишем формулу анимации при которой окружность будет заполняться из одной точки в разные стороны:

 <circle id="crc1" transform="rotate(90 100 100)"  cx="100" cy="100" r="90" stroke-dasharray="0 282.6 0 282.6"  >
     <animate attributeName="stroke-dasharray" values="0 282.6 0 282.6;0 0 565.2 0" dur="5s" fill="freeze" />
    </circle>  

Ниже полный код заполнения слева-направо

.container{
width:100%;
height:100%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; }

#bg{ fill:none; stroke:#D5D5D5; stroke-width:10; }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>

  </defs> 
  <circle id="bg"  cx="100" cy="100" r="90" />
   <circle id="crc1" transform="rotate(0 100 100)"  cx="100" cy="100" r="90" stroke-dasharray="0 282.6 0 282.6"  >
   <animate attributeName="stroke-dasharray" values="0 282.6 0 282.6;0 0 565.2 0" dur="5s" fill="freeze" />
   </circle>
</svg>

</div>

Сверху-вниз

.container{
width:100%;
height:100%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; }

#bg{ fill:none; stroke:#D5D5D5; stroke-width:10; }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>

  </defs> 
  <circle id="bg"  cx="100" cy="100" r="90" />
   <circle id="crc1" transform="rotate(90 100 100)"  cx="100" cy="100" r="90" stroke-dasharray="0 282.6 0 282.6"  >
   <animate attributeName="stroke-dasharray" values="0 282.6 0 282.6;0 0 565.2 0" dur="5s" fill="freeze" />
   </circle>
</svg>

</div>

Справа - налево

.container{
width:100%;
height:100%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; }

#bg{ fill:none; stroke:#D5D5D5; stroke-width:10; }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>

  </defs> 
  <circle id="bg"  cx="100" cy="100" r="90" />
   <circle id="crc1" transform="rotate(180 100 100)"  cx="100" cy="100" r="90" stroke-dasharray="0 282.6 0 282.6"  >
   <animate attributeName="stroke-dasharray" values="0 282.6 0 282.6;0 0 565.2 0" dur="5s" fill="freeze" />
   </circle>
</svg>

</div>

Снизу - вверх

.container{
width:100%;
height:100%;
}

#crc1{ fill:none; stroke:red; stroke-width:10; }

#bg{ fill:none; stroke:#D5D5D5; stroke-width:10; }

<div class="container">
<svg height="200" width="200" viewBox="0 0 200 200">
  <defs>

  </defs> 
  <circle id="bg"  cx="100" cy="100" r="90" />
   <circle id="crc1" transform="rotate(270 100 100)"  cx="100" cy="100" r="90" stroke-dasharray="0 282.6 0 282.6"  >
   <animate attributeName="stroke-dasharray" values="0 282.6 0 282.6;0 0 565.2 0" dur="5s" fill="freeze" />
   </circle>
</svg>

</div>
Alexandr_TT
  • 110,146
  • 23
  • 114
  • 384
  • 1
    Ого, красивая реализация вышла. – De.Minov Jan 21 '19 at 18:42
  • 1
    @CbIPoK2513 великий и могучий svg, одна формула и уже можно делать всякие лоадеры, прогрессбары и т.д. – Alexandr_TT Jan 21 '19 at 18:46