Compare commits

...

22 Commits

Author SHA1 Message Date
David Souther dc4845a6f3 Things mostly render. 10 years ago
David Souther ac2603c126 Added canvas class. 10 years ago
frank shao 9201272013 built 11 years ago
frank shao 48e2684edf RenderWatch: Scatterpluslinechart 11 years ago
frank shao 0ef0e39384 RenderWatch: added to scatter plus line chart 11 years ago
David Souther 7364da25d5 Built. 11 years ago
David Souther 9800adeba6 Added watch on scatterPlusLineChart render end. 11 years ago
Robin Hu 8d8b727bb2 Renderwatch: Adding renderEnd dispatch to scatterPlusLineChart 11 years ago
frank shao 5da5323859 RenderWatch: added renderwatch api to selection.prototype 11 years ago
frank shao 504b5b0c9d RenderWatch: added render watch to cumulative line chart; various fixes 11 years ago
frank shao e177cae4d2 RenderWatch: added render watch to lineChart 11 years ago
frank shao 204a2e9e19 RenderWatch: Added render watch to scatterchart and submodels 11 years ago
frank shao a41d985c19 RenderWatch: Fixes 11 years ago
frank shao 5e1a7490b5 renderWatch: Modified addModels to accept list of models 11 years ago
frank shao 95fd1026b3 RenderWatch: fixes to axis.js 11 years ago
frank shao 7b490d2c85 Added util to watch for end of render; applied to multibarchart and its sub-models 11 years ago
frank shao 23fbe55024 Merge branch 'development' of https://github.com/novus/nvd3 into development 11 years ago
frank shao 7ad5580db3 Deprecated 'multibar.delay'. Use multibar.duration instead 11 years ago
Robin Hu e75bfb274e Merge branch 'master' into development 11 years ago
frank shao 4672f4b6ca build dist 11 years ago
frank shao fd61caaff6 Merge branch 'development' of https://github.com/novus/nvd3 into development 11 years ago
frank shao f621237ff8 Added tooltip adjustment so that it can also be dom element 11 years ago

@ -14,6 +14,9 @@ module.exports = function(grunt) {
'src/interactiveLayer.js', 'src/interactiveLayer.js',
'src/tooltip.js', 'src/tooltip.js',
'src/utils.js', 'src/utils.js',
'src/canvas.js',
'src/models/axis.js', 'src/models/axis.js',
'src/models/historicalBar.js', 'src/models/historicalBar.js',
'src/models/bullet.js', 'src/models/bullet.js',
@ -46,6 +49,7 @@ module.exports = function(grunt) {
'src/models/sparklinePlus.js', 'src/models/sparklinePlus.js',
'src/models/stackedArea.js', 'src/models/stackedArea.js',
'src/models/stackedAreaChart.js', 'src/models/stackedAreaChart.js',
'src/outro.js' 'src/outro.js'
], ],
dest: 'nv.d3.js' dest: 'nv.d3.js'

