d3.js 如何让饼图的图案平行于扇区的中心线?

How to make the patterns of a pie chart parallel to the center line of sectors in d3.js?

我创建了一个充满模式的饼图。我想让图案平行于它所在扇区的中心线。我试图改变 patternTransform 参数来旋转图案,但我不知道如何获得正确的度数。

我怎样才能做到这一点?

我当前的代码:

<script src="https://unpkg.com/d3@5.15.1/dist/d3.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<svg width="300" height="200"> </svg>
<svg>
    
    <defs>
        <pattern id="pattern0"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
      
          <rect id="rotateRect" x="5" y = "5" width = "10" height = "10" fill = "   #87CEFA "/>
      
        </pattern>
      </defs>
    
      <defs>
        <pattern id="pattern1"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
      
            <rect id="rotateRect" x="5" y = "5" width = "10" height = "10" fill = " #4169E1 "/>
      
        </pattern>
      </defs>
    
      <defs>
        <pattern id="pattern2"
                 x="0" y="0" width="20" height="20"
                 patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
      
            <rect id="rotateRect" x="5" y = "5" width = "10" height = "10" fill = " #000080 "/>
      
        </pattern>
      </defs>
    
        <rect x="0" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern0);" />   
    <rect x="100" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern1);" />   
        <rect x="200" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern2);" />   
</svg>

<script>
    var data = [{ year: '2001', value:10 },
            { year: '2002', value:30 },
            { year: '2003', value:60 },
           ]

    var svg = d3.select("svg"),
        width = svg.attr("width"),
        height = svg.attr("height"),
        radius = Math.min(width, height) / 2,
        g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    var color = d3.scaleOrdinal(['#4daf4a','#377eb8','#ff7f00','#984ea3','#e41a1c']);

    // Generate the pie
    var pie = d3.pie().value(function(d) { 
    return d.value;});

    // Generate the arcs
    var arc = d3.arc()
                .innerRadius(0)
                .outerRadius(radius);

    //Generate groups
    var arcs = g.selectAll("arc")
                .data(pie(data))
                .enter()
                .append("g")
                .attr("class", "arc")

    //Draw arc paths
    arcs.append("path")
        .attr("d", arc)
  .attr('stroke', "black")
            .attr('stroke-width', '1')
            .attr("fill", function(d,i) { return  "url(#pattern" + i +")"});
</script>
</script>

提前致谢。

由于 patternTransform 属性是在模式上定义的,因此您需要在运行时动态创建 <pattern> 元素。

d3.pie(data) returns 具有 startAngleendAngle 属性的对象数组。您可以使用它们来计算所需的旋转:

averageAngle = (d.startAngle + d.endAngle) * 90 / Math.PI

我本来希望只定义一次图案几何,然后re-use那个as a template,但不幸的是至少Firefox没有通过影子树模型实现样式继承。因此,如果我做这样的事情...

 <pattern id="patternTemplate"
          x="0" y="0" width="20" height="20"
          patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
  
     <rect id="rotateRect" x="5" y = "5" width = "10" height = "10" />
 </pattern>

 <pattern id="pattern0" href="#patternTemplate" fill="#4daf4a" />

...图案中使用的填充颜色保持黑色。

<script src="https://unpkg.com/d3@5.15.1/dist/d3.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<svg id="pie" width="300" height="200"> </svg>
<svg id="legend">
    
        <rect x="0" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern0);" />   
    <rect x="100" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern1);" />   
        <rect x="200" y="0" width="100" height="100"
        style="stroke: #000000; fill: url(#pattern2);" />   
</svg>

<script>
    var data = [{ year: '2001', value:10 },
            { year: '2002', value:30 },
            { year: '2003', value:60 },
           ]

    var svg = d3.select("svg#pie"),
        width = svg.attr("width"),
        height = svg.attr("height"),
        radius = Math.min(width, height) / 2,
        g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    var color = d3.scaleOrdinal(['#4daf4a','#377eb8','#ff7f00','#984ea3','#e41a1c']);

    // Generate the pie
    var pie = d3.pie().value(function(d) { 
    return d.value;})(data);

    // Generate the patterns
    var legend = d3.select("svg#legend"),
        defs = legend.append("defs");
    defs.selectAll("pattern")
        .data(pie)
        .enter()
        .append("pattern")
        .attr("id", (d, i) => "pattern" + i)
        .attr("patternUnits", "userSpaceOnUse")
        .attr("width", 20)
        .attr("height", 20)
        .attr("patternTransform", (d) => `rotate(${(d.startAngle + d.endAngle) * 90 / Math.PI})`)
        .style("fill", (d, i) => color(i))
             .append("rect")
             .attr("x", 5)
             .attr("y", 5)
             .attr("width", 10)
             .attr("height", 10)

    // Generate the arcs
    var arc = d3.arc()
                .innerRadius(0)
                .outerRadius(radius);

    //Generate groups
    var arcs = g.selectAll("arc")
                .data(pie)
                .enter()
                .append("g")
                .attr("class", "arc")

    //Draw arc paths
    arcs.append("path")
        .attr("d", arc)
  .attr('stroke', "black")
            .attr('stroke-width', '1')
            .attr("fill", function(d,i) { return  "url(#pattern" + i +")"});
</script>
</script>