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 */
});
有沒有辦法讓這些點擊事件冒泡,所以我可以在自己的代碼中對它們作出響應(比如點擊特定的泡泡等)? –
看看on(event,...)監聽器系統:https://github.com/dc-js/dc.js/blob/master/web/docs/api-latest.md#dc.baseMixin + on –
此外,[圖表註冊表](https://github.com/dc-js/dc.js/blob/develop/web/docs/api-latest.md#dc.chartRegistry)是在圖表 - 這是'dc.redrawAll'調用的內容。 – Gordon