过渡d3.radar图表

时间:2016-06-08 16:46:47

标签: javascript d3.js charts

我正在试图弄清楚如何在Alangrafu的雷达图表上添加过渡,因此不是用新数据刷新整个图表,而只是移动点。

或者,如果这太复杂,可能会产生类似Highcharts提供的影响。

但是,它比我最初认为的要困难得多。 (我是一个相当低级的程序员!)

我希望有人可以提供有关如何做到这一点的任何提示/见解? (如果可以不对原始代码进行大量重写?)

我认为这就像添加区域图调用的​​过渡一样容易(见下文),遗憾的是我错了。

http://jsfiddle.net/j8g8s6Lz/

我会非常感谢任何帮助!

谢谢

g.selectAll(".area")
                .data([dataValues])
                .enter()
                .transition()
                    .duration(750)
                    .ease(cubic)
                .append("polygon")
                .attr("class", "radar-chart-series_"+series)
                .style("stroke-width", strokeWidthPolygon)
                .style("stroke", cfg.color(series))
                .attr("points",function(d) {
                    var str="";
                    for (var pti=0;pti<d.length;pti++) {
                        str=str+d[pti][0]+","+d[pti][1]+" ";
                    }
                    return str;
                })
                .style("fill", function(j, i) {
                    return cfg.color(series);
                })

1 个答案:

答案 0 :(得分:2)

这是对你所追求的事情的真正快速破解:

<!DOCTYPE html>
<html>

  <head>
    <script data-require="d3@3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
    <style>
    .big.radarChart {
      width: 500px;
      height: 400px;
      margin: 20px;
    }
    
    #chart-radar1 {
      margin-bottom: 60px;
    }
  </style>
</head>

