2015-07-03 67 views

回答

6

由於這是一個chart.js之問題:-),你這是怎麼做到這一點chart.js之(這不是太複雜,要麼)

設置餅圖

// pie 
var data = [ 
    { 
     value: 300, 
     color: "#F7464A", 
     highlight: "#FF5A5E", 
     label: "Red", 
     subData: [28, 48, 40, 19, 86, 27, 190] 
    }, { 
     value: 50, 
     color: "#46BFBD", 
     highlight: "#5AD3D1", 
     label: "Green", 
     subData: [90, 28, 48, 40, 19, 86, 127] 
    }, { 
     value: 100, 
     color: "#FDB45C", 
     highlight: "#FFC870", 
     label: "Yellow", 
     subData: [28, 48, 40, 19, 86, 27, 190] 
    } 
] 

var canvas = document.getElementById("chart"); 
var ctx = canvas.getContext("2d"); 
var myPieChart = new Chart(ctx).Pie(data); 

設置使用餅圖數據

// bar using pie's sub data 
var bardata = { 
    labels: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], 
    datasets: [ 
     { 
      label: "My Second dataset", 
      fillColor: "rgba(151,187,205,0.5)", 
      strokeColor: "rgba(151,187,205,0.8)", 
      highlightFill: "rgba(151,187,205,0.75)", 
      highlightStroke: "rgba(151,187,205,1)", 
      data: data[0].subData.map(function (point, i) { 
       var pointTotal = 0; 
       data.forEach(function (point) { 
        pointTotal += point.subData[i] 
       }) 
       return pointTotal; 
      }) 
     } 
    ] 
}; 

var subcanvas = document.getElementById("subchart") 
var subctx = subcanvas.getContext("2d"); 
var myBarChart = new Chart(subctx).Bar(bardata); 

Updatin條形圖點擊餅圖

// connect them both 
canvas.onclick = function (evt) { 
    var activeSector = myPieChart.getSegmentsAtEvent(evt); 

    myBarChart.datasets[0].bars.forEach(function (bar, i) { 
     var pointTotal = 0; 
     data.forEach(function (point, j) { 
      if (activeSector.length === 0 || point.label === activeSector[0].label) 
       pointTotal += data[j].subData[i] 
     }) 

     bar.value = pointTotal; 
    }); 

    myBarChart.update(); 
}; 

點擊(在餅圖的畫布但)餡餅外時克酒吧數據復位條形圖。


小提琴 - http://jsfiddle.net/0zwkjv8a/

+0

謝謝!有沒有什麼方法可以顯示「過濾量」背後的「全額」?像http://i.imgur.com/4gTXNFK.gif一樣 –

3

已經張貼其他的答案包括什麼,我會一般建議這裏是使用DC-JS如果你想crossfilter啓用圖表出了大門。我會評論這個答案,但我沒有足夠的聲譽,所以我發佈這個選項'c。)',其中'a。)'正在使用dc-js和'b。)'進行一些修改到現有的Chart.js圖表​​。

選項'c。)'用於擴展Chart.js圖表​​類型並使子圖表像DC-js圖表一樣工作。 Chart.js圖表​​類型遵循繼承層次結構,因此如果您喜歡已經存在的圖表,則可以使用自己的原型方法來包裝其原型方法。此選項非常重要,在標題爲「dc.js - 偵聽圖表組渲染」的堆棧溢出問題的選定答案中,描述了dc-js的chartRegistry對象的當前實現如何與d3或dc內部相當分離,所以任何實現chartRegistry接口的圖表都可以成爲chartGroup的一部分。

我當時非常希望在數據集中使用Polar Area Charts,因爲我已經在使用一個充滿了dc-js圖表的圖表組來交叉過濾數據。我爲Polar Area圖表編寫了一個擴展,它可以作爲一種方式的示例(我將繼續說並不是最好的方式),以便像dc-js一樣擴展圖表類型。這樣做的回購是https://github.com/nsubordin81/Chart.dc.js,麻省理工學院的許可協議進行許可,並且如果是有史以來去任何地方,所有的代碼被複制到例如小提琴:http://jsfiddle.net/nsubordin81/3w725o3c/1/

Chart.dc.js v 0.1.0

