切片移出 d3.js 切片点击

Slice move out in d3.js slice click

大家好,我是 d3.js 图表的新手,我想在 3D Donut . Please go tho this link Basic Donut Chart ,I want add segment click feature into 3D Donut 中添加一些额外的东西。我试试下面的代码

var arc = d3.svg.arc()              
                .outerRadius(ir+10);

            slices.selectAll("path").on("click",function(d){

             d3.select(this)
           .attr("stroke","white")
           .transition()
           .duration(1000)
           .attr("d", arc)             
           .attr("stroke-width",6);
            })

3D Donut 但未按预期工作。请帮助我实现这一目标。工作示例代码将不胜感激。

谢谢.

在 SVG 元素中添加 onclick

var svg = d3.select("body").append("svg")
  //other code
.on('click', function(d,i){  });

这有点烦人。由于每个切片由多条路径组成,我起初认为您可以将它们分组为一个 g,然后在单击时将其转换为 "bump out"。但看起来“3D 甜甜圈”的作者依赖于绘图堆栈 "hide" 片在彼此后面。因此,我必须为每个片段分配一个唯一的 class,这样我才能找到属于一个切片的所有片段。之后你可以沿着它们的角度的中点将它们转换出来:

  function clickHandler(d, i) {
      var self = d3.select(this),
        pieces = ['innerSlice', 'topSlice', 'outerSlice', 'percent'],
        c = self.attr("class").split(" ")[1];

      if (self.attr("transform")) {

        pieces.forEach(function(d) {
          slices.select("." + d + "." + c)
            .attr("transform", null);
        });

      } else {

        var a = (d.endAngle + d.startAngle) / 2,
          x = (ir + 15) * Math.cos(a),
          y = (ir + 15) * Math.sin(a);

        pieces.forEach(function(d) {
          slices.select("." + d + "." + c)
            .attr("transform", "translate(" + [x, y] + ")");
        });

      }
    }
  }

这是 运行 代码:

<!DOCTYPE html>