@ -46,6 +46,7 @@ nv.models.scatterChart = function() {
//nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's'); //nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's');
}; };
var renderWatch = nv.utils.renderWatch(dispatch);
function chart(selection) { function chart(selection) {
selection.each(function(data) { selection.each(function(data) {

@ -59,19 +59,22 @@ text {
// Wrapping in nv.addGraph allows for '0 timeout render', stores rendered charts in nv.graphs, and may do more in the future... it's NOT required // Wrapping in nv.addGraph allows for '0 timeout render', stores rendered charts in nv.graphs, and may do more in the future... it's NOT required
nv.addGraph(function() { nv.addGraph(function() {
var chart = nv.models.cumulativeLineChart() var chart = nv.models.cumulativeLineChart()
.useInteractiveGuideline(true) .useInteractiveGuideline(true)
.x(function(d) { return d[0] }) .x(function(d) { return d[0] })
.y(function(d) { return d[1]/100 }) .y(function(d) { return d[1]/100 })
.color(d3.scale.category10().range()) .color(d3.scale.category10().range())
.average(function(d) { return d.mean/100; }) .average(function(d) { return d.mean/100; })
.transitionDuration(300) .duration(300)
.clipVoronoi(false); .clipVoronoi(false);
chart.dispatch.on('renderEnd', function() {
console.log('render complete: cumulative line with guide line');
});
chart.xAxis chart.xAxis
.tickFormat(function(d) { .tickFormat(function(d) {
return d3.time.format('%m/%d/%y')(new Date(d)) return d3.time.format('%m/%d/%y')(new Date(d))
}); });
chart.yAxis chart.yAxis
.tickFormat(d3.format(',.1%')); .tickFormat(d3.format(',.1%'));
@ -97,8 +100,13 @@ nv.addGraph(function() {
.y(function(d) { return d[1]/100 }) .y(function(d) { return d[1]/100 })
.color(d3.scale.category10().range()) .color(d3.scale.category10().range())
.average(function(d) { return d.mean/100; }) .average(function(d) { return d.mean/100; })
.duration(300)
.clipVoronoi(false); .clipVoronoi(false);
chart.dispatch.on('renderEnd', function() {
console.log('render complete: cumulative line without guide line');
});
chart.xAxis chart.xAxis
.tickFormat(function(d) { .tickFormat(function(d) {
return d3.time.format('%m/%d/%y')(new Date(d)) return d3.time.format('%m/%d/%y')(new Date(d))

@ -32,6 +32,9 @@ nv.addGraph({
.height(height) .height(height)
.margin({top: 20, right: 20, bottom: 20, left: 20}) .margin({top: 20, right: 20, bottom: 20, left: 20})
chart.dispatch.on('renderEnd', function(){
console.log('render complete');
});
d3.select('#test1') d3.select('#test1')
.attr('width', width) .attr('width', width)

@ -62,19 +62,24 @@ nv.addGraph(function() {
x: function(d,i) { return i}, x: function(d,i) { return i},
showXAxis: true, showXAxis: true,
showYAxis: true, showYAxis: true,
transitionDuration: 250 transitionDuration: 300,
useInteractiveGuideline: true
}) })
; ;
// chart sub-models (ie. xAxis, yAxis, etc) when accessed directly, return themselves, not the parent chart, so need to chain separately // chart sub-models (ie. xAxis, yAxis, etc) when accessed directly, return themselves, not the parent chart, so need to chain separately
chart.xAxis chart.xAxis
.axisLabel("Time (s)") .axisLabel("Time (s)")
.tickFormat(d3.format(',.1f')); .tickFormat(d3.format(',.1f'))
;
chart.yAxis chart.yAxis
.axisLabel('Voltage (v)') .axisLabel('Voltage (v)')
.tickFormat(d3.format(',.2f')) .tickFormat(d3.format(',.2f'))
; ;
chart.dispatch.on('renderEnd', function(){
console.log('render complete');
});
d3.select('#chart1 svg') d3.select('#chart1 svg')
.datum(sinAndCos()) .datum(sinAndCos())

@ -48,14 +48,19 @@ nv.addGraph({
.width(width) .width(width)
.height(height) .height(height)
.stacked(true) .stacked(true)
.delay(0)
chart.dispatch.on('renderEnd', function(){
console.log('Render Complete');
});
var svg = d3.select('#test1 svg') var svg = d3.select('#test1 svg')
.attr('width', width) .attr('width', width)
.attr('height', height) .attr('height', height)
.datum(test_data); .datum(test_data);
console.log('calling chart');
svg.transition().duration(500).call(chart); svg.transition().duration(0).call(chart);
return chart; return chart;
}, },
@ -79,10 +84,11 @@ nv.addGraph({
.width(width) .width(width)
.height(height); .height(height);
console.log('calling chart');
d3.select('#test1 svg') d3.select('#test1 svg')
.attr('width', width) .attr('width', width)
.attr('height', height) .attr('height', height)
transition().duration(500) transition().duration(0)
.call(graph); .call(graph);
}; };

@ -69,24 +69,34 @@ nv.addGraph(function() {
chart = nv.models.multiBarChart() chart = nv.models.multiBarChart()
.barColor(d3.scale.category20().range()) .barColor(d3.scale.category20().range())
.margin({bottom: 100}) .margin({bottom: 100})
.transitionDuration(300) .duration(300)
.delay(0)
.rotateLabels(45) .rotateLabels(45)
.groupSpacing(0.1) .groupSpacing(0.1)
; ;
chart.multibar chart.multibar
.hideable(true); .hideable(true)
// .duration(4000)
;
chart.reduceXTicks(false).staggerLabels(true); chart.reduceXTicks(false).staggerLabels(true);
chart.xAxis chart.xAxis
.axisLabel("Current Index") .axisLabel("Current Index")
.showMaxMin(true) .showMaxMin(true)
.tickFormat(d3.format(',.6f')); .tickFormat(d3.format(',.6f'))
;
chart.yAxis chart.yAxis
.tickFormat(d3.format(',.1f')); .tickFormat(d3.format(',.1f'))
// .duration(2000)
;
chart.dispatch.on('renderEnd', function(){
console.log('Render Complete');
});
d3.select('#chart1 svg') d3.select('#chart1 svg')
.datum(negative_test_data) .datum(negative_test_data)
@ -96,9 +106,18 @@ nv.addGraph(function() {
chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); }); chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });
return chart; return chart;
}); });
// setTimeout(function(){
// negative_test_data.forEach(function(d){
// d.values.forEach(function(e){
// e.y = 10;
// });
// });
// chart.update();
// }, 2000);

@ -35,7 +35,6 @@ nv.addGraph({
.useVoronoi(false) .useVoronoi(false)
//.interactive(false) //.interactive(false)
d3.select('#test1') d3.select('#test1')
.attr('width', width) .attr('width', width)
.attr('height', height) .attr('height', height)

@ -70,6 +70,9 @@ nv.addGraph(function() {
.color(d3.scale.category10().range()) .color(d3.scale.category10().range())
.transitionDuration(300) .transitionDuration(300)
; ;
chart.dispatch.on('renderEnd', function(){
console.log('render complete');
});
chart.xAxis.tickFormat(d3.format('.02f')); chart.xAxis.tickFormat(d3.format('.02f'));
chart.yAxis.tickFormat(d3.format('.02f')); chart.yAxis.tickFormat(d3.format('.02f'));

@ -69,6 +69,10 @@ nv.addGraph(function() {
.transitionDuration(300) .transitionDuration(300)
.color(d3.scale.category10().range()); .color(d3.scale.category10().range());
chart.dispatch.on('renderEnd', function(){
console.log('render complete');
});
chart.xAxis.tickFormat(d3.format('.02f')) chart.xAxis.tickFormat(d3.format('.02f'))
chart.yAxis.tickFormat(d3.format('.02f')) chart.yAxis.tickFormat(d3.format('.02f'))

1612
nv.d3.js

File diff suppressed because it is too large Load Diff

@ -0,0 +1,290 @@
/*
Get a new SVG canvas, with margins and scales. Pass an object as `options` to
set values. Defaults:
{
size: # Size of SVG. Available size will be smaller by the size of the margins.
width: 960
height: 500
available:
width: 900
height: 450
margin: # Margins for the graphic.
top: 20
right: 20
bottom: 30
left: 40
scale: # d3.scales to scale against the canvas
x: linear
y: linear
domain: # Domain of scales for the canvas.
x: [0, 1]
y: [0, 1]
}
@param root String selector for finding the SVG element.
@param options Object matching the defaults to override.
@return Object with defaults, overriden by the options, and an additional two properties:
{
svg: SVG_Element # SVG root
defs: SVG_Defs_Element # <defs> to attach gradient and filter definitions to.
}
*/
function Canvas (root, chart) {
var margin, width, height, svg, scales, canvas;
root == null && (root = 'body');
svg = d3.select(root).attr({
width: width,
height: height
});
options = chart.options;
options.size || (options.size = {});
options.margin || (options.margin = {});
options.scale || (options.scale = {});
options.domain || (options.domain = {});
margin = {
top: options.margin.top || 20,
right: options.margin.top || 20,
bottom: options.margin.top || 30,
left: options.margin.top || 40
};
margin.leftright = margin.left + margin.right;
margin.topbottom = margin.top + margin.bottom;
width = (options.size.width || parseInt(svg.style('width')) || 960);
height = (options.size.height || parseInt(svg.style('height')) || 500);
scales = {
x: d3.scale[options.scale.x || 'linear']()
.range([0, width])
.domain(options.domain.x || [0, 1])
.nice(),
y: d3.scale[options.scale.y || 'linear']()
.range([0, height])
.domain(options.domain.y || [0, 1])
.nice()
};
chart.size = {
width: width,
height: height,
available: {
width: width - margin.leftright,
height: height - margin.topbottom
},
};
chart.margin = margin,
chart.scale = scales,
chart.svg = svg,
chart.defs = svg.select('defs'),
chart.dispatch = d3.dispatch.apply(null, options.dispatch || [])
}
function Chart (options) {
// var chart = Canvas(root, options);
options = options || {};
var chart = {
options: options,
noData: options.noData || 'No Data Available.',
color: options.color || nv.utils.defaultColor()
};
Chart.axis(chart, options);
Chart.defaultState(chart, options);
Chart.legend(chart, options);
chart.tooltip = options.tooltip ||
function (key, x, y, e, graph) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
};
chart.duration = options.duration || 250;
chart.useInteractiveGuideline = options.useInteractiveGuideline || false;
return chart;
}
Chart.canvas = function(chart, root){
Canvas(root, chart)
};
Chart.axis = function (chart, options) {
chart.axis = options.axis || {
x: nv.models.axis(),
y: nv.models.axis()
};
chart.axis.rightAlignY = chart.axis.rightAlignY || false;
chart.axis.topAlignX = chart.axis.topAlignX || false;
chart.axis.x
.orient((chart.axis.topAlignX) ? 'top' : 'bottom')
.tickPadding(7);
chart.axis.y
.orient((chart.axis.rightAlignY) ? 'right' : 'left');
chart.xAxis = chart.axis.x;
chart.yAxis = chart.axis.y;
};
Chart.axis.build = function (chart) {
if (chart.axis.x) {
chart.axis.x
.scale(chart.scale.x)
.ticks(chart.size.available.width / 100)
.tickSize(-chart.size.available.eight, 0);
chart.g.select('.nv-x.nv-axis')
.attr('transform',
'translate(0,' + chart.scale.y.range()[0] + ')'
);
chart.g.select('.nv-x.nv-axis')
.call(chart.axis.x);
}
if (chart.axis.y) {
chart.axis.y
.scale(chart.scale.y)
.ticks(chart.size.available.height / 36)
.tickSize(-chart.size.available.width, 0);
chart.g.select('.nv-y.nv-axis')
.call(chart.axis.y);
}
};
Chart.legend = function (chart, options) {
chart.legend = options.legend || nv.models.legend();
}
Chart.legend.build = function (chart, data) {
if(!chart.legend){ return; }
chart.legend.width(chart.size.available.width);
chart.svg.select('.nv-legendWrap')
.datum(data)
.call(chart.legend);
if (chart.margin.top != chart.legend.height()) {
chart.margin.top = chart.legend.height();
availableHeight = (chart.size.height || parseInt(chart.svg.style('height')) || 400) - margin.top - margin.bottom;
}
chart.wrap.select('.nv-legendWrap')
.attr('transform', 'translate(0,' + (-chart.margin.top) + ')')
}
Chart.defaultState = function (chart, options) {
chart.state = options.state || {};
chart.defaultState = options.defaultState || null;
}
Chart.defaultState.set = function(chart, data){
chart.state.disabled = data.map(function (d) {
return !!d.disabled
});
if (!chart.defaultState) {
var key;
chart.defaultState = {};
for (key in chart.state) {
if (chart.state[key] instanceof Array)
chart.defaultState[key] = chart.state[key].slice(0);
else
chart.defaultState[key] = chart.state[key];
}
}
}
Chart.checkData = function(chart, data){
if (!data || !data.length || !data.filter(function (d) {
return d.values.length
}).length) {
var noDataText = chart.svg.selectAll('.nv-noData').data([chart.noData]);
noDataText.enter().append('text')
.attr('class', 'nvd3 nv-noData')
.attr('dy', '-.7em')
.style('text-anchor', 'middle');
noDataText
.attr('x', chart.margin.left + chart.size.available.width / 2)
.attr('y', chart.margin.top + chart.size.available.height / 2)
.text(function (d) {
return d
});
return true;
} else {
chart.svg.selectAll('.nv-noData').remove();
return false;
}
}
Chart.attachCallable = function(chart, fn){
fn.dispatch = chart.dispatch;
fn.legend = chart.legend;
fn.xAxis = chart.axis.x;
fn.yAxis = chart.yAxis;
fn.options = nv.utils.optionsFunc.bind(chart);
fn.margin = function (_) {
margin = chart.options.margin
if (!arguments.length) return margin;
margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
return fn;
};
[
'width',
'height',
'showLegend',
'showXAxis',
'showYAxis',
'tooltips',
'tooltipContent',
'state',
'defaultState',
'noData'
].forEach(function(property){
fn[property] = function (_) {
if (!arguments.length) return chart[property];
chart[property] = _;
return fn;
};
});
fn.x = function(){
return chart.scales.x.apply(chart.scales.x, arguments);
}
fn.y = function(){
return chart.scales.y.apply(chart.scales.y, arguments);
}
fn.color = function (_) {
if (!arguments.length) return chart.color;
chart.color = nv.utils.getColor(_);
legend.color(chart.color);
return fn;
};
fn.rightAlignYAxis = function (_) {
if (!arguments.length) return chart.axis.rightAlignY;
chart.axis.rightAlignY = _;
chart.axis.y.orient((_) ? 'right' : 'left');
return fn;
};
fn.useInteractiveGuideline = function (_) {
if (!arguments.length) return chart.useInteractiveGuideline;
chart.useInteractiveGuideline = _;
if (_ === true) {
// fn.interactive(false);
// fn.useVoronoi(false);
}
return fn;
};
fn.transitionDuration = function (_) {
nv.deprecated('linefn.transitionDuration');
return fn.duration(_);
};
};

@ -48,6 +48,11 @@ nv.log = function() {
return arguments[arguments.length - 1]; return arguments[arguments.length - 1];
}; };
nv.deprecated = function(name) {
if (nv.dev && console && console.warn)
console.warn('`' + name + '` has been deprecated.');
}
nv.render = function render(step) { nv.render = function render(step) {
step = step || 1; // number of graphs to generate in each timeout loop step = step || 1; // number of graphs to generate in each timeout loop

@ -20,8 +20,11 @@ nv.models.axis = function() {
, isOrdinal = false , isOrdinal = false
, ticks = null , ticks = null
, axisLabelDistance = 12 //The larger this number is, the closer the axis label is to the axis. , axisLabelDistance = 12 //The larger this number is, the closer the axis label is to the axis.
, duration = 250
, dispatch = d3.dispatch('renderEnd')
, axisRendered = false
, maxMinRendered = false
; ;
axis axis
.scale(scale) .scale(scale)
.orient('bottom') .orient('bottom')
@ -35,16 +38,17 @@ nv.models.axis = function() {
// Private Variables // Private Variables
//------------------------------------------------------------ //------------------------------------------------------------
var scale0; var scale0
, renderWatch = nv.utils.renderWatch(dispatch, duration)
;
//============================================================ //============================================================
function chart(selection) { function chart(selection) {
renderWatch.reset();
selection.each(function(data) { selection.each(function(data) {
var container = d3.select(this); var container = d3.select(this);
//------------------------------------------------------------ //------------------------------------------------------------
// Setup containers and skeleton of chart // Setup containers and skeleton of chart
@ -63,9 +67,7 @@ nv.models.axis = function() {
//TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component //TODO: consider calculating width/height based on whether or not label is added, for reference in charts using this component
g.watchTransition(renderWatch, 'axis').call(axis);
g.transition().call(axis);
scale0 = scale0 || axis.scale(); scale0 = scale0 || axis.scale();
@ -102,7 +104,7 @@ nv.models.axis = function() {
var v = fmt(d); var v = fmt(d);
return ('' + v).match('NaN') ? '' : v; return ('' + v).match('NaN') ? '' : v;
}); });
axisMaxMin.transition() axisMaxMin.watchTransition(renderWatch, 'min-max top')
.attr('transform', function(d,i) { .attr('transform', function(d,i) {
return 'translate(' + scale.range()[i] + ',0)' return 'translate(' + scale.range()[i] + ',0)'
}); });
@ -152,10 +154,8 @@ nv.models.axis = function() {
var v = fmt(d); var v = fmt(d);
return ('' + v).match('NaN') ? '' : v; return ('' + v).match('NaN') ? '' : v;
}); });
axisMaxMin.transition() axisMaxMin.watchTransition(renderWatch, 'min-max bottom')
.attr('transform', function(d,i) { .attr('transform', function(d,i) {
//return 'translate(' + scale.range()[i] + ',0)'
//return 'translate(' + scale(d) + ',0)'
return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)' return 'translate(' + (scale(d) + (isOrdinal ? scale.rangeBand() / 2 : 0)) + ',0)'
}); });
} }
@ -190,7 +190,7 @@ nv.models.axis = function() {
var v = fmt(d); var v = fmt(d);
return ('' + v).match('NaN') ? '' : v; return ('' + v).match('NaN') ? '' : v;
}); });
axisMaxMin.transition() axisMaxMin.watchTransition(renderWatch, 'min-max right')
.attr('transform', function(d,i) { .attr('transform', function(d,i) {
return 'translate(0,' + scale.range()[i] + ')' return 'translate(0,' + scale.range()[i] + ')'
}) })
@ -232,7 +232,7 @@ nv.models.axis = function() {
var v = fmt(d); var v = fmt(d);
return ('' + v).match('NaN') ? '' : v; return ('' + v).match('NaN') ? '' : v;
}); });
axisMaxMin.transition() axisMaxMin.watchTransition(renderWatch, 'min-max right')
.attr('transform', function(d,i) { .attr('transform', function(d,i) {
return 'translate(0,' + scale.range()[i] + ')' return 'translate(0,' + scale.range()[i] + ')'
}) })
@ -303,7 +303,8 @@ nv.models.axis = function() {
scale0 = scale.copy(); scale0 = scale.copy();
}); });
renderWatch.renderEnd('axis immediate');
return chart; return chart;
} }
@ -314,6 +315,7 @@ nv.models.axis = function() {
// expose chart's sub-components // expose chart's sub-components
chart.axis = axis; chart.axis = axis;
chart.dispatch = dispatch;
d3.rebind(chart, axis, 'orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat'); d3.rebind(chart, axis, 'orient', 'tickValues', 'tickSubdivide', 'tickSize', 'tickPadding', 'tickFormat');
d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use d3.rebind(chart, scale, 'domain', 'range', 'rangeBand', 'rangeBands'); //these are also accessible by chart.scale(), but added common ones directly for ease of use
@ -398,6 +400,14 @@ nv.models.axis = function() {
return chart; return chart;
}; };
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
renderWatch.reset(duration);
return chart;
};
//============================================================ //============================================================

@ -36,8 +36,9 @@ nv.models.cumulativeLineChart = function() {
, defaultState = null , defaultState = null
, noData = 'No Data Available.' , noData = 'No Data Available.'
, average = function(d) { return d.average } , average = function(d) { return d.average }
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
, transitionDuration = 250 , transitionDuration = 250
, duration = 250
, noErrorCheck = false //if set to TRUE, will bypass an error check in the indexify function. , noErrorCheck = false //if set to TRUE, will bypass an error check in the indexify function.
; ;
@ -56,9 +57,11 @@ nv.models.cumulativeLineChart = function() {
// Private Variables // Private Variables
//------------------------------------------------------------ //------------------------------------------------------------
var dx = d3.scale.linear() var dx = d3.scale.linear()
, index = {i: 0, x: 0} , index = {i: 0, x: 0}
; , renderWatch = nv.utils.renderWatch(dispatch, duration)
;
var showTooltip = function(e, offsetElement) { var showTooltip = function(e, offsetElement) {
var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ), var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
@ -73,6 +76,10 @@ nv.models.cumulativeLineChart = function() {
//============================================================ //============================================================
function chart(selection) { function chart(selection) {
renderWatch.reset();
renderWatch.models(lines);
if (showXAxis) renderWatch.models(xAxis);
if (showYAxis) renderWatch.models(yAxis);
selection.each(function(data) { selection.each(function(data) {
var container = d3.select(this).classed('nv-chart-' + id, true), var container = d3.select(this).classed('nv-chart-' + id, true),
that = this; that = this;
@ -83,7 +90,12 @@ nv.models.cumulativeLineChart = function() {
- margin.top - margin.bottom; - margin.top - margin.bottom;
chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; chart.update = function() {
if (duration === 0)
container.call(chart);
else
container.transition().duration(duration).call(chart)
};
chart.container = this; chart.container = this;
//set state.disabled //set state.disabled
@ -394,7 +406,7 @@ nv.models.cumulativeLineChart = function() {
g.select('.nv-x.nv-axis') g.select('.nv-x.nv-axis')
.attr('transform', 'translate(0,' + y.range()[0] + ')'); .attr('transform', 'translate(0,' + y.range()[0] + ')');
d3.transition(g.select('.nv-x.nv-axis')) g.select('.nv-x.nv-axis')
.call(xAxis); .call(xAxis);
} }
@ -405,7 +417,7 @@ nv.models.cumulativeLineChart = function() {
.ticks( availableHeight / 36 ) .ticks( availableHeight / 36 )
.tickSize( -availableWidth, 0); .tickSize( -availableWidth, 0);
d3.transition(g.select('.nv-y.nv-axis')) g.select('.nv-y.nv-axis')
.call(yAxis); .call(yAxis);
} }
//------------------------------------------------------------ //------------------------------------------------------------
@ -422,10 +434,10 @@ nv.models.cumulativeLineChart = function() {
//When dragging the index line, turn off line transitions. //When dragging the index line, turn off line transitions.
// Then turn them back on when done dragging. // Then turn them back on when done dragging.
var oldDuration = chart.transitionDuration(); var oldDuration = chart.duration();
chart.transitionDuration(0); chart.duration(0);
chart.update(); chart.update();
chart.transitionDuration(oldDuration); chart.duration(oldDuration);
} }
g.select('.nv-background rect') g.select('.nv-background rect')
@ -564,6 +576,8 @@ nv.models.cumulativeLineChart = function() {
}); });
renderWatch.renderEnd('cumulativeLineChart immediate');
return chart; return chart;
} }
@ -716,8 +730,17 @@ nv.models.cumulativeLineChart = function() {
}; };
chart.transitionDuration = function(_) { chart.transitionDuration = function(_) {
if (!arguments.length) return transitionDuration; nv.deprecated('cumulativeLineChart.transitionDuration');
transitionDuration = _; return chart.duration(_);
};
chart.duration = function(_) {
if(!arguments.length) return duration;
duration = _;
lines.duration(duration);
xAxis.duration(duration);
yAxis.duration(duration);
renderWatch.reset(duration);
return chart; return chart;
}; };

@ -13,6 +13,8 @@ nv.models.distribution = function() {
, color = nv.utils.defaultColor() , color = nv.utils.defaultColor()
, scale = d3.scale.linear() , scale = d3.scale.linear()
, domain , domain
, duration = 250
, dispatch = d3.dispatch('renderEnd')
; ;
//============================================================ //============================================================
@ -23,11 +25,13 @@ nv.models.distribution = function() {
//------------------------------------------------------------ //------------------------------------------------------------
var scale0; var scale0;
var renderWatch = nv.utils.renderWatch(dispatch, duration);
//============================================================ //============================================================
function chart(selection) { function chart(selection) {
renderWatch.reset();
selection.each(function(data) { selection.each(function(data) {
var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom), var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom),
naxis = axis == 'x' ? 'y' : 'x', naxis = axis == 'x' ? 'y' : 'x',
@ -68,8 +72,8 @@ nv.models.distribution = function() {
dist.enter().append('line') dist.enter().append('line')
.attr(axis + '1', function(d,i) { return scale0(getData(d,i)) }) .attr(axis + '1', function(d,i) { return scale0(getData(d,i)) })
.attr(axis + '2', function(d,i) { return scale0(getData(d,i)) }) .attr(axis + '2', function(d,i) { return scale0(getData(d,i)) })
distWrap.exit().selectAll('line.nv-dist' + axis) renderWatch.transition(distWrap.exit().selectAll('line.nv-dist' + axis), 'dist exit')
.transition() // .transition()
.attr(axis + '1', function(d,i) { return scale(getData(d,i)) }) .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
.attr(axis + '2', function(d,i) { return scale(getData(d,i)) }) .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
.style('stroke-opacity', 0) .style('stroke-opacity', 0)
@ -78,8 +82,8 @@ nv.models.distribution = function() {
.attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i }) .attr('class', function(d,i) { return 'nv-dist' + axis + ' nv-dist' + axis + '-' + i })
.attr(naxis + '1', 0) .attr(naxis + '1', 0)
.attr(naxis + '2', size); .attr(naxis + '2', size);
dist renderWatch.transition(dist, 'dist')
.transition() // .transition()
.attr(axis + '1', function(d,i) { return scale(getData(d,i)) }) .attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
.attr(axis + '2', function(d,i) { return scale(getData(d,i)) }) .attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
@ -87,7 +91,7 @@ nv.models.distribution = function() {
scale0 = scale.copy(); scale0 = scale.copy();
}); });
renderWatch.renderEnd('distribution immediate');
return chart; return chart;
} }
@ -96,6 +100,7 @@ nv.models.distribution = function() {
// Expose Public Variables // Expose Public Variables
//------------------------------------------------------------ //------------------------------------------------------------
chart.options = nv.utils.optionsFunc.bind(chart); chart.options = nv.utils.optionsFunc.bind(chart);
chart.dispatch = dispatch;
chart.margin = function(_) { chart.margin = function(_) {
if (!arguments.length) return margin; if (!arguments.length) return margin;
@ -141,6 +146,13 @@ nv.models.distribution = function() {
color = nv.utils.getColor(_); color = nv.utils.getColor(_);
return chart; return chart;
}; };
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
renderWatch.reset(duration);
return chart;
};
//============================================================ //============================================================

@ -20,6 +20,8 @@ nv.models.line = function() {
, x //can be accessed via chart.xScale() , x //can be accessed via chart.xScale()
, y //can be accessed via chart.yScale() , y //can be accessed via chart.yScale()
, interpolate = "linear" // controls the line interpolation , interpolate = "linear" // controls the line interpolation
, duration = 250
, dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
; ;
scatter scatter
@ -35,12 +37,15 @@ nv.models.line = function() {
//------------------------------------------------------------ //------------------------------------------------------------
var x0, y0 //used to store previous scales var x0, y0 //used to store previous scales
; , renderWatch = nv.utils.renderWatch(dispatch, duration)
;
//============================================================ //============================================================
function chart(selection) { function chart(selection) {
renderWatch.reset();
renderWatch.models(scatter);
selection.each(function(data) { selection.each(function(data) {
var availableWidth = width - margin.left - margin.right, var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom, availableHeight = height - margin.top - margin.bottom,
@ -84,7 +89,7 @@ nv.models.line = function() {
var scatterWrap = wrap.select('.nv-scatterWrap'); var scatterWrap = wrap.select('.nv-scatterWrap');
//.datum(data); // Data automatically trickles down from the wrap //.datum(data); // Data automatically trickles down from the wrap
scatterWrap.transition().call(scatter); scatterWrap.call(scatter);
@ -102,7 +107,6 @@ nv.models.line = function() {
var groups = wrap.select('.nv-groups').selectAll('.nv-group') var groups = wrap.select('.nv-groups').selectAll('.nv-group')
.data(function(d) { return d }, function(d) { return d.key }); .data(function(d) { return d }, function(d) { return d.key });
groups.enter().append('g') groups.enter().append('g')
@ -116,8 +120,7 @@ nv.models.line = function() {
.classed('hover', function(d) { return d.hover }) .classed('hover', function(d) { return d.hover })
.style('fill', function(d,i){ return color(d, i) }) .style('fill', function(d,i){ return color(d, i) })
.style('stroke', function(d,i){ return color(d, i)}); .style('stroke', function(d,i){ return color(d, i)});
groups groups.watchTransition(renderWatch, 'line: groups')
.transition()
.style('stroke-opacity', 1) .style('stroke-opacity', 1)
.style('fill-opacity', .5); .style('fill-opacity', .5);
@ -140,8 +143,7 @@ nv.models.line = function() {
groups.exit().selectAll('path.nv-area') groups.exit().selectAll('path.nv-area')
.remove(); .remove();
areaPaths areaPaths.watchTransition(renderWatch, 'line: areaPaths')
.transition()
.attr('d', function(d) { .attr('d', function(d) {
return d3.svg.area() return d3.svg.area()
.interpolate(interpolate) .interpolate(interpolate)
@ -167,8 +169,7 @@ nv.models.line = function() {
.y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) }) .y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
); );
linePaths linePaths.watchTransition(renderWatch, 'line: linePaths')
.transition()
.attr('d', .attr('d',
d3.svg.line() d3.svg.line()
.interpolate(interpolate) .interpolate(interpolate)
@ -184,7 +185,7 @@ nv.models.line = function() {
y0 = y.copy(); y0 = y.copy();
}); });
renderWatch.renderEnd('line immediate');
return chart; return chart;
} }
@ -193,8 +194,12 @@ nv.models.line = function() {
// Expose Public Variables // Expose Public Variables
//------------------------------------------------------------ //------------------------------------------------------------
chart.dispatch = scatter.dispatch; chart.dispatch = dispatch;
chart.scatter = scatter; chart.scatter = scatter;
// Pass through events
scatter.dispatch.on('elementClick', function(){ dispatch.elementClick.apply(this, arguments); })
scatter.dispatch.on('elementMouseover', function(){ dispatch.elementMouseover.apply(this, arguments); })
scatter.dispatch.on('elementMouseout', function(){ dispatch.elementMouseout.apply(this, arguments); })
d3.rebind(chart, scatter, 'id', 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', d3.rebind(chart, scatter, 'id', 'interactive', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange',
'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi', 'clipRadius', 'padData','highlightPoint','clearHighlights'); 'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi', 'clipRadius', 'padData','highlightPoint','clearHighlights');
@ -267,6 +272,14 @@ nv.models.line = function() {
return chart; return chart;
}; };
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
renderWatch.reset(duration);
scatter.duration(duration);
return chart;
};
//============================================================ //============================================================

@ -1,467 +1,252 @@
/* global nv, d3, Chart */
nv.models.lineChart = function() {
"use strict"; nv.models.lineChart = function () {
//============================================================ 'use strict';
// Public Variables with Default Settings
//------------------------------------------------------------ var defaults = {
margin: {
var lines = nv.models.line() top: 30,
, xAxis = nv.models.axis() right: 20,
, yAxis = nv.models.axis() bottom: 50,
, legend = nv.models.legend() left: 60
, interactiveLayer = nv.interactiveGuideline() },
; dispatch: [
'tooltipShow',
var margin = {top: 30, right: 20, bottom: 50, left: 60} 'tooltipHide',
, color = nv.utils.defaultColor() 'stateChange',
, width = null 'changeState',
, height = null 'renderEnd'
, showLegend = true ]
, showXAxis = true };
, showYAxis = true
, rightAlignYAxis = false //============================================================
, useInteractiveGuideline = false // Public Variables with Default Settings
, tooltips = true //------------------------------------------------------------
, tooltip = function(key, x, y, e, graph) { var lines = nv.models.line(),
return '<h3>' + key + '</h3>' + interactiveLayer = nv.interactiveGuideline(),
'<p>' + y + ' at ' + x + '</p>' inst = Chart.call(null, defaults);
}
, x //============================================================
, y //============================================================
, state = {} // Private Variables
, defaultState = null //------------------------------------------------------------
, noData = 'No Data Available.' var showTooltip = function(canvas){
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') return function (e, offsetElement) {
, transitionDuration = 250 var left = e.pos[0] + (offsetElement.offsetLeft || 0),
; top = e.pos[1] + (offsetElement.offsetTop || 0),
x = canvas.axis.x.tickFormat()(canvas.scale.x()(e.point, e.pointIndex)),
xAxis y = canvas.axis.y.tickFormat()(canvas.scale.y()(e.point, e.pointIndex)),
.orient('bottom') content = canvas.tooltip(e.series.key, x, y, e, chart);
.tickPadding(7) nv.tooltip.show([left, top], content, null, null, offsetElement);
; };
yAxis };
.orient((rightAlignYAxis) ? 'right' : 'left') // var renderWatch = nv.utils.renderWatch(canvas.dispatch, otions.duration);
;
//============================================================
//============================================================ function chart(selection) {
// renderWatch.reset();
// renderWatch.models(lines);
//============================================================ // if (showXAxis) renderWatch.models(xAxis);
// Private Variables // if (showYAxis) renderWatch.models(yAxis);
//------------------------------------------------------------
selection.each(function (data) {
var showTooltip = function(e, offsetElement) { Chart.canvas(inst, this);
var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ), var canvas = inst;
top = e.pos[1] + ( offsetElement.offsetTop || 0), var container = canvas.svg,
x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)), that = this;
y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
content = tooltip(e.series.key, x, y, e, chart); canvas.update = chart.update = function () {
if (canvas.duration === 0)
nv.tooltip.show([left, top], content, null, null, offsetElement); canvas.svg.call(chart);
}; else
canvas.svg.transition().duration(canvas.duration).call(chart)
//============================================================ };
nv.models.lineChart.build(canvas, data, lines);
function chart(selection) { });
selection.each(function(data) { // renderWatch.renderEnd('lineChart immediate');
var container = d3.select(this),
that = this;
var availableWidth = (width || parseInt(container.style('width')) || 960)
- margin.left - margin.right,
availableHeight = (height || parseInt(container.style('height')) || 400)
- margin.top - margin.bottom;
chart.update = function() { container.transition().duration(transitionDuration).call(chart) };
chart.container = this;
//set state.disabled
state.disabled = data.map(function(d) { return !!d.disabled });
if (!defaultState) {
var key;
defaultState = {};
for (key in state) {
if (state[key] instanceof Array)
defaultState[key] = state[key].slice(0);
else
defaultState[key] = state[key];
}
}
//------------------------------------------------------------
// Display noData message if there's nothing to show.
if (!data || !data.length || !data.filter(function(d) { return d.values.length }).length) {
var noDataText = container.selectAll('.nv-noData').data([noData]);
noDataText.enter().append('text')
.attr('class', 'nvd3 nv-noData')
.attr('dy', '-.7em')
.style('text-anchor', 'middle');
noDataText
.attr('x', margin.left + availableWidth / 2)
.attr('y', margin.top + availableHeight / 2)
.text(function(d) { return d });
return chart; return chart;
} else { }
container.selectAll('.nv-noData').remove();
}
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Scales
x = lines.xScale();
y = lines.yScale();
//------------------------------------------------------------
//------------------------------------------------------------
// Setup containers and skeleton of chart
var wrap = container.selectAll('g.nv-wrap.nv-lineChart').data([data]);
var gEnter = wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineChart').append('g');
var g = wrap.select('g');
gEnter.append("rect").style("opacity",0);
gEnter.append('g').attr('class', 'nv-x nv-axis');
gEnter.append('g').attr('class', 'nv-y nv-axis');
gEnter.append('g').attr('class', 'nv-linesWrap');
gEnter.append('g').attr('class', 'nv-legendWrap');
gEnter.append('g').attr('class', 'nv-interactive');
g.select("rect")
.attr("width",availableWidth)
.attr("height",(availableHeight > 0) ? availableHeight : 0);
//------------------------------------------------------------
// Legend
if (showLegend) {
legend.width(availableWidth);
g.select('.nv-legendWrap')
.datum(data)
.call(legend);
if ( margin.top != legend.height()) {
margin.top = legend.height();
availableHeight = (height || parseInt(container.style('height')) || 400)
- margin.top - margin.bottom;
}
wrap.select('.nv-legendWrap')
.attr('transform', 'translate(0,' + (-margin.top) +')')
}
//------------------------------------------------------------
wrap.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
if (rightAlignYAxis) {
g.select(".nv-y.nv-axis")
.attr("transform", "translate(" + availableWidth + ",0)");
}
//------------------------------------------------------------
// Main Chart Component(s)
//------------------------------------------------------------
//Set up interactive layer
if (useInteractiveGuideline) {
interactiveLayer
.width(availableWidth)
.height(availableHeight)
.margin({left:margin.left, top:margin.top})
.svgContainer(container)
.xScale(x);
wrap.select(".nv-interactive").call(interactiveLayer);
}
lines
.width(availableWidth)
.height(availableHeight)
.color(data.map(function(d,i) {
return d.color || color(d, i);
}).filter(function(d,i) { return !data[i].disabled }));
var linesWrap = g.select('.nv-linesWrap')
.datum(data.filter(function(d) { return !d.disabled }))
linesWrap.transition().call(lines);
//------------------------------------------------------------ //============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
// lines.dispatch.on('elementMouseover.tooltip', function (e) {
// e.pos = [e.pos[0] + canvas.margin.left, e.pos[1] + canvas.margin.top];
// canvas.dispatch.tooltipShow(e);
// });
// lines.dispatch.on('elementMouseout.tooltip', function (e) {
// canvas.dispatch.tooltipHide(e);
// });
// canvas.dispatch.on('tooltipHide', function () {
// if (canvas.tooltips) nv.tooltip.cleanup();
// });
Chart.attachCallable(inst, chart);
inst.interactiveLayer = chart.interactiveLayer = interactiveLayer;
chart.duration = function (_) {
if (!arguments.length) return inst.duration;
inst.duration = _;
// renderWatch.reset(duration);
lines.duration(chart.duration);
inst.axis.x.duration(duration);
inst.axis.y.duration(duration);
return chart;
}
d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'useVoronoi','id', 'interpolate');
//------------------------------------------------------------ //============================================================
// Setup Axes return chart;
}
if (showXAxis) { nv.models.lineChart.build = function(chart, data, lines){
xAxis Chart.defaultState.set(chart, data);
.scale(x)
.ticks( availableWidth / 100 )
.tickSize(-availableHeight, 0);
g.select('.nv-x.nv-axis') if(Chart.checkData(chart, data)){
.attr('transform', 'translate(0,' + y.range()[0] + ')'); return chart;
g.select('.nv-x.nv-axis') }
.transition()
.call(xAxis);
}
if (showYAxis) { nv.models.lineChart.wrap(chart, data);
yAxis Chart.legend.build(chart, data);
.scale(y)
.ticks( availableHeight / 36 )
.tickSize( -availableWidth, 0);
g.select('.nv-y.nv-axis') //------------------------------------------------------------
.transition() chart.wrap.attr('transform', 'translate(' + chart.margin.left + ',' + chart.margin.top + ')');
.call(yAxis); if (chart.axis.rightAlignY) {
} chart.g.select('.nv-y.nv-axis')
//------------------------------------------------------------ .attr('transform', 'translate(' + chart.size.available.width + ',0)');
}
//------------------------------------------------------------
// Main Chart Component(s)
//------------------------------------------------------------
//Set up interactive layer
if (chart.useInteractiveGuideline) {
chart.interactiveLayer
.width(chart.size.available.width)
.height(chart.size.available.height)
.margin({
left: chart.margin.left,
top: chart.margin.top
})
.svgContainer(chart.svg)
.xScale(chart.scale.x);
chart.wrap.select('.nv-interactive').call(chart.interactiveLayer);
}
//============================================================ nv.models.lineChart.lines(chart, data, lines);
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
legend.dispatch.on('stateChange', function(newState) { //------------------------------------------------------------
state = newState; //------------------------------------------------------------
dispatch.stateChange(state); // Setup Axes
chart.update(); Chart.axis.build(chart, data);
});
interactiveLayer.dispatch.on('elementMousemove', function(e) { //------------------------------------------------------------
lines.clearHighlights(); //============================================================
var singlePoint, pointIndex, pointXLocation, allData = []; // Event Handling/Dispatching (in chart's scope)
data //------------------------------------------------------------
.filter(function(series, i) { chart.legend.dispatch.on('stateChange', function (newState) {
series.seriesIndex = i; chart.state = newState;
return !series.disabled; chart.dispatch.stateChange(chart.state);
}) chart.update();
.forEach(function(series,i) { });
pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x()); chart.interactiveLayer.dispatch.on('elementMousemove', function (e) {
lines.highlightPoint(i, pointIndex, true); lines.clearHighlights();
var point = series.values[pointIndex]; var singlePoint, pointIndex, pointXLocation, allData = [];
if (typeof point === 'undefined') return; data
if (typeof singlePoint === 'undefined') singlePoint = point; .filter(function (series, i) {
if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex)); series.seriesIndex = i;
allData.push({ return !series.disabled;
key: series.key, })
value: chart.y()(point, pointIndex), .forEach(function (series, i) {
color: color(series,series.seriesIndex) pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.scale.x());
}); lines.highlightPoint(i, pointIndex, true);
}); var point = series.values[pointIndex];
//Highlight the tooltip entry based on which point the mouse is closest to. if (typeof point === 'undefined') return;
if (allData.length > 2) { if (typeof singlePoint === 'undefined') singlePoint = point;
if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.scale.x()(point, pointIndex));
allData.push({
key: series.key,
value: chart.scale.y()(point, pointIndex),
color: chart.color(series, series.seriesIndex)
});
});
//Highlight the tooltip entry based on which point the mouse is closest to.
if (allData.length > 2) {
var yValue = chart.yScale().invert(e.mouseY); var yValue = chart.yScale().invert(e.mouseY);
var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]); var domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
var threshold = 0.03 * domainExtent; var threshold = 0.03 * domainExtent;
var indexToHighlight = nv.nearestValueIndex(allData.map(function(d){return d.value}),yValue,threshold); var indexToHighlight = nv.nearestValueIndex(allData.map(function (d) {
return d.value
}), yValue, threshold);
if (indexToHighlight !== null) if (indexToHighlight !== null)
allData[indexToHighlight].highlight = true; allData[indexToHighlight].highlight = true;
} }
var xValue = chart.axis.x.tickFormat()(chart.scale.x()(singlePoint, pointIndex));
var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex)); chart.interactiveLayer.tooltip
interactiveLayer.tooltip .position({
.position({left: pointXLocation + margin.left, top: e.mouseY + margin.top}) left: pointXLocation + chart.margin.left,
.chartContainer(that.parentNode) top: e.mouseY + chart.margin.top
.enabled(tooltips) })
.valueFormatter(function(d,i) { .chartContainer(that.parentNode)
return yAxis.tickFormat()(d); .enabled(chart.tooltips)
}) .valueFormatter(function (d, i) {
.data( return chart.axis.y.tickFormat()(d);
{ })
value: xValue, .data({
series: allData value: xValue,
} series: allData
)(); })();
chart.interactiveLayer.renderGuideLine(pointXLocation);
interactiveLayer.renderGuideLine(pointXLocation); });
chart.interactiveLayer.dispatch.on('elementMouseout', function (e) {
}); chart.dispatch.tooltipHide();
lines.clearHighlights();
interactiveLayer.dispatch.on("elementMouseout",function(e) { });
dispatch.tooltipHide(); chart.dispatch.on('tooltipShow', function (e) {
lines.clearHighlights(); if (chart.tooltips) showTooltip(e, that.parentNode);
}); });
chart.dispatch.on('changeState', function (e) {
dispatch.on('tooltipShow', function(e) {
if (tooltips) showTooltip(e, that.parentNode);
});
dispatch.on('changeState', function(e) {
if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) { if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
data.forEach(function(series,i) { data.forEach(function (series, i) {
series.disabled = e.disabled[i]; series.disabled = e.disabled[i];
}); });
chart.state.disabled = e.disabled;
state.disabled = e.disabled;
} }
chart.update(); chart.update();
});
//============================================================
}); });
};
nv.models.lineChart.wrap = function(chart, data) {
chart.wrap = chart.svg.selectAll('g.nv-wrap.nv-lineChart').data([data]);
var gEnter = chart.wrap.enter().append('g').attr('class', 'nvd3 nv-wrap nv-lineChart').append('g');
chart.g = chart.wrap.select('g');
gEnter.append('rect').style('opacity', 0);
gEnter.append('g').attr('class', 'nv-x nv-axis');
gEnter.append('g').attr('class', 'nv-y nv-axis');
gEnter.append('g').attr('class', 'nv-linesWrap');
gEnter.append('g').attr('class', 'nv-legendWrap');
gEnter.append('g').attr('class', 'nv-interactive');
chart.g.select('rect')
.attr({
width: chart.size.available.width,
height: Math.max(chart.size.available.height, 0)
});
};
nv.models.lineChart.lines = function(chart, data, lines){
lines
.width(chart.size.available.width)
.height(chart.size.available.height)
.color(data.map(function (d, i) {
return d.color || chart.color(d, i);
}).filter(function (d, i) {
return !data[i].disabled
}));
var linesWrap = chart.g.select('.nv-linesWrap')
.datum(data.filter(function (d) {
return !d.disabled
}))
linesWrap.call(lines);
};
return chart;
}
//============================================================
// Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------
lines.dispatch.on('elementMouseover.tooltip', function(e) {
e.pos = [e.pos[0] + margin.left, e.pos[1] + margin.top];
dispatch.tooltipShow(e);
});
lines.dispatch.on('elementMouseout.tooltip', function(e) {
dispatch.tooltipHide(e);
});
dispatch.on('tooltipHide', function() {
if (tooltips) nv.tooltip.cleanup();
});
//============================================================
//============================================================
// Expose Public Variables
//------------------------------------------------------------
// expose chart's sub-components
chart.dispatch = dispatch;
chart.lines = lines;
chart.legend = legend;
chart.xAxis = xAxis;
chart.yAxis = yAxis;
chart.interactiveLayer = interactiveLayer;
d3.rebind(chart, lines, 'defined', 'isArea', 'x', 'y', 'size', 'xScale', 'yScale', 'xDomain', 'yDomain', 'xRange', 'yRange'
, 'forceX', 'forceY', 'interactive', 'clipEdge', 'clipVoronoi', 'useVoronoi','id', 'interpolate');
chart.options = nv.utils.optionsFunc.bind(chart);
chart.margin = function(_) {
if (!arguments.length) return margin;
margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
margin.right = typeof _.right != 'undefined' ? _.right : margin.right;
margin.bottom = typeof _.bottom != 'undefined' ? _.bottom : margin.bottom;
margin.left = typeof _.left != 'undefined' ? _.left : margin.left;
return chart;
};
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.color = function(_) {
if (!arguments.length) return color;
color = nv.utils.getColor(_);
legend.color(color);
return chart;
};
chart.showLegend = function(_) {
if (!arguments.length) return showLegend;
showLegend = _;
return chart;
};
chart.showXAxis = function(_) {
if (!arguments.length) return showXAxis;
showXAxis = _;
return chart;
};
chart.showYAxis = function(_) {
if (!arguments.length) return showYAxis;
showYAxis = _;
return chart;
};
chart.rightAlignYAxis = function(_) {
if(!arguments.length) return rightAlignYAxis;
rightAlignYAxis = _;
yAxis.orient( (_) ? 'right' : 'left');
return chart;
};
chart.useInteractiveGuideline = function(_) {
if(!arguments.length) return useInteractiveGuideline;
useInteractiveGuideline = _;
if (_ === true) {
chart.interactive(false);
chart.useVoronoi(false);
}
return chart;
};
chart.tooltips = function(_) {
if (!arguments.length) return tooltips;
tooltips = _;
return chart;
};
chart.tooltipContent = function(_) {
if (!arguments.length) return tooltip;
tooltip = _;
return chart;
};
chart.state = function(_) {
if (!arguments.length) return state;
state = _;
return chart;
};
chart.defaultState = function(_) {
if (!arguments.length) return defaultState;
defaultState = _;
return chart;
};
chart.noData = function(_) {
if (!arguments.length) return noData;
noData = _;
return chart;
};
chart.transitionDuration = function(_) {
if (!arguments.length) return transitionDuration;
transitionDuration = _;
return chart;
};
//============================================================
return chart;
}

