2016-06-08 88 views
0

我想弄清楚如何在Alangrafu的雷达图上添加一个转换,所以不是用新数据刷新整个图表,而只是移动点。转换d3.radar图表

或者如果这太复杂了,可能是像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); 
       }) 

回答

2

这里是一个真正的快速劈了你所追求的:

<!DOCTYPE html> 
 
<html> 
 

 
    <head> 
 
    <script data-require="[email protected]" 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>

为什么这是一个黑客?

你没有按照输入,更新,退出范例。您正在销毁图表并在每次更新时重建图表。这是浪费,尽管它保持不变。这是一种黑客行为,因为我正在为转换而毁坏之前存储多边形点。我们应该在同一个多边形上操作...

+0

嗨马克,我知道这可能是一个长期的!但我希望你能提供一个见解,说明为什么它不适用于多个数据输入? ([see fiddle](http://jsfiddle.net/j8g8s6Lz/3/))。我尝试过调试,而且我发现的唯一的事情是当只有一组数据要绘制时,数组的长度是13,但是当有两组时,第一组点数组是7和第二十九。当我猜测它应该是13/13?我不确定如果我有什么想法我在。希望我可以自己弄清楚,我认为在另一个问题中提出这个问题并不合适! – lolio

+0

@Iolio,你的代码中有很多东西不对。首先,最大的是可变范围问题。你正在依赖全局变量,这让你感到沮丧。例如,在你的更新中,你创建一个名为'json'的** local **变量,然后调用你的重绘函数。由于这个重新分配是本地的,它不会影响全局的'json'变量。你应该把这个变量传递给你的绘图... – Mark

+0

@Iolio,其次,你正在做大量的显式循环你的数据,而'd3'完全是关于没有显式循环的绑定。这会给你带来麻烦,特别是在你的'.append(“svg:circle”)'你重新推入数据值的东西(这就是为什么你在一个数组中得到19的原因)... – Mark