<head>
  <meta charset="utf-8">
  <style>
    body {
      font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
      width: 960px;
      height: 500px;
      position: relative;
    }
    path.slice {
      stroke-width: 2px;
    }
    polyline {
      opacity: .3;
      stroke: black;
      stroke-width: 2px;
      fill: none;
    }
    svg text.percent {
      fill: white;
      text-anchor: middle;
      font-size: 12px;
    }
  </style>

  <script>
    ! function() {
      var Donut3D = {};

      function pieTop(d, rx, ry, ir) {
        if (d.endAngle - d.startAngle == 0) return "M 0 0";
        var sx = rx * Math.cos(d.startAngle),
          sy = ry * Math.sin(d.startAngle),
          ex = rx * Math.cos(d.endAngle),
          ey = ry * Math.sin(d.endAngle);

        var ret = [];
        ret.push("M", sx, sy, "A", rx, ry, "0", (d.endAngle - d.startAngle > Math.PI ? 1 : 0), "1", ex, ey, "L", ir * ex, ir * ey);
        ret.push("A", ir * rx, ir * ry, "0", (d.endAngle - d.startAngle > Math.PI ? 1 : 0), "0", ir * sx, ir * sy, "z");
        return ret.join(" ");
      }

      function pieOuter(d, rx, ry, h) {
        var startAngle = (d.startAngle > Math.PI ? Math.PI : d.startAngle);
        var endAngle = (d.endAngle > Math.PI ? Math.PI : d.endAngle);

        var sx = rx * Math.cos(startAngle),
          sy = ry * Math.sin(startAngle),
          ex = rx * Math.cos(endAngle),
          ey = ry * Math.sin(endAngle);

        var ret = [];
        ret.push("M", sx, h + sy, "A", rx, ry, "0 0 1", ex, h + ey, "L", ex, ey, "A", rx, ry, "0 0 0", sx, sy, "z");
        return ret.join(" ");
      }

      function pieInner(d, rx, ry, h, ir) {
        var startAngle = (d.startAngle < Math.PI ? Math.PI : d.startAngle);
        var endAngle = (d.endAngle < Math.PI ? Math.PI : d.endAngle);

        var sx = ir * rx * Math.cos(startAngle),
          sy = ir * ry * Math.sin(startAngle),
          ex = ir * rx * Math.cos(endAngle),
          ey = ir * ry * Math.sin(endAngle);

        var ret = [];
        ret.push("M", sx, sy, "A", ir * rx, ir * ry, "0 0 1", ex, ey, "L", ex, h + ey, "A", ir * rx, ir * ry, "0 0 0", sx, h + sy, "z");
        return ret.join(" ");
      }

      function getPercent(d) {
        return (d.endAngle - d.startAngle > 0.2 ?
          Math.round(1000 * (d.endAngle - d.startAngle) / (Math.PI * 2)) / 10 + '%' : '');
      }

      Donut3D.transition = function(id, data, rx, ry, h, ir) {
        function arcTweenInner(a) {
          var i = d3.interpolate(this._current, a);
          this._current = i(0);
          return function(t) {
            return pieInner(i(t), rx + 0.5, ry + 0.5, h, ir);
          };
        }

        function arcTweenTop(a) {
          var i = d3.interpolate(this._current, a);
          this._current = i(0);
          return function(t) {
            return pieTop(i(t), rx, ry, ir);
          };
        }

        function arcTweenOuter(a) {
          var i = d3.interpolate(this._current, a);
          this._current = i(0);
          return function(t) {
            return pieOuter(i(t), rx - .5, ry - .5, h);
          };
        }

        function textTweenX(a) {
          var i = d3.interpolate(this._current, a);
          this._current = i(0);
          return function(t) {
            return 0.6 * rx * Math.cos(0.5 * (i(t).startAngle + i(t).endAngle));
          };
        }

        function textTweenY(a) {
          var i = d3.interpolate(this._current, a);
          this._current = i(0);
          return function(t) {
            return 0.6 * rx * Math.sin(0.5 * (i(t).startAngle + i(t).endAngle));
          };
        }

        var _data = d3.layout.pie().sort(null).value(function(d) {
          return d.value;
        })(data);

        d3.select("#" + id).selectAll(".innerSlice").data(_data)
          .transition().duration(750).attrTween("d", arcTweenInner);

        d3.select("#" + id).selectAll(".topSlice").data(_data)
          .transition().duration(750).attrTween("d", arcTweenTop);

        d3.select("#" + id).selectAll(".outerSlice").data(_data)
          .transition().duration(750).attrTween("d", arcTweenOuter);

        d3.select("#" + id).selectAll(".percent").data(_data).transition().duration(750)
          .attrTween("x", textTweenX).attrTween("y", textTweenY).text(getPercent);
      }

      Donut3D.draw = function(id, data, x /*center x*/ , y /*center y*/ ,
        rx /*radius x*/ , ry /*radius y*/ , h /*height*/ , ir /*inner radius*/ ) {

        var _data = d3.layout.pie().sort(null).value(function(d) {
          return d.value;
        })(data);

        var slices = d3.select("#" + id).append("g").attr("transform", "translate(" + x + "," + y + ")")
          .attr("class", "slices");

        slices.selectAll(".innerSlice").data(_data).enter().append("path")
          .style("fill", function(d) {
            return d3.hsl(d.data.color).darker(0.7);
          })
          .attr("d", function(d) {
            return pieInner(d, rx + 0.5, ry + 0.5, h, ir);
          })
          .attr("class", function(d) {
            return "innerSlice slice-" + d.data.label;
          })
          .each(function(d) {
            this._current = d;
          })
          .on('click', clickHandler);

        slices.selectAll(".topSlice").data(_data).enter().append("path")
          .style("fill", function(d) {
            return d.data.color;
          })
          .style("stroke", function(d) {
            return d.data.color;
          })
          .attr("d", function(d) {
            return pieTop(d, rx, ry, ir);
          })
          .each(function(d) {
            this._current = d;
          })
          .attr("class", function(d) {
            return "topSlice slice-" + d.data.label;
          })
          .on('click', clickHandler);

        slices.selectAll(".outerSlice").data(_data).enter().append("path")
          .style("fill", function(d) {
            return d3.hsl(d.data.color).darker(0.7);
          })
          .attr("d", function(d) {
            return pieOuter(d, rx - .5, ry - .5, h);
          })
          .attr("class", function(d) {
            return "outerSlice slice-" + d.data.label;
          })
          .each(function(d) {
            this._current = d;
          })
          .on('click', clickHandler);

        slices.selectAll(".percent").data(_data).enter().append("text")
          .attr("class", function(d) {
            return "percent slice-" + d.data.label;
          })
          .attr("x", function(d) {
            return 0.6 * rx * Math.cos(0.5 * (d.startAngle + d.endAngle));
          })
          .attr("y", function(d) {
            return 0.6 * ry * Math.sin(0.5 * (d.startAngle + d.endAngle));
          })
          .text(getPercent).each(function(d) {
            this._current = d;
          })
          .on('click', clickHandler);

        function clickHandler(d, i) {

          var self = d3.select(this),          
              jC = self.attr("transform");
          
          slices.selectAll('path, text').each(function(d) {
            d3.select(this)
              .attr("transform", null);
          });
          
          if (jC) return;

          var self = d3.select(this),
            pieces = ['innerSlice', 'topSlice', 'outerSlice', 'percent'],
            c = self.attr("class").split(" ")[1],
            a = (d.endAngle + d.startAngle) / 2,
            x = (ir + 15) * Math.cos(a),
            y = (ir + 15) * Math.sin(a);

          pieces.forEach(function(d) {
            slices.select("." + d + "." + c)
              .attr("transform", "translate(" + [x, y] + ")");
          });

      }
    }

    this.Donut3D = Donut3D;
    }();
  </script>

</head>

<body>
  <button onClick="changeData()">Change Data</button>
  <script src="http://d3js.org/d3.v3.min.js"></script>
  <script src="Donut3D.js"></script>
  <script>
    var salesData = [{
      label: "Basic",
      color: "#3366CC"
    }, {
      label: "Plus",
      color: "#DC3912"
    }, {
      label: "Lite",
      color: "#FF9900"
    }, {
      label: "Elite",
      color: "#109618"
    }, {
      label: "Delux",
      color: "#990099"
    }];

    var svg = d3.select("body").append("svg").attr("width", 700).attr("height", 300);

    svg.append("g").attr("id", "salesDonut");
    svg.append("g").attr("id", "quotesDonut");

    Donut3D.draw("salesDonut", randomData(), 150, 150, 130, 100, 30, 0.4);
    Donut3D.draw("quotesDonut", randomData(), 450, 150, 130, 100, 30, 0);

    function changeData() {
      Donut3D.transition("salesDonut", randomData(), 130, 100, 30, 0.4);
      Donut3D.transition("quotesDonut", randomData(), 130, 100, 30, 0);
    }

    function randomData() {
      return salesData.map(function(d) {
        return {
          label: d.label,
          value: 1000 * Math.random(),
          color: d.color
        };
      });
    }
  </script>
</body>