@ -21,13 +21,13 @@ nv.models.multiBar = function() {
, hideable = false , hideable = false
, barColor = null // adding the ability to set the color for each rather than the whole group , barColor = null // adding the ability to set the color for each rather than the whole group
, disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled , disabled // used in conjunction with barColor to communicate from multiBarHorizontalChart what series are disabled
, delay = 1200 , duration = 1000
, xDomain , xDomain
, yDomain , yDomain
, xRange , xRange
, yRange , yRange
, groupSpacing = 0.1 , groupSpacing = 0.1
, dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout') , dispatch = d3.dispatch('chartClick', 'elementClick', 'elementDblClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
; ;
//============================================================ //============================================================
@ -38,17 +38,30 @@ nv.models.multiBar = function() {
//------------------------------------------------------------ //------------------------------------------------------------
var x0, y0 //used to store previous scales var x0, y0 //used to store previous scales
, renderWatch = nv.utils.renderWatch(dispatch, duration)
; ;
//============================================================ //============================================================
function chart(selection) { function chart(selection) {
renderWatch.reset();
selection.each(function(data) { selection.each(function(data) {
var availableWidth = width - margin.left - margin.right, var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom, availableHeight = height - margin.top - margin.bottom,
container = d3.select(this); container = d3.select(this);
// This function defines the requirements for render complete
var endFn = function(d, i) {
if (d.series === data.length - 1 && i === data[0].values.length - 1)
return true;
return false;
}
if(hideable && data.length) hideable = [{ if(hideable && data.length) hideable = [{
values: data[0].values.map(function(d) { values: data[0].values.map(function(d) {
return { return {
@ -156,31 +169,29 @@ nv.models.multiBar = function() {
g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : ''); g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
var groups = wrap.select('.nv-groups').selectAll('.nv-group') var groups = wrap.select('.nv-groups').selectAll('.nv-group')
.data(function(d) { return d }, function(d,i) { return i }); .data(function(d) { return d }, function(d,i) { return i });
groups.enter().append('g') groups.enter().append('g')
.style('stroke-opacity', 1e-6) .style('stroke-opacity', 1e-6)
.style('fill-opacity', 1e-6); .style('fill-opacity', 1e-6);
groups.exit()
.transition() var exitTransition = renderWatch
.selectAll('rect.nv-bar') .transition(groups.exit().selectAll('rect.nv-bar'), 'multibarExit', Math.min(250, duration))
.delay(function(d,i) {
return i * delay/ data[0].values.length;
})
.attr('y', function(d) { return stacked ? y0(d.y0) : y0(0) }) .attr('y', function(d) { return stacked ? y0(d.y0) : y0(0) })
.attr('height', 0) .attr('height', 0)
.remove(); .remove();
if (exitTransition.delay)
exitTransition.delay(function(d,i) {
return i * duration / data[0].values.length;
});
groups groups
.attr('class', function(d,i) { return 'nv-group nv-series-' + i }) .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
.classed('hover', function(d) { return d.hover }) .classed('hover', function(d) { return d.hover })
.style('fill', function(d,i){ return color(d, i) }) .style('fill', function(d,i){ return color(d, i) })
.style('stroke', function(d,i){ return color(d, i) }); .style('stroke', function(d,i){ return color(d, i) });
groups groups
.transition()
.style('stroke-opacity', 1) .style('stroke-opacity', 1)
.style('fill-opacity', .75); .style('fill-opacity', 0.75);
var bars = groups.selectAll('rect.nv-bar') var bars = groups.selectAll('rect.nv-bar')
@ -251,7 +262,6 @@ nv.models.multiBar = function() {
}); });
bars bars
.attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'}) .attr('class', function(d,i) { return getY(d,i) < 0 ? 'nv-bar negative' : 'nv-bar positive'})
.transition()
.attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; }) .attr('transform', function(d,i) { return 'translate(' + x(getX(d,i)) + ',0)'; })
if (barColor) { if (barColor) {
@ -261,63 +271,61 @@ nv.models.multiBar = function() {
.style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); }); .style('stroke', function(d,i,j) { return d3.rgb(barColor(d,i)).darker( disabled.map(function(d,i) { return i }).filter(function(d,i){ return !disabled[i] })[j] ).toString(); });
} }
var barSelection =
bars.watchTransition(renderWatch, 'multibar', Math.min(250, duration))
.delay(function(d,i) {
return i * duration / data[0].values.length;
});
if (stacked) if (stacked)
bars.transition() barSelection
.delay(function(d,i) {
return i * delay / data[0].values.length;
})
.attr('y', function(d,i) { .attr('y', function(d,i) {
return y((stacked ? d.y1 : 0)); return y((stacked ? d.y1 : 0));
}) })
.attr('height', function(d,i) { .attr('height', function(d,i) {
return Math.max(Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0))),1); return Math.max(Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0))),1);
}) })
.attr('x', function(d,i) { .attr('x', function(d,i) {
return stacked ? 0 : (d.series * x.rangeBand() / data.length ) return stacked ? 0 : (d.series * x.rangeBand() / data.length )
}) })
.attr('width', x.rangeBand() / (stacked ? 1 : data.length) ); .attr('width', x.rangeBand() / (stacked ? 1 : data.length) );
else else
bars.transition() barSelection
.delay(function(d,i) {
return i * delay/ data[0].values.length;
})
.attr('x', function(d,i) { .attr('x', function(d,i) {
return d.series * x.rangeBand() / data.length return d.series * x.rangeBand() / data.length
}) })
.attr('width', x.rangeBand() / data.length) .attr('width', x.rangeBand() / data.length)
.attr('y', function(d,i) { .attr('y', function(d,i) {
return getY(d,i) < 0 ? return getY(d,i) < 0 ?
y(0) : y(0) :
y(0) - y(getY(d,i)) < 1 ? y(0) - y(getY(d,i)) < 1 ?
y(0) - 1 : y(0) - 1 :
y(getY(d,i)) || 0; y(getY(d,i)) || 0;
}) })
.attr('height', function(d,i) { .attr('height', function(d,i) {
return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0; return Math.max(Math.abs(y(getY(d,i)) - y(0)),1) || 0;
}); });
//store old scales for use in transitions on update //store old scales for use in transitions on update
x0 = x.copy(); x0 = x.copy();
y0 = y.copy(); y0 = y.copy();
}); });
renderWatch.renderEnd('multibar immediate');
return chart; return chart;
} }
//============================================================ //============================================================
// Expose Public Variables // Expose Public Variables
//------------------------------------------------------------ //------------------------------------------------------------
chart.dispatch = dispatch; // As a getter, returns a new instance of d3 dispatch and sets appropriate vars.
// As a setter, sets dispatch.
// Useful when same chart instance is used to render several data models.
// Since dispatch is instance-specific, it cannot be contained inside chart model.
chart.options = nv.utils.optionsFunc.bind(chart); chart.dispatch = dispatch;
chart.x = function(_) { chart.x = function(_) {
if (!arguments.length) return getX; if (!arguments.length) return getX;
@ -442,20 +450,33 @@ nv.models.multiBar = function() {
return chart; return chart;
}; };
chart.delay = function(_) {
if (!arguments.length) return delay;
delay = _;
return chart;
};
chart.groupSpacing = function(_) { chart.groupSpacing = function(_) {
if (!arguments.length) return groupSpacing; if (!arguments.length) return groupSpacing;
groupSpacing = _; groupSpacing = _;
return chart; return chart;
}; };
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
renderWatch.reset(duration);
return chart;
}
//============================================================ //============================================================
// Deprecated Methods
//------------------------------------------------------------
chart.delay = function(_) {
nv.deprecated('multiBar.delay');
return chart.duration(_);
};
chart.options = nv.utils.optionsFunc.bind(chart);
//============================================================
return chart; return chart;
} }

