SVG动态角度线性梯度

问题描述

我正在向线性图中的每个路径动态添加线性渐变。我希望每个渐变都将每个路径分开到中间。像这样:

image alt

我知道我可以使用gradientTransform旋转渐变,但是旋转取决于圆形图中路径的位置而不同。如何计算路径角度并相应绘制线性梯度?我也尝试过操纵线性渐变的x1,x2,y1和y2坐标,但是我不确定如何相应地操纵它们。

let data = {
  name: "demo",children: [{
      "ID": "001","Games": "PS2","children": [{
        "ID": "001-1",}]
    },{
      "ID": "002","children": [{
        "ID": "002-2",{
      "ID": "003","children": [{
        "ID": "003-1",{
      "ID": "004","children": [{
        "ID": "004-1",{
      "ID": "005","children": [{
        "ID": "005-5",}]
    }
  ]
}


let width = 500;
let height = 500;
let radius = Math.min(width,height) / 2;
let color = d3.scaleOrdinal(d3.schemeCategory20b);

let g = d3.select('svg')
  .attr('width',width)
  .attr('height',height)
  .append('g')
  .attr('transform','translate(' + width / 2 + ',' + height / 2 + ')');

// Data strucure
let partition = d3.partition()
  .size([2 * Math.PI,radius]);

// Find data root
let root = d3.hierarchy(data)
  .sum(function(d) {
    return !d.children || d.children.length === 0 ? 2 : 0
  });

// Size arcs
partition(root);
let arc = d3.arc()
  .startAngle(function(d) {
    return d.x0
  })
  .endAngle(function(d) {
    return d.x1
  })
  .innerRadius(function(d) {
    return d.y0
  })
  .outerRadius(function(d) {
    return d.y1
  });

let svg = d3.select('svg')
// Put it all together
g.selectAll('path')
  .data(root.descendants())
  .enter().append('path')
  .attr("stroke-width","5")
  .each((d,i,m) => {
    let lg = svg.append("defs")
      .append("linearGradient")
      .attr("id",`gradient${d.data.ID}`)
      .attr("gradientTransform",`rotate(${0})`)
      .attr("x1","0%")
      .attr("x2","100%")
      .attr("y1","0%")
      .attr("y2","0%")

    lg.append("stop")
      .attr("offset","50%")
      .attr("stop-color","#188F6B")

    lg.append("stop")
      .attr("offset","#3BDBAB")
  })
  .style("fill",(d) => {
    return `url(#gradient${d.data.ID})`
  })
  .attr("display",function(d) {
    return d.depth ? null : "none";
  })
  .attr("d",arc)
  .style('stroke','#fff')
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

<svg></svg>

解决方法

这是我的解决方案:我仅使用一个渐变和2条路径。结果就是您需要的两个。我将两个路径分组为一个ID,以便可以根据需要多次重复使用。

这将大大简化您的代码。请注意,我将样式和所有属性移到组中,因为它们相同。您无需重复自己。

由于该组是一个圆的1/5,这意味着该组的展开角为72º

我正在重复使用组4次,将使用元素旋转1 72度,2 72度,3 72度和4 72度。

由于图形围绕点{x:0,y:0}居中,因此您无需添加旋转中心。

<svg width="500" height="500" viewBox="-250 -250 500 500">
  <g id="theG" style="fill: url(#gradient); stroke: rgb(255,255,255);"  stroke-width="5">
    <path d="M97.96420871541218,134.8361657291579A166.66666666666666,166.66666666666666,1,-97.96420871541217,134.8361657291579L-48.982104357706085,67.41808286457895A83.33333333333333,83.33333333333333,48.98210435770609,67.41808286457895Z" ></path>
    <path  d="M146.9463130731183,202.25424859373686A250,250,-146.94631307311826,202.25424859373686L-97.96420871541217,97.96420871541218,134.8361657291579Z"></path>
</g>
  
  <use xlink:href="#theG" transform="rotate(72)" />
  <use xlink:href="#theG" transform="rotate(144)" />
  <use xlink:href="#theG" transform="rotate(216)" />
  <use xlink:href="#theG" transform="rotate(288)" />
  
  <defs>
    <linearGradient id="gradient" gradientTransform="rotate(0)" x1="0%" x2="100%" y1="0%" y2="0%">
      <stop offset="50%" stop-color="#188F6B"></stop>
      <stop offset="50%" stop-color="#3BDBAB"></stop>
    </linearGradient>
  </defs>
 
</svg>