2016-03-01 70 views
1

努力學習dc.js 綜觀Dc.js主頁納斯達克例如:http://dc-js.github.io/dc.js/事件處理和過濾如何在dc.js中工作?

在這個時候,我的主要吸引力是要弄清楚如何在各個圖表組件,如特定的泡沫點擊,環形圖的特定部分等等......觸發對所有其他組件的重新過濾。 我正在瀏覽stock.js文件,但我似乎無法找到事件處理程序,它會響應特定的鼠標選擇,並觸發其他部分進行更新。 任何有經驗的人都可以指點我在哪裏尋找這些東西?

這裏是stock.js:

'use strict'; 

var gainOrLossChart = dc.pieChart('#gain-loss-chart'); 
var fluctuationChart = dc.barChart('#fluctuation-chart'); 
var quarterChart = dc.pieChart('#quarter-chart'); 
var dayOfWeekChart = dc.rowChart('#day-of-week-chart'); 
var moveChart = dc.lineChart('#monthly-move-chart'); 
var volumeChart = dc.barChart('#monthly-volume-chart'); 
var yearlyBubbleChart = dc.bubbleChart('#yearly-bubble-chart'); 
var nasdaqCount = dc.dataCount('.dc-data-count'); 
var nasdaqTable = dc.dataTable('.dc-data-table'); 


<div id='chart'> 
<a class='reset' 
href='javascript:myChart.filterAll();dc.redrawAll();' 
style='visibility: hidden;'>reset</a> 
</div> 

<div id='chart'> 
<span class='reset' style='visibility: hidden;'> 
Current filter: <span class='filter'></span> 
</span> 
</div> 