@ -34,9 +34,10 @@ nv.models.multiBarChart = function() {
, state = { stacked: false } , state = { stacked: false }
, defaultState = null , defaultState = null
, noData = "No Data Available." , noData = "No Data Available."
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
, controlWidth = function() { return showControls ? 180 : 0 } , controlWidth = function() { return showControls ? 180 : 0 }
, transitionDuration = 250 , duration = 250
; ;
multibar multibar
@ -61,7 +62,8 @@ nv.models.multiBarChart = function() {
//============================================================ //============================================================
// Private Variables // Private Variables
//------------------------------------------------------------ //------------------------------------------------------------
var renderWatch = nv.utils.renderWatch(dispatch);
var showTooltip = function(e, offsetElement) { var showTooltip = function(e, offsetElement) {
var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ), var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
top = e.pos[1] + ( offsetElement.offsetTop || 0), top = e.pos[1] + ( offsetElement.offsetTop || 0),
@ -76,6 +78,11 @@ nv.models.multiBarChart = function() {
function chart(selection) { function chart(selection) {
renderWatch.reset();
renderWatch.models(multibar);
if (showXAxis) renderWatch.models(xAxis);
if (showYAxis) renderWatch.models(yAxis);
selection.each(function(data) { selection.each(function(data) {
var container = d3.select(this), var container = d3.select(this),
that = this; that = this;
@ -85,7 +92,14 @@ nv.models.multiBarChart = function() {
availableHeight = (height || parseInt(container.style('height')) || 400) availableHeight = (height || parseInt(container.style('height')) || 400)
- margin.top - margin.bottom; - margin.top - margin.bottom;
chart.update = function() { container.transition().duration(transitionDuration).call(chart) }; chart.update = function() {
if (duration === 0)
container.call(chart);
else
container.transition()
.duration(duration)
.call(chart);
};
chart.container = this; chart.container = this;
//set state.disabled //set state.disabled
@ -219,7 +233,7 @@ nv.models.multiBarChart = function() {
var barsWrap = g.select('.nv-barsWrap') var barsWrap = g.select('.nv-barsWrap')
.datum(data.filter(function(d) { return !d.disabled })) .datum(data.filter(function(d) { return !d.disabled }))
barsWrap.transition().call(multibar); barsWrap.call(multibar);
//------------------------------------------------------------ //------------------------------------------------------------
@ -235,7 +249,7 @@ nv.models.multiBarChart = function() {
g.select('.nv-x.nv-axis') g.select('.nv-x.nv-axis')
.attr('transform', 'translate(0,' + y.range()[0] + ')'); .attr('transform', 'translate(0,' + y.range()[0] + ')');
g.select('.nv-x.nv-axis').transition() g.select('.nv-x.nv-axis')
.call(xAxis); .call(xAxis);
var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g'); var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');
@ -283,13 +297,13 @@ nv.models.multiBarChart = function() {
} }
if (showYAxis) { if (showYAxis) {
yAxis yAxis
.scale(y) .scale(y)
.ticks( availableHeight / 36 ) .ticks( availableHeight / 36 )
.tickSize( -availableWidth, 0); .tickSize( -availableWidth, 0);
g.select('.nv-y.nv-axis').transition() g.select('.nv-y.nv-axis')
.call(yAxis); .call(yAxis);
} }
@ -359,6 +373,8 @@ nv.models.multiBarChart = function() {
}); });
renderWatch.renderEnd('multibarchart immediate');
return chart; return chart;
} }
@ -379,6 +395,7 @@ nv.models.multiBarChart = function() {
if (tooltips) nv.tooltip.cleanup(); if (tooltips) nv.tooltip.cleanup();
}); });
//============================================================ //============================================================
@ -414,12 +431,14 @@ nv.models.multiBarChart = function() {
}; };
chart.height = function(_) { chart.height = function(_) {
if (!arguments.length) return height; if (!arguments.length) return height;
height = _; height = _;
return chart; return chart;
}; };
chart.color = function(_) { chart.color = function(_) {
if (!arguments.length) return color; if (!arguments.length) return color;
color = nv.utils.getColor(_); color = nv.utils.getColor(_);
legend.color(color); legend.color(color);
@ -512,11 +531,20 @@ nv.models.multiBarChart = function() {
}; };
chart.transitionDuration = function(_) { chart.transitionDuration = function(_) {
if (!arguments.length) return transitionDuration; nv.deprecated('multiBarChart.transitionDuration');
transitionDuration = _; return chart.duration(_);
return chart;
}; };
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
multibar.duration(duration);
xAxis.duration(duration);
yAxis.duration(duration);
renderWatch.reset(duration);
return chart;
}
//============================================================ //============================================================

@ -36,8 +36,9 @@ nv.models.scatter = function() {
, sizeDomain = null // Override point size domain , sizeDomain = null // Override point size domain
, sizeRange = null , sizeRange = null
, singlePoint = false , singlePoint = false
, dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout') , dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
, useVoronoi = true , useVoronoi = true
, duration = 250
; ;
//============================================================ //============================================================
@ -50,12 +51,14 @@ nv.models.scatter = function() {
var x0, y0, z0 // used to store previous scales var x0, y0, z0 // used to store previous scales
, timeoutID , timeoutID
, needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips , needsUpdate = false // Flag for when the points are visually updating, but the interactive layer is behind, to disable tooltips
, renderWatch = nv.utils.renderWatch(dispatch, duration)
; ;
//============================================================ //============================================================
function chart(selection) { function chart(selection) {
renderWatch.reset();
selection.each(function(data) { selection.each(function(data) {
var availableWidth = width - margin.left - margin.right, var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom, availableHeight = height - margin.top - margin.bottom,
@ -333,7 +336,6 @@ nv.models.scatter = function() {
} }
needsUpdate = true; needsUpdate = true;
var groups = wrap.select('.nv-groups').selectAll('.nv-group') var groups = wrap.select('.nv-groups').selectAll('.nv-group')
.data(function(d) { return d }, function(d) { return d.key }); .data(function(d) { return d }, function(d) { return d.key });
groups.enter().append('g') groups.enter().append('g')
@ -344,8 +346,7 @@ nv.models.scatter = function() {
groups groups
.attr('class', function(d,i) { return 'nv-group nv-series-' + i }) .attr('class', function(d,i) { return 'nv-group nv-series-' + i })
.classed('hover', function(d) { return d.hover }); .classed('hover', function(d) { return d.hover });
groups groups.watchTransition(renderWatch, 'scatter: groups')
.transition()
.style('fill', function(d,i) { return color(d, i) }) .style('fill', function(d,i) { return color(d, i) })
.style('stroke', function(d,i) { return color(d, i) }) .style('stroke', function(d,i) { return color(d, i) })
.style('stroke-opacity', 1) .style('stroke-opacity', 1)
@ -353,7 +354,6 @@ nv.models.scatter = function() {
if (onlyCircles) { if (onlyCircles) {
var points = groups.selectAll('circle.nv-point') var points = groups.selectAll('circle.nv-point')
.data(function(d) { return d.values }, pointKey); .data(function(d) { return d.values }, pointKey);
points.enter().append('circle') points.enter().append('circle')
@ -363,7 +363,8 @@ nv.models.scatter = function() {
.attr('cy', function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) }) .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
.attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) }); .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
points.exit().remove(); points.exit().remove();
groups.exit().selectAll('path.nv-point').transition() groups.exit().selectAll('path.nv-point')
.watchTransition(renderWatch, 'scatter exit')
.attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
.attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) }) .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
.remove(); .remove();
@ -374,7 +375,8 @@ nv.models.scatter = function() {
.classed('hover',false) .classed('hover',false)
; ;
}); });
points.transition() points
.watchTransition(renderWatch, 'scatter points')
.attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) }) .attr('cx', function(d,i) { return nv.utils.NaNtoZero(x(getX(d,i))) })
.attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) }) .attr('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
.attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) }); .attr('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
@ -396,7 +398,7 @@ nv.models.scatter = function() {
); );
points.exit().remove(); points.exit().remove();
groups.exit().selectAll('path.nv-point') groups.exit().selectAll('path.nv-point')
.transition() .watchTransition(renderWatch, 'scatter exit')
.attr('transform', function(d,i) { .attr('transform', function(d,i) {
return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')' return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
}) })
@ -408,7 +410,8 @@ nv.models.scatter = function() {
.classed('hover',false) .classed('hover',false)
; ;
}); });
points.transition() points
.watchTransition(renderWatch, 'scatter points')
.attr('transform', function(d,i) { .attr('transform', function(d,i) {
//nv.log(d,i,getX(d,i), x(getX(d,i))); //nv.log(d,i,getX(d,i), x(getX(d,i)));
return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')' return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
@ -432,7 +435,7 @@ nv.models.scatter = function() {
z0 = z.copy(); z0 = z.copy();
}); });
renderWatch.renderEnd('scatter immediate');
return chart; return chart;
} }
@ -667,6 +670,13 @@ nv.models.scatter = function() {
return chart; return chart;
}; };
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
renderWatch.reset(duration);
return chart;
};
//============================================================ //============================================================

