Как закрасить путь по линии?

202
19 апреля 2017, 12:08

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

html, body, svg { 
  height: 100%; 
  width: 100%; 
  display: block; 
  margin: 0; 
} 
 
path { 
  fill: none; 
  stroke-width: 3px; 
  stroke: url(#gradient); 
  stroke-linecap: round; 
}
<svg viewBox="-2 -2 36 36"> 
  <defs> 
    <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%"> 
      <stop offset="0%" stop-color="red"/> 
      <stop offset="100%" stop-color="blue"/> 
    </linearGradient> 
  </defs> 
 
  <path d="M 16 0 a 16 16 0 1 1 0 32 a 8 8 0 1 1 0 -16" /> 
</svg>

Answer 1

На просторах интернета нашелся такой пример с использованием D3.js.

Примерный перевод:

Этот пример демонстрирует способ создания градиента, который идет вдоль линии. Такая техника иногда используется для определения направленности вдоль искривленного края, например, иерархического связывания краев.

Для начала возьмем любой SVG path-элемент и равномерно разделим точки вдоль пути, используя getPointAtLength (этот метод также может быть использован для анимации path). Затем для каждого сегмента между соседними точками, рассчитаем угловое соединение при помощи line-line intersection. И в конце заполним каждый сегмент путем интерполяции начального и конечного цветов, в данном случае от зеленого к красному, используя нормализованную длину t вдоль path. Несмотря на то, что каждый сегмент представляет собой постоянный цвет, линия состоит из множества сегментов, благодаря чему возникает эффект градиента.

В данном примере использован тонкий штрих в добавок к заполнению сегментов. Это позволяет избежать артефактов при сглаживании в большинстве современных браузеров без применения full-scene сглаживания.

Источник:

https://bl.ocks.org/mbostock/4163057

var color = d3.interpolateRainbow; 
 
var path = d3.select("path").remove(); 
 
d3.select("svg").selectAll("path") 
    .data(quads(samples(path.node(), 8))) 
  .enter().append("path") 
    .style("fill", function(d) { return color(d.t); }) 
    .style("stroke", function(d) { return color(d.t); }) 
    .attr("d", function(d) { return lineJoin(d[0], d[1], d[2], d[3], 32); }); 
 
// Разделим SVG path равномерно с определенной точностью. 
function samples(path, precision) { 
  var n = path.getTotalLength(), t = [0], i = 0, dt = precision; 
  while ((i += dt) < n) t.push(i); 
  t.push(n); 
  return t.map(function(t) { 
    var p = path.getPointAtLength(t), a = [p.x, p.y]; 
    a.t = t / n; 
    return a; 
  }); 
} 
 
// Рассчитаем четверки соседних точек [p0, p1, p2, p3]. 
function quads(points) { 
  return d3.range(points.length - 1).map(function(i) { 
    var a = [points[i - 1], points[i], points[i + 1], points[i + 2]]; 
    a.t = (points[i].t + points[i + 1].t) / 2; 
    return a; 
  }); 
} 
 
// Рассчитаем внещний штрих для сегмента p12. 
function lineJoin(p0, p1, p2, p3, width) { 
  var u12 = perp(p1, p2), 
      r = width / 2, 
      a = [p1[0] + u12[0] * r, p1[1] + u12[1] * r], 
      b = [p2[0] + u12[0] * r, p2[1] + u12[1] * r], 
      c = [p2[0] - u12[0] * r, p2[1] - u12[1] * r], 
      d = [p1[0] - u12[0] * r, p1[1] - u12[1] * r]; 
 
  if (p0) { // обрежем ad и dc используя среднее число от u01 и u12 
    var u01 = perp(p0, p1), e = [p1[0] + u01[0] + u12[0], p1[1] + u01[1] + u12[1]]; 
    a = lineIntersect(p1, e, a, b); 
    d = lineIntersect(p1, e, d, c); 
  } 
 
  if (p3) { // обрежем ab и dc используя среднее число от u12 и u23 
    var u23 = perp(p2, p3), e = [p2[0] + u23[0] + u12[0], p2[1] + u23[1] + u12[1]]; 
    b = lineIntersect(p2, e, a, b); 
    c = lineIntersect(p2, e, d, c); 
  } 
 
  return "M" + a + "L" + b + " " + c + " " + d + "Z"; 
} 
 
// Рассчитаем пересечение двух бесконечных линий ab и cd. 
function lineIntersect(a, b, c, d) { 
  var x1 = c[0], x3 = a[0], x21 = d[0] - x1, x43 = b[0] - x3, 
      y1 = c[1], y3 = a[1], y21 = d[1] - y1, y43 = b[1] - y3, 
      ua = (x43 * (y1 - y3) - y43 * (x1 - x3)) / (y43 * x21 - x43 * y21); 
  return [x1 + ua * x21, y1 + ua * y21]; 
} 
 
// Вычислим единичный вектор перпендикулярный к p01. 
function perp(p0, p1) { 
  var u01x = p0[1] - p1[1], u01y = p1[0] - p0[0], 
      u01d = Math.sqrt(u01x * u01x + u01y * u01y); 
  return [u01x / u01d, u01y / u01d]; 
}
html, body, svg { 
  height: 100%; 
  width: 100%; 
  display: block; 
  margin: 0; 
} 
 
path { 
  fill: none; 
  stroke-width: 3px; 
  stroke-linecap: round; 
}
<script src="https://d3js.org/d3.v4.min.js"></script> 
 
<svg width="960" height="500"> 
  <path d=" 
      M86,388 
      L203,330 
      C320,272,554,156,673.8333333333334,165.83333333333334 
      C793.6666666666666,175.66666666666666,799.3333333333334,311.3333333333333,683.5,316.6666666666667 
      C567.6666666666666,322,330.3333333333333,197,211.66666666666666,134.5 
      L93,72"></path> 
</svg>

Answer 2

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

<path  id="path2" d="M 32 0 a 8 8 0 1 1 0 16" />
      <path  id="path1" d="M 32 0 a 8 8 0 1 1 0 -16" />

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink" 
     width="30" height="30"   viewBox="0 -30 60 60" > 
 
  <defs> 
    <linearGradient id="grad1" x1="0%" y1="0%" x2="10%" y2="100%"> 
      <stop offset="0%" stop-color="red" stop-opacity="1"/> 
       
	  <stop offset="100%" stop-color="#FFE4E1" stop-opacity="1"/> 
    </linearGradient> 
	 
	<linearGradient id="grad2" x1="0%" y1="0%" x2="0%" y2="100%"> 
      <stop offset="0%" stop-color="#FFE4E1" />  
	  <stop offset="100%" stop-color="blue"/>  
       
    </linearGradient> 
  </defs> 
   
  <style> 
  html, body, svg { 
  height: 100%; 
  width: 100%; 
  display: block; 
  margin: 0; 
} 
 
#path1 { 
  fill: none; 
  stroke-width: 3px; 
  stroke: url(#grad1); 
  stroke-linecap: round; 
} 
 
#path2 { 
  fill: none; 
  stroke-width: 3px; 
  stroke: url(#grad2); 
  stroke-linecap: round; 
} 
  </style> 
   
  <g transform="skewX(0) skewY(0)"> 
  <path  id="path2" d="M 32 0 a 8 8 0 1 1 0 16" /> 
  <path  id="path1" d="M 32 0 a 8 8 0 1 1 0 -16" /> 
  </g> 
</svg>

Благодарность @Grundy, за подсказку о последующем трансформировании.

Ниже пример с трансформацией - скручивания вокруг осей Xи Y

 <g transform="skewX(40) skewY(10)">   

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink" 
     width="30" height="30"   viewBox="0 -30 60 60" > 
 
  <defs> 
    <linearGradient id="grad1" x1="0%" y1="0%" x2="10%" y2="100%"> 
      <stop offset="0%" stop-color="red" stop-opacity="1"/> 
       
	  <stop offset="100%" stop-color="#FFE4E1" stop-opacity="1"/> 
    </linearGradient> 
	 
	<linearGradient id="grad2" x1="0%" y1="0%" x2="0%" y2="100%"> 
      <stop offset="0%" stop-color="#FFE4E1" />  
	  <stop offset="100%" stop-color="blue"/>  
       
    </linearGradient> 
  </defs> 
   
  <style> 
  html, body, svg { 
  height: 100%; 
  width: 100%; 
  display: block; 
  margin: 0; 
} 
 
#path1 { 
  fill: none; 
  stroke-width: 3px; 
  stroke: url(#grad1); 
  stroke-linecap: round; 
} 
 
#path2 { 
  fill: none; 
  stroke-width: 3px; 
  stroke: url(#grad2); 
  stroke-linecap: round; 
} 
  </style> 
   
  <g transform="skewX(40) skewY(10)"> 
  <path  id="path2" d="M 32 0 a 8 8 0 1 1 0 16" /> 
  <path  id="path1" d="M 32 0 a 8 8 0 1 1 0 -16" /> 
  </g> 
</svg>

Пример с анимцией skewX:

<svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
     xmlns:xlink="http://www.w3.org/1999/xlink" 
     width="30" height="30"   viewBox="0 -30 60 60" > 
 
  <defs> 
    <linearGradient id="grad1" x1="0%" y1="0%" x2="10%" y2="100%"> 
      <stop offset="0%" stop-color="red" stop-opacity="1"/> 
       
	  <stop offset="100%" stop-color="#FFE4E1" stop-opacity="1"/> 
    </linearGradient> 
	 
	<linearGradient id="grad2" x1="0%" y1="0%" x2="0%" y2="100%"> 
      <stop offset="0%" stop-color="#FFE4E1" />  
	  <stop offset="100%" stop-color="blue"/>  
       
    </linearGradient> 
  </defs> 
   
  <style> 
  html, body, svg { 
  height: 100%; 
  width: 100%; 
  display: block; 
  margin: 0; 
} 
 
#path1 { 
  fill: none; 
  stroke-width: 3px; 
  stroke: url(#grad1); 
  stroke-linecap: round; 
} 
 
#path2 { 
  fill: none; 
  stroke-width: 3px; 
  stroke: url(#grad2); 
  stroke-linecap: round; 
} 
  </style> 
   
  <g transform="skewX(0) skewY(0)"> 
  <path  id="path2" d="M 32 0 a 8 8 0 1 1 0 16" /> 
  <path  id="path1" d="M 32 0 a 8 8 0 1 1 0 -16" /> 
   
   <animateTransform begin="0s" attributeName="transform" type="skewX" dur="15s" repeatCount="2" values="0;60;0;-60;0" fill="freeze"/> 
  </g> 
</svg>

READ ALSO
Не могу задать правильное поведение hover

Не могу задать правильное поведение hover

Всем приветПочти сделал рейтинг со звездами, как вдруг проблема с наведением по ним

233
Запретить увеличение кнопок

Запретить увеличение кнопок

Есть фиксированные кнопки при просмотре с мобильного, как запретить увеличение при зумировании?

233
Не Могу импортировать дамп на PhPMyAdmin

Не Могу импортировать дамп на PhPMyAdmin

Во время импорта на прямую с PhpMyAdmin выдает следущую ошибку

213
Поиск в базе mysql по массиву в колонке json (mysql 5.7)

Поиск в базе mysql по массиву в колонке json (mysql 5.7)

Есть таблица данных mysql 57, в которой одно из полей в формате JSON, допустим, такого вида:

188