MIT許可:opensource.org/licenses/MIT 版權所有(C)2015年泰勒鳥

 (function() { 
    "use strict"; 

    var root = this, 
     Chart = root.Chart, 
     dc = root.dc, 
     helpers = Chart.helpers, 
     //class for data structure that manages filters as they relate to chart segments. This should probably be generalized to chart elements of all kinds. 
     FilterManager = function (segmentList) { 

      //private member variable 
      var filterMap = []; 

      //constructor 
      //accepts a list of SegmentArcs that have had the extra properties added to them 
      for (var i = 0; i < segmentList.length; i++) { 
       add(segmentList[i].segmentID); 
      } 

      //private methods 
      function testOnAll(test) { 
       var testResult = true; 
       for (var i = 0; i < filterMap.length; i++) { 
        //one failure of test means testOnAll fails 
        if (!test(filterMap[i])) { 
         testResult = false; 
        } 
       } 
       return testResult; 
      } 
      //add a filter, pretty much just a wrapper for push 
      function add(segmentID) { 
       filterMap.push({ 
        "segmentID": segmentID, 
         "active": false 
       }); 
      } 
      //remove a filter by id, returns removed filter 
      function remove(segmentID) { 
       var removed = filterMap.find(segmentID); 
       filterMap = filterMap.filter(function (elem) { 
        return elem.segmentID !== segmentID; 
       }); 
       return removed; 
      } 
      //return this segment if it is filtered 
      function find(segmentID) { 
       for (var i = 0; i < filterMap.length; i++) { 
        if (filterMap[i].segmentID === segmentID) { 
         return filterMap[i]; 
        } 
       } 
       return -1; 
      } 

      //public methods 
      return { 
       //tell me if the filter for this segment is active 
       isActive: function (segmentID) { 
        var filter = find(segmentID); 
        if (filter === -1) { 
         console.error("something went wrong, the filter for this segment does not exist"); 
        } 
        return filter.active; 
       }, 
       //for the given segment, activate or deactivate its filter. return whether the filter is on or off. 
       flip: function (segmentID) { 
        var filter = find(segmentID); 
        if (filter === -1) { 
         console.error("something went wrong, the filter for this segment does not exist"); 
        } 
        filter.active ? filter.active = false : filter.active = true; 
        return filter.active; 
       }, 
       //if all filters are on, we want to be able to quickly deactivate them all 
       turnAllOff: function() { 
        for (var i = 0; i < filterMap.length; i++) { 
         filterMap[i].active = false; 
        } 
       }, 
       //tell me if all of the filters are off 
       allOff: function() { 
        return testOnAll(function (elem) { 
         return !elem.active; 
        }); 
       }, 
       //tell me if all the filters are on 
       allOn: function() { 
        return testOnAll(function (elem) { 
         return elem.active; 
        }); 
       } 
      } 
     }; 

    //utility function, Takes an array that has some property as its key 
    //and forms a javascript object with the keys as properties so we can get O(1) access 
    function createKeyMap(arr, propName) { 
     var keyMap = {} 
     for (var i = 0; i < arr.length; i++) { 
      keyMap[arr[i][propName]] = arr[i]; 
     } 
     return keyMap; 
    } 


    Chart.types.PolarArea.extend({ 
     name: "PolarAreaXF", 
     //this will have to be a member 
     dimension: undefined, 
     colorTypes: { 
      "NORMAL": 0, 
       "HIGHLIGHT": 1, 
       "FILTER": 2, 
       "FILTER_HIGHLIGHT": 3 
     }, 
     chartGroup: undefined, 
     filters: undefined, 
     originalDataKeys: undefined, 
     initialize: function (data) { 
      //--PRE-- 
      var that = this; 
      //Polar Area initialize method is expecting (data, options) in arguments, 
      //but we pass in an array of components to merge. Let's clean this up. 
      var argsArray = Array.prototype.slice.call(arguments); 
      //remove the first element of arguments which is our array, then we do a bunch of Chartjs converison on it . . . 
      argsArray.splice(0, 1); 
      //TODO - check if data is an array, if not, put a message in a console explaining how you are supposed to send data in an array 
      this.dimension = data.dimension; 
      data.chartGroup ? this.chartGroup = data.chartGroup : this.chartGroup = 0; 
      //short but magical line. Now we are linked with all dc charts in this group! 
      dc.registerChart(this, this.chartGroup); 
      var data = this.setupChartData(data.colors, data.highlights, data.labels); 
      //... and push the result in its place. 
      argsArray.unshift(data); 
      //originalDataArray -- this is used as a reference to the original state of the chart, since segments can come and go, 
      //we use this to track what a segment's original colors were when adding it back in. This would mess up adding a truly new segment, but who 
      //is gonna do that? Assumption here is dimensions start with so many groups and that is it. 
      this.originalDataKeys = createKeyMap(data, "key"); 
      //parent's initialize 
      Chart.types.PolarArea.prototype.initialize.apply(this, argsArray); 
      //--modify SegmentArcs-- 
      //assign colors and ids to all existing segment arcs 
      var mySegments = this.segments; 
      for (var i = 0; i < mySegments.length; i++) { 
       mySegments[i].colorList = [undefined, undefined, "#777", "#aaa"]; 
       mySegments[i].colorList[this.colorTypes.NORMAL] = mySegments[i].fillColor; 
       mySegments[i].colorList[this.colorTypes.HIGHLIGHT] = mySegments[i].highlight; 
       mySegments[i].segmentID = i; 
       mySegments[i].key = data[i].key; 
      } 
      //add methods to SegmentArc objects that will color them one way or the other depending on their filter 
      this.SegmentArc.prototype.setIncluded = function (include) { 
       if (include) { 
        this.fillColor = this.colorList[that.colorTypes.NORMAL]; 
        this.highlight = this.colorList[that.colorTypes.HIGHLIGHT]; 
       } else { 
        this.fillColor = this.colorList[that.colorTypes.FILTER]; 
        this.highlight = this.colorList[that.colorTypes.FILTER_HIGHLIGHT]; 
       } 
      } 
      //--initialize filters-- 
      this.filters = new FilterManager(this.segments); 
      //handle clicks on segments as filter events, do the styling and crossfilter changes at the Chart level in the filter method. 
      helpers.bindEvents(this, ["mousedown"], function (evt) { 
       var activeSegment = Chart.types.PolarArea.prototype.getSegmentsAtEvent.apply(this, [evt])[0]; 
       this.handleFilter(activeSegment); 
      }); 

     }, 
     //convert crossfilter dimension into chart.js Polar Area data object array 
     setupChartData: function (colors, highlights, labels) { 
      var chartJSible = []; 
      //probably need checks here to make sure client actually passed in a crossfilter dimension 
      var grouped = this.dimension.group().reduceCount().top(Infinity); 
      //probably need checks here to either fail if the arrays aren't all long enough or have some way to add random colors/highlights if they are shorter. 
      for (var i = 0; i < grouped.length; i++) { 
       var dataObject = { 
        value: grouped[i].value, 
        key: grouped[i].key, 
        color: colors[i], 
        highlight: highlights[i], 
        label: labels ? (labels[i] ? labels[i] : grouped[i].key) : grouped[i].key 
       }; 
       chartJSible.push(dataObject); 
      } 

      return chartJSible; 

     }, 
     //figure out what changed between Chart.js' internally maintained data object array and crossfilter's dimension data. use the saved information 
     //about what colors and highlight a key has to rebuild the segmentArc list 'segments'. can't trash the old, it might mess up the animations. 
     redraw: function() { 
      var grouped = this.dimension.group().reduceCount().top(Infinity); 
      var currentSegmentKeys = createKeyMap(this.segments, "key"); 
      var crossfilterGroupKeys = createKeyMap(grouped, "key"); 
      //loop through the segment list, if the segment for a group is already there, update the value, if it is not there, add it back using the 
      //original data as a guide for what it's color and highlight color should be. if there are segments in the existing list 

      var length = Math.max(this.segments.length, grouped.length); 
      //going through both lists, whichever is longer 
      for (var i = 0; i < length; i++) { 
       var sList = this.segments; 
       var gList = grouped; 
       //only do this part if we still have items in the new filtered list 
       if (gList[i]) { 
        //we already have a segment for this crossfilter group, just get that segment and update its value 
        if (currentSegmentKeys[gList[i].key]) { 
         currentSegmentKeys[gList[i].key].value = gList[i].value; 
        } else { 
         //the chart doesn't have the crossfilter group item, add a new segment with the right colors and values from original data 
         var theSegment = this.originalDataKeys[gList[i].key]; 
         this.addData(theSegment, 0, true); 
        } 
       } 
       //only do this part if we still have items in the current chart segment list 
       if (sList[i]) { 
        //we don't have this segment in the new crossfilter group, remove it from the chart 
        if (!crossfilterGroupKeys[sList[i].key]) { 
         this.removeData(i); 
        } 
       } 
      } 

      this.update(); 
     }, 
     filterAll: function() { 
      this.dimension.filterAll(); 
      this.filters.turnAllOff(); 
      this.colorMeIn(); 
      this.redraw(); 
     }, 
     handleFilter: function (clicked) { 
      //after we have all of the filters figured out, change the colors to reflect what they should be and update the chart 
      this.filters.flip(clicked.segmentID); 
      this.colorMeIn(); 
      if (this.filters.allOn()) { 
       this.dimension = this.dimension.filterAll(); 
       dc.redrawAll(this.chartGroup); 
       this.filters.turnAllOff(); 
      } 
      dc.redrawAll(this.chartGroup); 
     }, 
     colorMeIn() { 
      var activeFilters = []; 
      var segments = this.segments; 
      for (var i = 0; i < segments.length; i++) { 
       var segment = segments[i]; 
       if (this.filters.isActive(segment.segmentID) || this.filters.allOff()) { 
        segment.setIncluded(true); 
        activeFilters.push(segment.key); 
       } else { 
        segment.setIncluded(false); 
       } 
      } 
      this.dimension = this.dimension.filterFunction(function (d) { 
       for (var i = 0; i < activeFilters.length; i++) { 
        if (d === activeFilters[i]) { 
         return true; 
        } 
       } 
       return false; 
      }); 
     } 
    }) 
}).call(this); 
相關問題