SVG线条上的线性渐变

16

我想知道如何使linearGradient穿过(从上到下)这条线,而不是像下面的示例中渐变沿着(从左到右)这条线。

<svg xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
    <linearGradient id="e" x1="40" y1="210" x2="460" y2="210" gradientUnits="userSpaceOnUse">
        <stop stop-color="steelblue" offset="0" />
        <stop stop-color="red" offset="1" />
    </linearGradient>
</defs>
 <line x1="40" y1="210" x2="460" y2="210" stroke="url(#e)" stroke-width="30" />
</svg>

在这里输入图片描述

对于未旋转的线,改变y坐标非常有效,linearGradient现在沿着该线从上到下延伸:

<svg xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
    <linearGradient id="e" x1="40" y1="195" x2="40" y2="225" gradientUnits="userSpaceOnUse">
        <stop stop-color="steelblue" offset="0" />
        <stop stop-color="red" offset="1" />
    </linearGradient>
</defs>
 <line x1="40" y1="210" x2="460" y2="210" stroke="url(#e)" stroke-width="30"/> 
</svg>

图片描述请见链接

但是旋转时无法正常工作

<svg xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
    <linearGradient id="e" x1="40" y1="235" x2="40" y2="265" gradientUnits="userSpaceOnUse">
        <stop stop-color="steelblue" offset="0" />
        <stop stop-color="red" offset="1" />
    </linearGradient>
</defs>
 <line x1="40" y1="210" x2="460" y2="290" stroke="url(#e)" stroke-width="30"/> 
</svg>

输入图像描述

我想要的是带有线性渐变的旋转线条。像这样: 示例


现在,也许gradientTransform是旋转渐变最简单的方法。 - ashleedawg
@Alex 你能用d3解决这个问题吗?我已经试了好几天了,但是旋转似乎不正确。 - Olli
@ashleedawg 你有例子吗?我已经用d3尝试了很长时间,但似乎无法通过gradientTransform获得正确的旋转。 - Olli
@Olli,你尝试使用被接受答案中的代码了吗? - Aleksandr Ianevski
1
@AlexNevsky 我已经这样做了,非常感谢您的回复!但是我错过了一些东西,最终还是解决了,我不得不设置 gradientUnits = userSpaceOnUse,现在它运行得非常好! - Olli
2个回答

14

<svg width="600" height="200" viewBox="0 190 600 200" xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
    <linearGradient id="e" x1="40" y1="210" x2="460" y2="290" gradientUnits="userSpaceOnUse">
        <stop stop-color="steelblue" offset="0" />
        <stop stop-color="red" offset="1" />
    </linearGradient>
</defs>
 <line x1="40" y1="210" x2="460" y2="290" stroke="url(#e)" stroke-width="30"/> 
</svg>

在第一个案例中,关键是使线的x1 y1,x2 y2匹配线性渐变的x1 y1,x2 y2坐标。对于第二个案例,涉及到更多的数学知识。您需要创建一条垂直于第一条线的线,并且具有所需线宽的长度,并从其中一个点的一半宽度开始。
因此,在您的情况下(伪代码!):
步骤1:
获取方向
   dx=x2-x1;
   dy=y2-y1;

dx,dy现在是从点1到点2的方向。

步骤2:

通过将dx和dy除以线段长度来将方向标准化为长度为1。

    len=Math.sqrt(dx*dx+dy*dy);
    dx=dx/len;
    dy=dy/len;

当然,如果长度为0是不起作用的,但因为你给了我坐标,所以现在我不必担心这个问题。

步骤3:

找到垂直方向。这其实非常简单,但从逻辑上讲可能有两个方向。我只选择其中一个。

    temp=dx;
    dx=-dy;
    dy=temp;

如果您想要另一个方向,只需否定dx和dy。在此过程之后。
    dx=-dx;
    dy=-dy;

dx,dy现在保持垂直方向。

步骤4:

将dx和dy乘以所需的线宽,例如30。我称其为w。

    dx=w*dx;
    dy=w*dy;

步骤5:

要找到梯度的p1和p2,请从该线中获取p1,并加上或减去dx的一半。

    gradient_x1=x1+dx*0.5;
    gradient_y1=y1+dx*0.5;
    gradient_x2=x1-dx*0.5;
    gradient_y2=y1-dx*0.5;

