SVG полукруг разбить на 3 равные части

116
04 января 2020, 22:30

У меня приложение на Vue.js и мне нужно прогресс-бар создать. Я использую Svg, потому что другие варианты не подойдут. Как можно разбить SVG-полукруг на равные части. У меня получается сплошная линия. Пытался манипулировать атрибутом stroke-dasharray, но не получается. Необходимо получить такой вид:

я пока добился такого

мой код:

<div class="radial"> 
        <svg  xmlns="http://www.w3.org/2000/svg" height="100" width="100" viewBox="0 0 200 200" data-value="40"> 
            <path class="bg" stroke="#ccc" d="M41 149.5a77 77 0 1 1 117.93 0"  fill="none"/> 
            <path class="meter" stroke="#D15F45" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" pathLength="100" stroke-dasharray="38, 100"/> 
        </svg> 
    </div>

Answer 1

Самый простой вариант, если фон белый, можно закрыть 2мя белыми линиями, идущими от центра:

requestAnimationFrame(draw) 
function draw(dt) { 
  document.querySelector('.meter').setAttribute('stroke-dasharray', `${dt/10%100}, 100`); 
  requestAnimationFrame(draw) 
}
<div class="radial"> 
  <svg xmlns="http://www.w3.org/2000/svg" height="200" width="200" viewBox="0 0 200 200" data-value="40"> 
    <path class="bg" stroke-width="22" stroke="#ccc" d="M41 149.5a77 77 0 1 1 117.93 0"  fill="none"/> 
    <path class="meter" stroke-width="22" stroke="#D15F45" d="M41 149.5a77 77 0 1 1 117.93 0" fill="none" pathLength="100" stroke-dasharray="38, 100"/> 
    <path stroke="white" stroke-width="5" d="M100 100 l-100 -100"/>  
    <path stroke="white" stroke-width="5" d="M100 100 l100 -100"/>  
  </svg> 
</div>

Вариант с масками

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

<body style="margin:0; overflow:hidden"> 
    <svg  height="200" width="640"> 
      <defs> 
      <mask id="m"> 
        <rect width="100%" height="100%" fill="white"/> 
        <path stroke="black" stroke-width="2" d="M100 100 l100 -100"/>  
        <path stroke="black" stroke-width="2" d="M100 100 l-100 -100"/>  
        <path fill="black" d="M100 100 l-115 100 l115 100 l115 -100z"/>     
      </mask> 
      </defs> 
 
      <g> 
        <circle fill="none" stroke-width="25" stroke="#999" r="77" cx="100" cy="100"/> 
      </g> 
       
      <g transform="translate(220,0)"> 
        <rect width="100%" height="100%" fill="white"/> 
        <path stroke="black" stroke-width="2" d="M100 100 l100 -100"/>  
        <path stroke="black" stroke-width="2" d="M100 100 l-100 -100"/>  
        <path fill="black" d="M100 100 l-115 100 l230 0z"/>     
      </g> 
       
<g transform="translate(440,0)"> 
        <circle mask="url(#m)"fill="none" stroke-width="25" stroke="#999" r="77" cx="100" cy="100"/> 
      </g> 
            <text text-anchor="middle" alignment-baseline="central" font-size=55 x=210 y=100>+</text> 
            <text text-anchor="middle" alignment-baseline="central" font-size=55 x=420 y=100>=</text> 
   </svg> 
 </body>

Результат:

let meter = document.querySelector('path.meter'); 
let text = document.querySelector('text'); 
let arrow = document.querySelector('path.arrow'); 
let length = meter.getTotalLength(); 
 
requestAnimationFrame(draw) 
 
function draw(dt) { 
  progress(dt / 100 % 100); 
  requestAnimationFrame(draw) 
} 
 
function progress(value) { 
  meter.setAttribute('stroke-dasharray', `${length/100*value}, ${length}`); 
  arrow.setAttribute('transform', `rotate(${-135+value*270/100} 100,100)`); 
  text.innerHTML = Math.round(value); 
}
<svg height="175" viewBox="0 0 200 200"> 
 
  <defs> 
 
    <mask id="mask1"> 
      <rect width="100%" height="100%" fill="white"/> 
      <path stroke="black" stroke-width="2" d="M100 100 l100 -100"/>  
      <path stroke="black" stroke-width="2" d="M100 100 l-100 -100"/>  
      <path fill="black" d="M100 100 l-115 100 l230 0z"/>     
    </mask> 
 
    <mask id="mask2"> 
      <rect width="100%" height="100%" fill="white"/> 
      <path stroke="black" stroke-width="5" d="M100 100 l100 -100"/>  
      <path stroke="black" stroke-width="5" d="M100 100 l-100 -100"/>   
    </mask> 
 
   </defs> 
   
   <g fill="none"> 
     <circle mask="url(#mask1)" stroke-width="25" stroke="#999" r="77" cx="100" cy="100"/> 
     <path mask="url(#mask2)" stroke-width="22" stroke="#ccc" d="M41 149.5a77 77 0 1 1 117.93 0"/> 
     <path mask="url(#mask2)" stroke-width="22" stroke="#d15f45" d="M41 149.5a77 77 0 1 1 117.93 0" class="meter"/> 
     <circle stroke-width="7" stroke="#ccc" r="33" cx="100" cy="100"/> 
   </g> 
   <text x="100" y="100" font-family="arial" font-size="22px" text-anchor="middle" alignment-baseline="central">0</text> 
   <path class="arrow" transform="rotate(-135 100,100)" fill="#ccc" d="M100,65 l5,0 l-5,-10 l-5,10"></path> 
 </svg>