@ -36,9 +36,10 @@ nv.models.scatterChart = function() {
, tooltip = null , tooltip = null
, state = {} , state = {}
, defaultState = null , defaultState = null
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
, noData = "No Data Available." , noData = "No Data Available."
, transitionDuration = 250 , transitionDuration = 250
, duration = 250
; ;
scatter scatter
@ -70,6 +71,7 @@ nv.models.scatterChart = function() {
//------------------------------------------------------------ //------------------------------------------------------------
var x0, y0; var x0, y0;
var renderWatch = nv.utils.renderWatch(dispatch, duration);
var showTooltip = function(e, offsetElement) { var showTooltip = function(e, offsetElement) {
//TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?) //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?)
@ -99,6 +101,13 @@ nv.models.scatterChart = function() {
function chart(selection) { function chart(selection) {
renderWatch.reset();
renderWatch.models(scatter);
if (showXAxis) renderWatch.models(xAxis);
if (showYAxis) renderWatch.models(yAxis);
if (showDistX) renderWatch.models(distX);
if (showDistY) renderWatch.models(distY);
selection.each(function(data) { selection.each(function(data) {
var container = d3.select(this), var container = d3.select(this),
that = this; that = this;
@ -108,7 +117,12 @@ nv.models.scatterChart = function() {
availableHeight = (height || parseInt(container.style('height')) || 400) availableHeight = (height || parseInt(container.style('height')) || 400)
- margin.top - margin.bottom; - margin.top - margin.bottom;
chart.update = function() { container.transition().duration(transitionDuration).call(chart); }; chart.update = function() {
if (duration === 0)
container.call(chart);
else
container.transition().duration(duration).call(chart);
};
chart.container = this; chart.container = this;
//set state.disabled //set state.disabled
@ -239,7 +253,6 @@ nv.models.scatterChart = function() {
if (yPadding !== 0) if (yPadding !== 0)
scatter.yDomain(null); scatter.yDomain(null);
wrap.select('.nv-scatterWrap') wrap.select('.nv-scatterWrap')
.datum(data.filter(function(d) { return !d.disabled })) .datum(data.filter(function(d) { return !d.disabled }))
.call(scatter); .call(scatter);
@ -440,7 +453,7 @@ nv.models.scatterChart = function() {
}); });
renderWatch.renderEnd('scatterChart immediate');
return chart; return chart;
} }
@ -614,11 +627,20 @@ nv.models.scatterChart = function() {
noData = _; noData = _;
return chart; return chart;
}; };
chart.duration = function(_) {
chart.transitionDuration = function(_) { if (!arguments.length) return duration;
if (!arguments.length) return transitionDuration; duration = _;
transitionDuration = _; renderWatch.reset(duration);
scatter.duration(duration);
xAxis.duration(duration);
yAxis.duration(duration);
distX.duration(duration);
distY.duration(duration);
return chart; return chart;
}
chart.transitionDuration = function(_) {
nv.deprecated('scatterChart.transitionDuration');
return chart.duration(_);
}; };
//============================================================ //============================================================

@ -32,13 +32,13 @@ nv.models.scatterPlusLineChart = function() {
, tooltips = true , tooltips = true
, tooltipX = function(key, x, y) { return '<strong>' + x + '</strong>' } , tooltipX = function(key, x, y) { return '<strong>' + x + '</strong>' }
, tooltipY = function(key, x, y) { return '<strong>' + y + '</strong>' } , tooltipY = function(key, x, y) { return '<strong>' + y + '</strong>' }
, tooltip = function(key, x, y, date) { return '<h3>' + key + '</h3>' , tooltip = function(key, x, y, date) { return '<h3>' + key + '</h3>'
+ '<p>' + date + '</p>' } + '<p>' + date + '</p>' }
, state = {} , state = {}
, defaultState = null , defaultState = null
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState') , dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
, noData = "No Data Available." , noData = "No Data Available."
, transitionDuration = 250 , duration = 250
; ;
scatter scatter
@ -59,7 +59,7 @@ nv.models.scatterPlusLineChart = function() {
distY distY
.axis('y') .axis('y')
; ;
controls.updateState(false); controls.updateState(false);
//============================================================ //============================================================
@ -68,7 +68,9 @@ nv.models.scatterPlusLineChart = function() {
// Private Variables // Private Variables
//------------------------------------------------------------ //------------------------------------------------------------
var x0, y0; var x0, y0
, renderWatch = nv.utils.renderWatch(dispatch, duration)
;
var showTooltip = function(e, offsetElement) { var showTooltip = function(e, offsetElement) {
//TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?) //TODO: make tooltip style an option between single or dual on axes (maybe on all charts with axes?)
@ -98,6 +100,13 @@ nv.models.scatterPlusLineChart = function() {
function chart(selection) { function chart(selection) {
renderWatch.reset();
renderWatch.models(scatter);
if (showXAxis) renderWatch.models(xAxis);
if (showYAxis) renderWatch.models(yAxis);
if (showDistX) renderWatch.models(distX);
if (showDistY) renderWatch.models(distY);
selection.each(function(data) { selection.each(function(data) {
var container = d3.select(this), var container = d3.select(this),
that = this; that = this;
@ -107,7 +116,12 @@ nv.models.scatterPlusLineChart = function() {
availableHeight = (height || parseInt(container.style('height')) || 400) availableHeight = (height || parseInt(container.style('height')) || 400)
- margin.top - margin.bottom; - margin.top - margin.bottom;
chart.update = function() { container.transition().duration(transitionDuration).call(chart); }; chart.update = function() {
if (duration === 0)
container.call(chart);
else
container.transition().duration(duration).call(chart);
};
chart.container = this; chart.container = this;
//set state.disabled //set state.disabled
@ -139,7 +153,9 @@ nv.models.scatterPlusLineChart = function() {
.attr('x', margin.left + availableWidth / 2) .attr('x', margin.left + availableWidth / 2)
.attr('y', margin.top + availableHeight / 2) .attr('y', margin.top + availableHeight / 2)
.text(function(d) { return d }); .text(function(d) { return d });
renderWatch.renderEnd('scatter immediate');
return chart; return chart;
} else { } else {
container.selectAll('.nv-noData').remove(); container.selectAll('.nv-noData').remove();
@ -245,7 +261,7 @@ nv.models.scatterPlusLineChart = function() {
var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines') var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines')
.data(function(d) {return d }); .data(function(d) {return d });
regWrap.enter().append('g').attr('class', 'nv-regLines'); regWrap.enter().append('g').attr('class', 'nv-regLines');
var regLine = regWrap.selectAll('.nv-regLine').data(function(d){return [d]}); var regLine = regWrap.selectAll('.nv-regLine').data(function(d){return [d]});
@ -254,14 +270,14 @@ nv.models.scatterPlusLineChart = function() {
.style('stroke-opacity', 0); .style('stroke-opacity', 0);
regLine regLine
.transition() .watchTransition(renderWatch, 'scatterPlusLineChart: regline')
.attr('x1', x.range()[0]) .attr('x1', x.range()[0])
.attr('x2', x.range()[1]) .attr('x2', x.range()[1])
.attr('y1', function(d,i) {return y(x.domain()[0] * d.slope + d.intercept) }) .attr('y1', function(d,i) {return y(x.domain()[0] * d.slope + d.intercept) })
.attr('y2', function(d,i) { return y(x.domain()[1] * d.slope + d.intercept) }) .attr('y2', function(d,i) { return y(x.domain()[1] * d.slope + d.intercept) })
.style('stroke', function(d,i,j) { return color(d,j) }) .style('stroke', function(d,i,j) { return color(d,j) })
.style('stroke-opacity', function(d,i) { .style('stroke-opacity', function(d,i) {
return (d.disabled || typeof d.slope === 'undefined' || typeof d.intercept === 'undefined') ? 0 : 1 return (d.disabled || typeof d.slope === 'undefined' || typeof d.intercept === 'undefined') ? 0 : 1
}); });
//------------------------------------------------------------ //------------------------------------------------------------
@ -342,6 +358,8 @@ nv.models.scatterPlusLineChart = function() {
}); });
} }
// At this point, everything has been selected and bound... I think
function updateFisheye() { function updateFisheye() {
if (pauseFisheye) { if (pauseFisheye) {
@ -364,7 +382,7 @@ nv.models.scatterPlusLineChart = function() {
if (showYAxis) if (showYAxis)
g.select('.nv-y.nv-axis').call(yAxis); g.select('.nv-y.nv-axis').call(yAxis);
g.select('.nv-distributionX') g.select('.nv-distributionX')
.datum(data.filter(function(d) { return !d.disabled })) .datum(data.filter(function(d) { return !d.disabled }))
.call(distX); .call(distX);
@ -400,7 +418,7 @@ nv.models.scatterPlusLineChart = function() {
chart.update(); chart.update();
}); });
legend.dispatch.on('stateChange', function(newState) { legend.dispatch.on('stateChange', function(newState) {
state = newState; state = newState;
dispatch.stateChange(state); dispatch.stateChange(state);
chart.update(); chart.update();
@ -445,10 +463,10 @@ nv.models.scatterPlusLineChart = function() {
}); });
renderWatch.renderEnd('scatter with line immediate');
return chart; return chart;
} }
//============================================================ //============================================================
// Event Handling/Dispatching (out of chart's scope) // Event Handling/Dispatching (out of chart's scope)
//------------------------------------------------------------ //------------------------------------------------------------
@ -485,7 +503,7 @@ nv.models.scatterPlusLineChart = function() {
d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi'); d3.rebind(chart, scatter, 'id', 'interactive', 'pointActive', 'x', 'y', 'shape', 'size', 'xScale', 'yScale', 'zScale', 'xDomain', 'yDomain', 'xRange', 'yRange', 'sizeDomain', 'sizeRange', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'clipRadius', 'useVoronoi');
chart.options = nv.utils.optionsFunc.bind(chart); chart.options = nv.utils.optionsFunc.bind(chart);
chart.margin = function(_) { chart.margin = function(_) {
if (!arguments.length) return margin; if (!arguments.length) return margin;
margin.top = typeof _.top != 'undefined' ? _.top : margin.top; margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
@ -608,8 +626,13 @@ nv.models.scatterPlusLineChart = function() {
}; };
chart.transitionDuration = function(_) { chart.transitionDuration = function(_) {
if (!arguments.length) return transitionDuration; nv.deprecated('scatterPlusLineChart.transitionDuration')
transitionDuration = _; return chart.duration(_);
};
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
return chart; return chart;
}; };

@ -337,7 +337,11 @@ window.nv.tooltip.* also has various helper methods.
container.style.left = 0; container.style.left = 0;
container.style.top = 0; container.style.top = 0;
container.style.opacity = 0; container.style.opacity = 0;
container.innerHTML = content; // Content can also be dom element
if (typeof content !== 'string')
container.appendChild(content);
else
container.innerHTML = content;
body.appendChild(container); body.appendChild(container);
//If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets. //If the parent container is an overflow <div> with scrollbars, subtract the scroll offsets.

@ -130,6 +130,90 @@ nv.utils.NaNtoZero = function(n) {
return n; return n;
}; };
// This utility class watches for d3 transition ends.
(function(){
d3.selection.prototype.watchTransition = function(renderWatch){
var args = [this].concat([].slice.call(arguments, 1));
return renderWatch.transition.apply(renderWatch, args);
}
})();
nv.utils.renderWatch = function(dispatch, duration) {
if (!(this instanceof nv.utils.renderWatch))
return new nv.utils.renderWatch(dispatch, duration);
var _duration = duration !== undefined ? duration : 250;
var renderStack = [];
var self = this;
this.models = function(models) {
models = [].slice.call(arguments, 0);
models.forEach(function(model){
model.__rendered = false;
(function(m){
m.dispatch.on('renderEnd', function(arg){
// nv.log('nv.utils renderEnd', arg);
m.__rendered = true;
self.renderEnd('model');
});
})(model);
if (renderStack.indexOf(model) < 0)
renderStack.push(model);
});
return this;
}
this.reset = function(duration) {
if (duration !== undefined) _duration = duration;
renderStack = [];
}
this.transition = function(selection, args, duration) {
args = arguments.length > 1 ? [].slice.call(arguments, 1) : [];
duration = args.length > 1 ? args.pop() :
_duration !== undefined ? _duration :
250;
selection.__rendered = false;
if (renderStack.indexOf(selection) < 0)
renderStack.push(selection);
if (duration === 0)
{
selection.__rendered = true;
selection.delay = function(){console.warn('`delay` not specified for selection.'); return this;}
selection.duration = function(){console.warn('`duration` not specified for selection.'); return this;}
return selection;
}
else
{
selection.__rendered = selection.length === 0 ? true :
selection.every( function(d){ return !d.length; }) ? true :
false;
var n = 0;
return selection
.transition()
.duration(duration)
.each(function(){ ++n; })
.each('end', function(d, i){
if (--n === 0)
{
selection.__rendered = true;
self.renderEnd.apply(this, args);
}
});
}
}
this.renderEnd = function() {
if (renderStack.every( function(d){ return d.__rendered; } ))
{
renderStack.forEach( function(d){ d.__rendered = false; });
dispatch.renderEnd.apply(this, arguments);
}
}
}
/* /*
Snippet of code you can insert into each nv.models.* to give you the ability to Snippet of code you can insert into each nv.models.* to give you the ability to
do things like: do things like:
@ -142,6 +226,7 @@ To enable in the chart:
chart.options = nv.utils.optionsFunc.bind(chart); chart.options = nv.utils.optionsFunc.bind(chart);
*/ */
nv.utils.optionsFunc = function(args) { nv.utils.optionsFunc = function(args) {
nv.deprecated('nv.utils.optionsFunc');
if (args) { if (args) {
d3.map(args).forEach((function(key,value) { d3.map(args).forEach((function(key,value) {
if (typeof this[key] === "function") { if (typeof this[key] === "function") {
@ -150,4 +235,5 @@ nv.utils.optionsFunc = function(args) {
}).bind(this)); }).bind(this));
} }
return this; return this;
}; };

@ -128,6 +128,7 @@
<script src="../nv.d3.js"></script> <script src="../nv.d3.js"></script>
<script src="../src/tooltip.js"></script> <script src="../src/tooltip.js"></script>
<script src="../src/utils.js"></script> <script src="../src/utils.js"></script>
<script src="../src/canvas.js"></script>
<script src="../src/interactiveLayer.js"></script> <script src="../src/interactiveLayer.js"></script>
<script src="../src/models/legend.js"></script> <script src="../src/models/legend.js"></script>
<script src="../src/models/axis.js"></script> <script src="../src/models/axis.js"></script>

Loading…
Cancel
Save