2016-06-08 71 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