UPD: сделал как в вопросе, с циферками и стрелочкой

Answer 2

Решение с помощью stroke-dasharray

У заданного пути максимальная длина равна 350px
Если нужно поделить на три равных сектора, то длина одного сектора будет равна 116.67px
Это сумма длин: черты - 110px и пробела 6.67px

<svg  xmlns="http://www.w3.org/2000/svg" height="200" width="200" viewBox="0 0 200 200" data-value="40"> 
       <path class="bg" stroke="#ccc" stroke-width="20" stroke-dasharray="110 6.67"  
		      d="M41 149.5a77 77 0 1 1 117.93 0"  fill="none"/> 
 </svg>         

Более подробно, как делить окружности на равные части с помощью stroke-dasharray здесь и здесь

Анимация заполнения первого сектора основана на изменении значения длины черты от нуля до максимального значения 110px

<animate id="an1" attributeName="stroke-dasharray" begin="0s;an3.end" values="0 110 0 240;110 0 0 240" dur="2s" fill="freeze" />

Точно также заполняются по очереди остальные 2 сектора.

Я сделал сектора разного цвета, при необходимости можно присвоить один цвет.

<svg  xmlns="http://www.w3.org/2000/svg" height="200" width="200" viewBox="0 0 200 200" data-value="40"> 
       <path class="bg" stroke="#ccc" stroke-width="20" stroke-dasharray="110 6.67"  
		      d="M41 149.5a77 77 0 1 1 117.93 0"  fill="none"/> 
           
		  <path  class="meter" stroke="green" stroke-width="20"  
			  d="M41 149.5a77 77 0 1 1 117.93 0"   fill="none"   stroke-dasharray="0 350" stroke-dashoffset="0" >  
			  <! анимация заполнения зелёного сектора --> 
			  <animate id="an1" 
          attributeName="stroke-dasharray" 
          begin="0s;an3.end" 
          values="0 110 0 240;110 0 0 240" 
          dur="2s" 
          fill="freeze" />   
		  </path>    
                   <! анимация заполнения жёлтого сектора -->		   
			  <path  class="meter" stroke="gold" stroke-width="20"  
			    d="M41 149.5a77 77 0 1 1 117.93 0"   fill="none"   stroke-dasharray="0 350" stroke-dashoffset="0" > 
			  <animate id="an2" 
           attributeName="stroke-dasharray" 
           begin="an1.end"  
			     values="0 116.67  0 110  0 116.67;0 116.67  110 0  0 116.67" 
           dur="2s" 
           fill="freeze" />   
			</path>    
			       <! анимация заполнения красного сектора --> 
			   <path  class="meter" stroke="red" stroke-width="20"  
			      d="M41 149.5a77 77 0 1 1 117.93 0"   fill="none"   stroke-dasharray="0 350" stroke-dashoffset="0" > 
			     <animate id="an3" 
              attributeName="stroke-dasharray" 
              begin="an2.end" 
              values="0 116.67 0 116.67 0 116.67;0 116.67  0 116.67 110 0" 
              dur="2s" 
              fill="freeze" />   
			</path>  
 </svg>

Анимация стрелки указателя

<animateTransform id="an_arrow" attributeName="transform" type="rotate" values="-10 100 100;255 100 100" dur="6s" repeatCount="indefinite" fill="freeze" />