<body>
  <button id="update">Update</button>
  <div class="big radarChart" id="chart-radar" ></div>
  <script>
    var wMaior = 400;
    var wMenor = 200;

    var colorscale = d3.scale.category10();
    var legendOptions = ['Legend 1'];
    var size = 2;

    if (size > 0) {
      var json = [
        [{
          "axis": "A",
          "value": 6
        }, {
          "axis": "B",
          "value": 4
        }, {
          "axis": "C",
          "value": 6
        }, {
          "axis": "D",
          "value": 5.5
        }, {
          "axis": "E",
          "value": 8
        }, {
          "axis": "F",
          "value": 7
        }, {
          "axis": "G",
          "value": 9
        }, {
          "axis": "H",
          "value": 10
        }, {
          "axis": "I",
          "value": 3.5
        }]
      ];
    }

    function drawRadarCharts() {
      drawRadarChart('#chart-radar', wMaior, wMaior);
    };

    function drawRadarChart(divId, w, h) {
      var textSizeLevels = "10px !important";
      var textSizeTooltip = "13px !important";
      var textSizeLegend = "11px !important";
      var circleSize = 5;
      var strokeWidthPolygon = "2px";

      var RadarChart = {
        draw: function(id, data, options) {
          var cfg = {
            radius: circleSize,
            w: w,
            h: h,
            factor: 1,
            factorLegend: .85,
            levels: 3,
            maxValue: 0,
            radians: 2 * Math.PI,
            opacityArea: 0.001,
            ToRight: 5,
            TranslateX: 80,
            TranslateY: 30,
            ExtraWidthX: 10,
            ExtraWidthY: 100,
            color: d3.scale.category10()
          };

          if ('undefined' !== typeof options) {
            for (var i in options) {
              if ('undefined' !== typeof options[i]) {
                cfg[i] = options[i];
              }
            }
          }

          cfg.maxValue = Math.max(cfg.maxValue, d3.max(data, function(i) {
            return d3.max(i.map(function(o) {
              return o.value;
            }));
          }));
          var allAxis = (data[0].map(function(i, j) {
            return i.axis;
          }));
          var total = allAxis.length;
          var radius = cfg.factor * Math.min(cfg.w / 2, cfg.h / 2);
          
          var svg = d3.select(id).select('svg'),
              polyPoints = null;
          if (svg.node()){
             polyPoints = svg.select("polygon").attr("points");
             svg.remove(); 
          }

          var g = d3.select(id)
            .append("svg")
            .attr("width", cfg.w + cfg.ExtraWidthX)
            .attr("height", cfg.h + cfg.ExtraWidthY)
            .attr("class", "graph-svg-component")
            .append("g")
            .attr("transform", "translate(" + cfg.TranslateX + "," + cfg.TranslateY + ")");

          var tooltip;

          // Circular segments
          for (var j = 0; j < cfg.levels - 1; j++) {
            var levelFactor = cfg.factor * radius * ((j + 1) / cfg.levels);
            g.selectAll(".levels")
              .data(allAxis)
              .enter()
              .append("svg:line")
              .attr("x1", function(d, i) {
                return levelFactor * (1 - cfg.factor * Math.sin(i * cfg.radians / total));
              })
              .attr("y1", function(d, i) {
                return levelFactor * (1 - cfg.factor * Math.cos(i * cfg.radians / total));
              })
              .attr("x2", function(d, i) {
                return levelFactor * (1 - cfg.factor * Math.sin((i + 1) * cfg.radians / total));
              })
              .attr("y2", function(d, i) {
                return levelFactor * (1 - cfg.factor * Math.cos((i + 1) * cfg.radians / total));
              })
              .attr("class", "line")

            .style("stroke", "grey")
              .style("stroke-opacity", "0.75")
              .style("stroke-width", "0.3px")
              .attr("transform", "translate(" + (cfg.w / 2 - levelFactor) + ", " + (cfg.h / 2 - levelFactor) + ")");
          }

          // Text indicating at what % each level is
          for (var j = 0; j < cfg.levels; j++) {
            var levelFactor = cfg.factor * radius * ((j + 1) / cfg.levels);
            g.selectAll(".levels")
              .data([1]) //dummy data
              .enter()
              .append("svg:text")
              .attr("x", function(d) {
                return levelFactor * (1 - cfg.factor * Math.sin(0));
              })
              .attr("y", function(d) {
                return levelFactor * (1 - cfg.factor * Math.cos(0));
              })
              .attr("class", "legend")
              .style("font-family", "sans-serif")
              .style("font-size", textSizeLevels)
              .attr("transform", "translate(" + (cfg.w / 2 - levelFactor + cfg.ToRight) + ", " + (cfg.h / 2 - levelFactor) + ")")
              .attr("fill", "#737373")
              .text((j + 1) * cfg.maxValue / cfg.levels);
          }

          series = 0;

          var axis = g.selectAll(".axis")
            .data(allAxis)
            .enter()
            .append("g")
            .attr("class", axis);

          axis.append("line")
            .attr("x1", cfg.w / 2)
            .attr("y1", cfg.h / 2)
            .attr("x2", function(d, i) {
              return cfg.w / 2 * (1 - cfg.factor * Math.sin(i * cfg.radians / total));
            })
            .attr("y2", function(d, i) {
              return cfg.h / 2 * (1 - cfg.factor * Math.cos(i * cfg.radians / total));
            })
            .attr("class", "line")
            .style("stroke", "grey")
            .style("stroke-width", "1px");

          axis.append("text")
            .attr("class", "legend")
            .text(function(d) {
              return d;
            })
            .style("font-family", "sans-serif")
            .style("font-size", textSizeLegend)
            .attr("text-anchor", "middle")
            .attr("dy", "1.5em")
            .attr("transform", function(d, i) {
              return "translate(0, -10)";
            })
            .attr("x", function(d, i) {
              return cfg.w / 2 * (1 - cfg.factorLegend * Math.sin(i * cfg.radians / total)) - 60 * Math.sin(i * cfg.radians / total);
            })
            .attr("y", function(d, i) {
              return cfg.h / 2 * (1 - Math.cos(i * cfg.radians / total)) - 20 * Math.cos(i * cfg.radians / total);
            });

          data.forEach(function(y, x) {
            dataValues = [];
            g.selectAll(".nodes")
              .data(y, function(j, i) {
                dataValues.push([
                  cfg.w / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.sin(i * cfg.radians / total)),
                  cfg.h / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.cos(i * cfg.radians / total))
                ]);
              });
            dataValues.push(dataValues[0]);
            g.selectAll(".area")
              .data([dataValues])
              .enter()
              .append("polygon")
              .attr("points", function(d){
                if (polyPoints)
                  return polyPoints;
                else
                  return d3.range(d.length).map(function(){
                    return (cfg.w / 2) + "," + (cfg.h / 2)
                  }).join(" ");
              })
              .attr("class", "radar-chart-series_" + series)
              .style("stroke-width", strokeWidthPolygon)
              .style("stroke", cfg.color(series))
              .style("fill-opacity", cfg.opacityArea)
              .on('mouseover', function(d) {
                z = "polygon." + d3.select(this).attr("class");
                g.selectAll("polygon")
                  .transition(200)
                  .style("fill-opacity", 0.1);
                g.selectAll(z)
                  .transition(200)
                  .style("fill-opacity", 0.7);
              })
              .on('mouseout', function() {
                g.selectAll("polygon")
                  .transition(200)
                  .style("fill-opacity", cfg.opacityArea);
              })
              .transition()
              .duration(2000)
              .attr("points", function(d) {
                var str = "";
                for (var pti = 0; pti < d.length; pti++) {
                  str = str + d[pti][0] + "," + d[pti][1] + " ";
                }
                return str;
              })
              .style("fill", function(j, i) {
                return cfg.color(series);
              })

            series++;
          });

          series = 0;

          data.forEach(function(y, x) {
            var c = g.selectAll(".nodes")
              .data(y).enter()
              .append("svg:circle")
              .attr("class", "radar-chart-series_" + series)
              .attr('r', cfg.radius)
              .attr("alt", function(j) {
                return Math.max(j.value, 0);
              })
              .attr("cx", function(j, i) {
                dataValues.push([
                  cfg.w / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.sin(i * cfg.radians / total)),
                  cfg.h / 2 * (1 - (parseFloat(Math.max(j.value, 0)) / cfg.maxValue) * cfg.factor * Math.cos(i * cfg.radians / total))
                ]);
                return cfg.w / 2 * (1 - (Math.max(j.value, 0) / cfg.maxValue) * cfg.factor * Math.sin(i * cfg.radians / total));
              })
              .attr("cy", function(j, i) {
                return cfg.h / 2 * (1 - (Math.max(j.value, 0) / cfg.maxValue) * cfg.factor * Math.cos(i * cfg.radians / total));
              })
              .attr("data-id", function(j) {
                return j.axis;
              })
              .style("fill", cfg.color(series))
              .style("fill-opacity", 0)
              .on('mouseover', function(d) {
                newX = parseFloat(d3.select(this).attr('cx')) - 10;
                newY = parseFloat(d3.select(this).attr('cy')) - 5;

                tooltip.attr('x', newX)
                  .attr('y', newY)
                  .text(d.value)
                  .transition(200)
                  .style('opacity', 1);

                z = "polygon." + d3.select(this).attr("class");
                g.selectAll("polygon")
                  .transition(200)
                  .style("fill-opacity", 0.1);
                g.selectAll(z)
                  .transition(200)
                  .style("fill-opacity", 0.7);
              })
              .on('mouseout', function() {
                tooltip.transition(200)
                  .style('opacity', 0);
                g.selectAll("polygon")
                  .transition(200)
                  .style("fill-opacity", cfg.opacityArea);
              });
              
            c.transition()
              .delay(1750)
              .duration(100)
              .style("fill-opacity", 0.9);
              
              c.append("svg:title")
              .text(function(j) {
                return Math.max(j.value, 0);
              });
              

            series++;
          });

          //Tooltip
          tooltip = g.append('text')
            .style('opacity', 0)
            .style('font-family', 'sans-serif')
            .style('font-size', textSizeTooltip);
        }
      };

      // Options for the Radar chart, other than default
      var myOptions = {
        w: w,
        h: h,
        ExtraWidthX: 180,
        labelScale: 0.7,
        levels: 5,
        levelScale: 0.85,
        facetPaddingScale: 1.9,
        maxValue: 0.6,
        showAxes: true,
        showAxesLabels: true,
        showLegend: true,
        showLevels: true,
        showLevelsLabels: false,
        showPolygons: true,
        showVertices: true
      };

      RadarChart.draw(divId, json, myOptions);

      ////////////////////////////////////////////
      /////////// Initiate legend ////////////////
      ////////////////////////////////////////////

      var svg = d3.select('#chart-radar')
        .selectAll('svg')
        .append('svg')
        .attr("width", w + 300)
        .attr("height", h)
        .style("font-size", textSizeLegend);

      // Initiate Legend
      var legend = svg.append("g")
        .attr("class", "legend")
        .attr("height", 100)
        .attr("width", 200)
        .attr('transform', 'translate(90,20)');

      // Create colour squares
      legend.selectAll('rect')
        .data(legendOptions)
        .enter()
        .append("rect")
        .attr("x", w - 8)
        .attr("y", function(d, i) {
          return i * 20;
        })
        .attr("width", 10)
        .attr("height", 10)
        .style("fill", function(d, i) {
          return colorscale(i);
        });

      // Create text next to squares
      legend.selectAll('text')
        .data(legendOptions)
        .enter()
        .append("text")
        .attr("x", w + 3)
        .attr("y", function(d, i) {
          return i * 20 + 9;
        })
        .attr("font-size", textSizeLegend)
        .attr("fill", "#737373")
        .text(function(d) {
          return d;
        });
    };

    function update() {
      console.log("here");
      json = [
        [{
          "axis": "A",
          "value": 4
        }, {
          "axis": "B",
          "value": 13
        }, {
          "axis": "C",
          "value": 8
        }, {
          "axis": "D",
          "value": 15
        }, {
          "axis": "E",
          "value": 2
        }, {
          "axis": "F",
          "value": 5
        }, {
          "axis": "G",
          "value": 9
        }, {
          "axis": "H",
          "value": 3
        }, {
          "axis": "I",
          "value": 1
        }]
      ];
      drawRadarChart('#chart-radar', wMaior, wMaior);
    };

    drawRadarCharts();

    d3.select("button").on("click", update);
  </script>

</html>

为什么这是一个黑客?

您没有按照enter,update,exit范例进行操作。您正在销毁图表并在每次更新时重建它。这很浪费,因为它保持不变。这是一个黑客,因为我在破坏之前存储多边形点只是为了过渡。我们应该在同一个多边形上运作......