现在您可以重新建立您的线路。为了让您更好地理解,我已经输入了您的数值并完成了整个过程,结果如下:
    Your case: (x1="40" y1="210" x2="460" y2="290" w=30)

    ## STEP1 ##
    dx: 420 dy:80

    ## STEP2 ##
    dx: 0.9823385664224747 dy:0.1871121078899952

    ## STEP3 ##
    dx: -0.1871121078899952 dy:0.9823385664224747

    ## STEP4 ##
    dx: -5.613363236699856 dy:29.47015699267424

    ## STEP5 ##
    gradient_x1=37.19331838165007
    gradient_y1=224.7350784963371

    gradient_x2=42.80668161834993
    gradient_y2=195.2649215036629

所以将其插入到您的示例中:

<svg width="600" height="200" viewBox="0 190 600 200" xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
    <linearGradient id="e" x1="37.19331838165007" y1="224.7350784963371" x2="42.80668161834993" y2="195.2649215036629" gradientUnits="userSpaceOnUse">
        <stop stop-color="steelblue" offset="0" />
        <stop stop-color="red" offset="1" />
    </linearGradient>
</defs>
 <line x1="40" y1="210" x2="460" y2="290" stroke="url(#e)" stroke-width="30"/> 
</svg>

总结

幸运的是,我们不需要进行所有这些计算,因为我们有计算机并且可以通过JavaScript轻松地操作SVG元素。 如果它们有一个id,用JavaScript访问svg元素会更方便。 你的渐变具有id="e",让我们给你的线条一个id="l"。

之后只需在页面中插入一小段脚本, 从线条("l")获取x1 y1,x2 y2,计算出所有内容并将其放入渐变("e"),然后你就会得到这样的结果:

<svg width="600" height="200" viewBox="0 190 600 200" xmlns="http://www.w3.org/2000/svg" version="1">
  <defs>
    <linearGradient id="e" x1="0" y1="0" x2="1" y2="1" gradientUnits="userSpaceOnUse">
    <!-- put the coords on 0,0 1,1 it really doesn't matter, they will be calculated-->
      <stop stop-color="steelblue" offset="0" />
      <stop stop-color="red" offset="1" />
  </linearGradient>
  </defs>
  <line id="l" x1="40" y1="270" x2="450" y2="210" stroke="url(#e)" stroke-width="30"/> 
</svg>

<script>
  var line=document.getElementById("l");
  var x1=parseFloat(l.getAttribute("x1"));
  var y1=parseFloat(l.getAttribute("y1"));
  var x2=parseFloat(l.getAttribute("x2"));
  var y2=parseFloat(l.getAttribute("y2"));
  var w=parseFloat(l.getAttribute("stroke-width"));

  // step 1
  var dx=x2-x1;
  var dy=y2-y1;

  // step 2
  len=Math.sqrt(dx*dx+dy*dy);
  dx=dx/len;
  dy=dy/len;

  // step 3
  var temp=dx;
  dx=-dy;
  dy=temp;
  
  //step 4
  dx=w*dx;
  dy=w*dy;

  //step 5
  var gradient_x1=x1+dx*0.5;
  var gradient_y1=y1+dy*0.5;
  var gradient_x2=x1-dx*0.5;
  var gradient_y2=y1-dy*0.5;

  document.getElementById("e");
  e.setAttribute("x1",gradient_x1);
  e.setAttribute("y1",gradient_y1);
  e.setAttribute("x2",gradient_x2);
  e.setAttribute("y2",gradient_y2);
</script>

您可以自由编辑线条的起点和终点,甚至可以调整笔画宽度,脚本将实时修复您的渐变。为了向您“证明”这一点,这正是我所做的。 :) 希望这有所帮助。


5
你是不是想旋转渐变?那么可以使用gradientTransform
<svg xmlns="http://www.w3.org/2000/svg" version="1">
<defs>
    <linearGradient id="e" x1="40" y1="210" x2="460" y2="210" gradientUnits="userSpaceOnUse" gradientTransform="rotate(90)">
        <stop stop-color="steelblue" offset="0" />
        <stop stop-color="red" offset="1" />
    </linearGradient>
</defs>
 <line x1="40" y1="210" x2="460" y2="210" stroke="url(#e)" stroke-width="30" />
</svg>

1
一旦你使用了y坐标解决方案,你可以使用CSS的transform来旋转整个SVG,以获得你想要的结果。 - jbutler483
2
问题在于我正在学习d3.js并可视化一个力导向图。线的x1、x2、y1、y2坐标不断变化。是否有可能相对于x1、x2、y1和y2坐标在该线上实现这样的渐变? - Aleksandr Ianevski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接