d3.csv('ndx.csv', function (data) { 
    var dateFormat = d3.time.format('%m/%d/%Y'); 
    var numberFormat = d3.format('.2f'); 

    data.forEach(function (d) { 
     d.dd = dateFormat.parse(d.date); 
     d.month = d3.time.month(d.dd); // pre-calculate month for better performance 
     d.close = +d.close; // coerce to number 
     d.open = +d.open; 
    }); 

    var ndx = crossfilter(data); 
    var all = ndx.groupAll(); 

    var yearlyDimension = ndx.dimension(function (d) { 
     return d3.time.year(d.dd).getFullYear(); 
    }); 
    var yearlyPerformanceGroup = yearlyDimension.group().reduce(
     /* callback for when data is added to the current filter results */ 
     function (p, v) { 
      ++p.count; 
      p.absGain += v.close - v.open; 
      p.fluctuation += Math.abs(v.close - v.open); 
      p.sumIndex += (v.open + v.close)/2; 
      p.avgIndex = p.sumIndex/p.count; 
      p.percentageGain = p.avgIndex ? (p.absGain/p.avgIndex) * 100 : 0; 
      p.fluctuationPercentage = p.avgIndex ? (p.fluctuation/p.avgIndex) * 100 : 0; 
      return p; 
     }, 
     function (p, v) { 
      --p.count; 
      p.absGain -= v.close - v.open; 
      p.fluctuation -= Math.abs(v.close - v.open); 
      p.sumIndex -= (v.open + v.close)/2; 
      p.avgIndex = p.count ? p.sumIndex/p.count : 0; 
      p.percentageGain = p.avgIndex ? (p.absGain/p.avgIndex) * 100 : 0; 
      p.fluctuationPercentage = p.avgIndex ? (p.fluctuation/p.avgIndex) * 100 : 0; 
      return p; 
     }, 
     /* initialize p */ 
     function() { 
      return { 
       count: 0, 
       absGain: 0, 
       fluctuation: 0, 
       fluctuationPercentage: 0, 
       sumIndex: 0, 
       avgIndex: 0, 
       percentageGain: 0 
      }; 
     } 
    ); 

    var dateDimension = ndx.dimension(function (d) { 
     return d.dd; 
    }); 

    var moveMonths = ndx.dimension(function (d) { 
     return d.month; 
    }); 
    var monthlyMoveGroup = moveMonths.group().reduceSum(function (d) { 
     return Math.abs(d.close - d.open); 
    }); 
    var volumeByMonthGroup = moveMonths.group().reduceSum(function (d) { 
     return d.volume/500000; 
    }); 
    var indexAvgByMonthGroup = moveMonths.group().reduce(
     function (p, v) { 
      ++p.days; 
      p.total += (v.open + v.close)/2; 
      p.avg = Math.round(p.total/p.days); 
      return p; 
     }, 
     function (p, v) { 
      --p.days; 
      p.total -= (v.open + v.close)/2; 
      p.avg = p.days ? Math.round(p.total/p.days) : 0; 
      return p; 
     }, 
     function() { 
      return {days: 0, total: 0, avg: 0}; 
     } 
    ); 

    var gainOrLoss = ndx.dimension(function (d) { 
     return d.open > d.close ? 'Open' : 'Closed'; 
    }); 
    var gainOrLossGroup = gainOrLoss.group(); 

    var fluctuation = ndx.dimension(function (d) { 
     return Math.round((d.close - d.open)/d.open * 100); 
    }); 
    var fluctuationGroup = fluctuation.group(); 

    var quarter = ndx.dimension(function (d) { 
     var month = d.dd.getMonth(); 
     if (month <= 2) { 
      return 'Q1'; 
     } else if (month > 2 && month <= 5) { 
      return 'Q2'; 
     } else if (month > 5 && month <= 8) { 
      return 'Q3'; 
     } else { 
      return 'Q4'; 
     } 
    }); 
    var quarterGroup = quarter.group().reduceSum(function (d) { 
     return d.volume; 
    }); 

    var dayOfWeek = ndx.dimension(function (d) { 
     var day = d.dd.getDay(); 
     var name = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']; 
     return day + '.' + name[day]; 
    }); 
    var dayOfWeekGroup = dayOfWeek.group(); 
.radiusValueAccessor(function (p) { 
      return p.value.fluctuationPercentage; 
     }) 
     .maxBubbleRelativeSize(0.3) 
     .x(d3.scale.linear().domain([-2500, 2500])) 
     .y(d3.scale.linear().domain([-100, 100])) 
     .r(d3.scale.linear().domain([0, 4000])) 

     .yAxisPadding(100) 
     .xAxisPadding(500) 
     .renderHorizontalGridLines(true) 
     .renderVerticalGridLines(true) 
     .xAxisLabel('Index Gain') 
     .yAxisLabel('Index Gain %') 

     .renderLabel(true) 
     .label(function (p) { 
      return p.key; 
     }) 
     // (_optional_) whether chart should render titles, `default = false` 
     .renderTitle(true) 
     .title(function (p) { 
      return [ 
       p.key, 
       'Index Gain: ' + numberFormat(p.value.absGain), 
       'Index Gain in Percentage: ' + numberFormat(p.value.percentageGain) + '%', 
       'Fluctuation/Index Ratio: ' + numberFormat(p.value.fluctuationPercentage) + '%' 
      ].join('\n'); 
     }) 

     .yAxis().tickFormat(function (v) { 
     return v + '%'; 
    }); 



    gainOrLossChart /* dc.pieChart('#gain-loss-chart', 'chartGroup') */ 

     .width(180) 
     .height(180) 
     .radius(80) 
     .dimension(gainOrLoss) 
     .group(gainOrLossGroup) 
     .label(function (d) { 
      if (gainOrLossChart.hasFilter() && !gainOrLossChart.hasFilter(d.key)) { 
       return d.key + '(0%)'; 
      } 
      var label = d.key; 
      if (all.value()) { 
       label += '(' + Math.floor(d.value/all.value() * 100) + '%)'; 
      } 
      return label; 
     }) 


    quarterChart /* dc.pieChart('#quarter-chart', 'chartGroup') */ 
     .width(180) 
     .height(180) 
     .radius(80) 
     .innerRadius(30) 
     .dimension(quarter) 
     .group(quarterGroup); 

     .width(180) 
     .height(180) 
     .margins({top: 20, left: 10, right: 10, bottom: 20}) 
     .group(dayOfWeekGroup) 
     .dimension(dayOfWeek) 
     .ordinalColors(['#3182bd', '#6baed6', '#9ecae1', '#c6dbef', '#dadaeb']) 
     .label(function (d) { 
      return d.key.split('.')[1]; 
     }) 
     // Title sets the row text 
     .title(function (d) { 
      return d.value; 
     }) 
     .elasticX(true) 
     .xAxis().ticks(4); 

     .width(420) 
     .height(180) 
     .margins({top: 10, right: 50, bottom: 30, left: 40}) 
     .dimension(fluctuation) 
     .group(fluctuationGroup) 
     .elasticY(true) 
     // (_optional_) whether bar should be center to its x value. Not needed for ordinal chart, `default=false` 
     .centerBar(true) 
     // (_optional_) set gap between bars manually in px, `default=2` 
     .gap(1) 
     // (_optional_) set filter brush rounding 
     .round(dc.round.floor) 
     .alwaysUseRounding(true) 
     .x(d3.scale.linear().domain([-25, 25])) 
     .renderHorizontalGridLines(true) 
     // Customize the filter displayed in the control span 
     .filterPrinter(function (filters) { 
      var filter = filters[0], s = ''; 
      s += numberFormat(filter[0]) + '% -> ' + numberFormat(filter[1]) + '%'; 
      return s; 
     }); 

    // Customize axes 
    fluctuationChart.xAxis().tickFormat(
     function (v) { return v + '%'; }); 
    fluctuationChart.yAxis().ticks(5); 

     .renderArea(true) 
     .width(990) 
     .height(200) 
     .transitionDuration(1000) 
     .margins({top: 30, right: 50, bottom: 25, left: 40}) 
     .dimension(moveMonths) 
     .mouseZoomable(true) 
     .rangeChart(volumeChart) 
     .x(d3.time.scale().domain([new Date(1985, 0, 1), new Date(2012, 11, 31)])) 
     .round(d3.time.month.round) 
     .xUnits(d3.time.months) 
     .elasticY(true) 
     .renderHorizontalGridLines(true) 

     .legend(dc.legend().x(800).y(10).itemHeight(13).gap(5)) 
     .brushOn(false) 

     .group(indexAvgByMonthGroup, 'Monthly Index Average') 
     .valueAccessor(function (d) { 
      return d.value.avg; 
     }) 

     .stack(monthlyMoveGroup, 'Monthly Index Move', function (d) { 
      return d.value; 
     }) 
     // Title can be called by any stack layer. 
     .title(function (d) { 
      var value = d.value.avg ? d.value.avg : d.value; 
      if (isNaN(value)) { 
       value = 0; 
      } 
      return dateFormat(d.key) + '\n' + numberFormat(value); 
     }); 


    volumeChart.width(990) /* dc.barChart('#monthly-volume-chart', 'chartGroup'); */ 
     .height(40) 
     .margins({top: 0, right: 50, bottom: 20, left: 40}) 
     .dimension(moveMonths) 
     .group(volumeByMonthGroup) 
     .centerBar(true) 
     .gap(1) 
     .x(d3.time.scale().domain([new Date(1985, 0, 1), new Date(2012, 11, 31)])) 
     .round(d3.time.month.round) 
     .alwaysUseRounding(true) 
     .xUnits(d3.time.months); 

    nasdaqCount /* dc.dataCount('.dc-data-count', 'chartGroup'); */ 
     .dimension(ndx) 
     .group(all)  
     .html({ 
      some: '<strong>%filter-count</strong> selected out of <strong>%total-count</strong> records' + 
      ' | <a href=\'javascript:dc.filterAll(); dc.renderAll();\'\'>Reset All</a>', 
      all: 'All records selected. Please click on the graph to apply filters.' 
     }); 

    nasdaqTable /* dc.dataTable('.dc-data-table', 'chartGroup') */ 
     .dimension(dateDimension) 
     .group(function (d) { 
      var format = d3.format('02d'); 
      return d.dd.getFullYear() + '/' + format((d.dd.getMonth() + 1)); 
     }) 
     // (_optional_) max number of records to be shown, `default = 25` 
     .size(10) 
     // There are several ways to specify the columns; see the data-table documentation. 
     // This code demonstrates generating the column header automatically based on the columns. 
     .columns([ 
      // Use the `d.date` field; capitalized automatically 
      'date', 
      // Use `d.open`, `d.close` 
      'open', 
      'close', 
      { 

       label: 'Change', 
       format: function (d) { 
        return numberFormat(d.close - d.open); 
       } 
      }, 
      // Use `d.volume` 
      'volume' 
     ]) 

     .sortBy(function (d) { 
      return d.dd; 
     }) 

    dc.geoChoroplethChart('#us-chart') 
    // (_optional_) define chart width, default 200 
    .width(990) 
    // (optional) define chart height, default 200 
    .height(500) 
    // (optional) define chart transition duration, default 1000 
    .transitionDuration(1000) 
    // set crossfilter dimension, dimension key should match the name retrieved in geojson layer 
    .dimension(states) 
    // set crossfilter group 
    .group(stateRaisedSum) 
    // (_optional_) define color function or array for bubbles 
    .colors(['#ccc', '#E2F2FF','#C4E4FF','#9ED2FF','#81C5FF','#6BBAFF','#51AEFF','#36A2FF','#1E96FF','#0089FF', 
    '#0061B5']) 
    // (_optional_) define color domain to match your data domain if you want to bind data or color 
    .colorDomain([-5, 200]) 
    // (_optional_) define color value accessor 
    .colorAccessor(function(d, i){return d.value;}) 
    // Project the given geojson. You can call this function multiple times with different geojson feed to generate 

    .overlayGeoJson(statesJson.features, 'state', function(d) { 
    return d.properties.name; 
    }) 

    .title(function(d) { 
    return 'State: ' + d.key + '\nTotal Amount Raised: ' + numberFormat(d.value ? d.value : 0) + 'M'; 
    }); 


    dc.bubbleOverlay('#bubble-overlay', 'chartGroup') 
    // The bubble overlay chart does not generate its own svg element but rather reuses an existing 
    // svg to generate its overlay layer 
    .svg(d3.select('#bubble-overlay svg')) 
    // (_optional_) define chart width, `default = 200` 
    .width(990) 
    // (_optional_) define chart height, `default = 200` 
    .height(500) 
    // (_optional_) define chart transition duration, `default = 1000` 
    .transitionDuration(1000) 
    // Set crossfilter dimension, dimension key should match the name retrieved in geo json layer 
    .dimension(states) 
    // Set crossfilter group 
    .group(stateRaisedSum) 
    // Closure used to retrieve x value from multi-value group 
    .keyAccessor(function(p) {return p.value.absGain;}) 
    // Closure used to retrieve y value from multi-value group 
    .valueAccessor(function(p) {return p.value.percentageGain;}) 
    // (_optional_) define color function or array for bubbles 
    .colors(['#ccc', '#E2F2FF','#C4E4FF','#9ED2FF','#81C5FF','#6BBAFF','#51AEFF','#36A2FF','#1E96FF','#0089FF', 
    '#0061B5']) 
    // (_optional_) define color domain to match your data domain if you want to bind data or color 
    .colorDomain([-5, 200]) 
    // (_optional_) define color value accessor 
    .colorAccessor(function(d, i){return d.value;}) 
    // Closure used to retrieve radius value from multi-value group 
    .radiusValueAccessor(function(p) {return p.value.fluctuationPercentage;}) 
    // set radius scale 
    .r(d3.scale.linear().domain([0, 3])) 
    // (_optional_) whether chart should render labels, `default = true` 
    .renderLabel(true) 
    // (_optional_) closure to generate label per bubble, `default = group.key` 
    .label(function(p) {return p.key.getFullYear();}) 
    // (_optional_) whether chart should render titles, `default = false` 
    .renderTitle(true) 
    // (_optional_) closure to generate title per bubble, `default = d.key + ': ' + d.value` 
    .title(function(d) { 
    return 'Title: ' + d.key; 
    }) 
    // add data point to its layer dimension key that matches point name: it will be used to 
    // generate a bubble. Multiple data points can be added to the bubble overlay to generate 
    // multiple bubbles. 
    .point('California', 100, 120) 
    .point('Colorado', 300, 120) 
    // (_optional_) setting debug flag to true will generate a transparent layer on top of 
    // bubble overlay which can be used to obtain relative `x`,`y` coordinate for specific 
    // data point, `default = false` 
    .debug(true); 
    */ 

    //#### Rendering 

    //simply call `.renderAll()` to render all charts on the page 
    dc.renderAll(); 
    /* 
    // Or you can render charts belonging to a specific chart group 
    dc.renderAll('group'); 
    // Once rendered you can call `.redrawAll()` to update charts incrementally when the data 
    // changes, without re-rendering everything 
    dc.redrawAll(); 
    // Or you can choose to redraw only those charts associated with a specific chart group 
    dc.redrawAll('group'); 
    */ 

}); 

//#### Versions 

//Determine the current version of dc with `dc.version` 
d3.selectAll('#version').text(dc.version); 

// Determine latest stable version in the repo via Github API 
d3.json('https://api.github.com/repos/dc-js/dc.js/releases/latest', function (error, latestRelease) { 
    /*jshint camelcase: false */ 
    d3.selectAll('#latest').text(latestRelease.tag_name); /* jscs:disable */ 
}); 

回答

2

我相信它是在基地混入的onclick處理程序(包含在所有圖表)定義:https://github.com/dc-js/dc.js/blob/develop/src/base-mixin.js#L1072

如果要觸發從dc.js外部重繪,使用dc.redrawAll和dc.renderAll方法。

+0

有沒有辦法讓這些點擊事件冒泡,所以我可以在自己的代碼中對它們作出響應(比如點擊特定的泡泡等)? –

+0

看看on(event,...)監聽器系統:https://github.com/dc-js/dc.js/blob/master/web/docs/api-latest.md#dc.baseMixin + on –

+0

此外,[圖表註冊表](https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.chartRegistry)是在圖表 - 這是'dc.redrawAll'調用的內容。 – Gordon