<svg  xmlns="http://www.w3.org/2000/svg" height="200" width="200" viewBox="0 0 200 200" data-value="40"> 
       <path class="bg" stroke="#ccc" stroke-width="20" stroke-dasharray="110 6.67"  
		      d="M41 149.5a77 77 0 1 1 117.93 0"  fill="none"/> 
           
		  <path  class="meter" stroke="green" stroke-width="20"  
			  d="M41 149.5a77 77 0 1 1 117.93 0"   fill="none"   stroke-dasharray="0 350" stroke-dashoffset="0" >  
			  <! анимация заполнения зелёного сектора --> 
			  <animate id="an1" 
          attributeName="stroke-dasharray" 
          begin="0s;an3.end" 
          values="0 110 0 240;110 0 0 240" 
          dur="2s" 
          fill="freeze" />   
		  </path>    
                   <! анимация заполнения жёлтого сектора -->		   
			  <path  class="meter" stroke="gold" stroke-width="20"  
			    d="M41 149.5a77 77 0 1 1 117.93 0"   fill="none"   stroke-dasharray="0 350" stroke-dashoffset="0" > 
			  <animate id="an2" 
        attributeName="stroke-dasharray" 
        begin="an1.end" 
        values="0 116.67  0 110  0 116.67;0 116.67  110 0  0 116.67" dur="2s" 
        fill="freeze" />   
			</path>    
			       <! анимация заполнения красного сектора --> 
			   <path  class="meter" stroke="red" stroke-width="20"  
			      d="M41 149.5a77 77 0 1 1 117.93 0"   fill="none"   stroke-dasharray="0 350" stroke-dashoffset="0" > 
			     <animate id="an3" 
           attributeName="stroke-dasharray" 
           begin="an2.end" 
           values="0 116.67 0 116.67 0 116.67;0 116.67  0 116.67 110 0" 
           dur="2s" 
           fill="freeze" />   
			</path>  
			 
			<circle cx="100" cy="100" r="50" fill="none" stroke-width="4" stroke="silver" /> 
			 <path d="m62.4 134.1c0 2.8 13.6 7.4-2.6 5-2.7-0.4-10.7-0.2-10.7-0.2 0 0 4-13.1 4.1-19.1 0.1-6 9.2 11.5 9.2 14.3z" fill="silver"> 
			 <animateTransform id="an_arrow" 
       attributeName="transform" 
       type="rotate" 
       values="-10 100 100;255 100 100" 
       dur="6s" 
       repeatCount="indefinite" 
       fill="freeze" /> 
			 </path> 
			 <circle cx="100" cy="100" r="3" fill="#8E8E8E"  /> 
        
	   </svg>

Ссылки на связанные топики c заполнением прогрессбара и выводом процентов:

Круговой прогресс бар

Круговой процентный прогресс бар

Круглый векторный индикатор прогресса

Практические примеры применения масок svg

Answer 3

Взяв за основу ответы Stranger in the Q и Alexander_TT, получил итог, который работает как необходимо в условиях моего проекта.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
<div class="radial"> 
        <svg  xmlns="http://www.w3.org/2000/svg" height="200" width="200" viewBox="0 0 200 200" data-value="40"> 
            <defs> 
                <mask id="mask1"> 
                    <rect width="100%" height="100%" fill="white"/> 
                    <path stroke="black" stroke-width="2" d="M100 100 l100 -100"/>  
                    <path stroke="black" stroke-width="2" d="M100 100 l-100 -100"/>  
                    <path fill="black" d="M100 100 l-115 100 l115 100 l115 -100z"/>     
                </mask> 
                <mask id="mask2"> 
                    <rect width="100%" height="100%" fill="white"/> 
                    <path stroke="black" stroke-width="5" d="M100 100 l100 -100"/>  
                    <path stroke="black" stroke-width="5" d="M100 100 l-100 -100"/>   
                </mask> 
            </defs> 
            <circle mask="url(#mask1)" fill="none" stroke-width="25" stroke="#bfb09d" r="77" cx="100" cy="100"/> 
            <path mask="url(#mask2)" fill="none" stroke-width="22" stroke="#FFF" d="M41 149.5a77 77 0 1 1 117.93 0"/> 
            <path mask="url(#mask2)" fill="none" stroke-width="22" stroke="#D15F45" d="M41 149.5a77 77 0 1 1 117.93 0" class="meter" pathLength="30" stroke-dasharray="22, 999"> 
                <animate id="an1" attributeName="stroke-dasharray" from="0 0" to="22 999" dur="1s" repeatCount="0" fill="freeze" /> 
            </path>   
            <circle cx="100" cy="100" r="50" fill="none" stroke-width="4" stroke="#bfb09d" class="arrow-circle"/> 
            <path d="m62.4 134.1c0 2.8 13.6 7.4-2.6 5-2.7-0.4-10.7-0.2-10.7-0.2 0 0 4-13.1 4.1-19.1 0.1-6 9.2 11.5 9.2 14.3z" fill="#bfb09d"> 
                <animateTransform id="an_arrow" attributeName="transform" type="rotate" from="-10 100 100" to="186 100 100" dur="1s" repeatCount="0" fill="freeze" /> 
            </path> 
            <text font-size="36px" x="100" y="100" fill="#D15F45" text-anchor="middle" alignment-baseline="central">22</text> 
        </svg> 
    </div>

Данные для атрибутов(в примере хардкодом заданы pathLength = 30 и currentVal = 22).

READ ALSO
js проверка на куки

js проверка на куки

Хотел проверить с помощью js наличие куки на страничкепри правильном вводе данных в логине и пароле - куки создаются без проблем, но скрипт...

100
Vue router in select

Vue router in select

Решил поэкспериментировать с Vue, прочитал-узнал, что есть роутинг, решил попробовать, но, к сожалению, на select он не работаетЗадача была такая,...

199
javascript не хочет выполнять функцию

javascript не хочет выполнять функцию

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

107
Проблема вывода addeventlistener

Проблема вывода addeventlistener

Проблема в выводе всего что идет после for

118