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 10 years ago
frank shao 48e2684edf RenderWatch: Scatterpluslinechart 10 years ago
frank shao 0ef0e39384 RenderWatch: added to scatter plus line chart 10 years ago
David Souther 7364da25d5 Built. 10 years ago
David Souther 9800adeba6 Added watch on scatterPlusLineChart render end. 10 years ago
Robin Hu 8d8b727bb2 Renderwatch: Adding renderEnd dispatch to scatterPlusLineChart 10 years ago
frank shao 5da5323859 RenderWatch: added renderwatch api to selection.prototype 10 years ago
frank shao 504b5b0c9d RenderWatch: added render watch to cumulative line chart; various fixes 10 years ago
frank shao e177cae4d2 RenderWatch: added render watch to lineChart 10 years ago
frank shao 204a2e9e19 RenderWatch: Added render watch to scatterchart and submodels 10 years ago
frank shao a41d985c19 RenderWatch: Fixes 10 years ago
frank shao 5e1a7490b5 renderWatch: Modified addModels to accept list of models 10 years ago
frank shao 95fd1026b3 RenderWatch: fixes to axis.js 10 years ago
frank shao 7b490d2c85 Added util to watch for end of render; applied to multibarchart and its sub-models 10 years ago
frank shao 23fbe55024 Merge branch 'development' of https://github.com/novus/nvd3 into development 10 years ago
frank shao 7ad5580db3 Deprecated 'multibar.delay'. Use multibar.duration instead 10 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/tooltip.js',
'src/utils.js',
'src/canvas.js',
'src/models/axis.js',
'src/models/historicalBar.js',
'src/models/bullet.js',
@ -46,6 +49,7 @@ module.exports = function(grunt) {
'src/models/sparklinePlus.js',
'src/models/stackedArea.js',
'src/models/stackedAreaChart.js',
'src/outro.js'
],
dest: 'nv.d3.js'

@ -46,6 +46,7 @@ nv.models.scatterChart = function() {
//nv.tooltip.show([left, top], content, e.value < 0 ? 'n' : 's');
};
var renderWatch = nv.utils.renderWatch(dispatch);
function chart(selection) {
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
nv.addGraph(function() {
var chart = nv.models.cumulativeLineChart()
.useInteractiveGuideline(true)
.x(function(d) { return d[0] })
.y(function(d) { return d[1]/100 })
.color(d3.scale.category10().range())
.average(function(d) { return d.mean/100; })
.transitionDuration(300)
.clipVoronoi(false);
var chart = nv.models.cumulativeLineChart()
.useInteractiveGuideline(true)
.x(function(d) { return d[0] })
.y(function(d) { return d[1]/100 })
.color(d3.scale.category10().range())
.average(function(d) { return d.mean/100; })
.duration(300)
.clipVoronoi(false);
chart.dispatch.on('renderEnd', function() {
console.log('render complete: cumulative line with guide line');
});
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%m/%d/%y')(new Date(d))
});
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%m/%d/%y')(new Date(d))
});
chart.yAxis
.tickFormat(d3.format(',.1%'));
@ -97,8 +100,13 @@ nv.addGraph(function() {
.y(function(d) { return d[1]/100 })
.color(d3.scale.category10().range())
.average(function(d) { return d.mean/100; })
.duration(300)
.clipVoronoi(false);
chart.dispatch.on('renderEnd', function() {
console.log('render complete: cumulative line without guide line');
});
chart.xAxis
.tickFormat(function(d) {
return d3.time.format('%m/%d/%y')(new Date(d))

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

@ -62,19 +62,24 @@ nv.addGraph(function() {
x: function(d,i) { return i},
showXAxis: 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.xAxis
.axisLabel("Time (s)")
.tickFormat(d3.format(',.1f'));
.tickFormat(d3.format(',.1f'))
;
chart.yAxis
.axisLabel('Voltage (v)')
.tickFormat(d3.format(',.2f'))
;
chart.dispatch.on('renderEnd', function(){
console.log('render complete');
});
d3.select('#chart1 svg')
.datum(sinAndCos())

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

@ -69,24 +69,34 @@ nv.addGraph(function() {
chart = nv.models.multiBarChart()
.barColor(d3.scale.category20().range())
.margin({bottom: 100})
.transitionDuration(300)
.delay(0)
.duration(300)
.rotateLabels(45)
.groupSpacing(0.1)
;
chart.multibar
.hideable(true);
.hideable(true)
// .duration(4000)
;
chart.reduceXTicks(false).staggerLabels(true);
chart.xAxis
.axisLabel("Current Index")
.showMaxMin(true)
.tickFormat(d3.format(',.6f'));
.tickFormat(d3.format(',.6f'))
;
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')
.datum(negative_test_data)
@ -96,9 +106,18 @@ nv.addGraph(function() {
chart.dispatch.on('stateChange', function(e) { nv.log('New State:', JSON.stringify(e)); });
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)
//.interactive(false)
d3.select('#test1')
.attr('width', width)
.attr('height', height)

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

@ -69,6 +69,10 @@ nv.addGraph(function() {
.transitionDuration(300)
.color(d3.scale.category10().range());
chart.dispatch.on('renderEnd', function(){
console.log('render complete');
});
chart.xAxis.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];
};
nv.deprecated = function(name) {
if (nv.dev && console && console.warn)
console.warn('`' + name + '` has been deprecated.');
}
nv.render = function render(step) {
step = step || 1; // number of graphs to generate in each timeout loop

@ -20,8 +20,11 @@ nv.models.axis = function() {
, isOrdinal = false
, ticks = null
, 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
.scale(scale)
.orient('bottom')
@ -35,16 +38,17 @@ nv.models.axis = function() {
// Private Variables
//------------------------------------------------------------
var scale0;
var scale0
, renderWatch = nv.utils.renderWatch(dispatch, duration)
;
//============================================================
function chart(selection) {
renderWatch.reset();
selection.each(function(data) {
var container = d3.select(this);
//------------------------------------------------------------
// 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
g.transition().call(axis);
g.watchTransition(renderWatch, 'axis').call(axis);
scale0 = scale0 || axis.scale();
@ -102,7 +104,7 @@ nv.models.axis = function() {
var v = fmt(d);
return ('' + v).match('NaN') ? '' : v;
});
axisMaxMin.transition()
axisMaxMin.watchTransition(renderWatch, 'min-max top')
.attr('transform', function(d,i) {
return 'translate(' + scale.range()[i] + ',0)'
});
@ -152,10 +154,8 @@ nv.models.axis = function() {
var v = fmt(d);
return ('' + v).match('NaN') ? '' : v;
});
axisMaxMin.transition()
axisMaxMin.watchTransition(renderWatch, 'min-max bottom')
.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)'
});
}
@ -190,7 +190,7 @@ nv.models.axis = function() {
var v = fmt(d);
return ('' + v).match('NaN') ? '' : v;
});
axisMaxMin.transition()
axisMaxMin.watchTransition(renderWatch, 'min-max right')
.attr('transform', function(d,i) {
return 'translate(0,' + scale.range()[i] + ')'
})
@ -232,7 +232,7 @@ nv.models.axis = function() {
var v = fmt(d);
return ('' + v).match('NaN') ? '' : v;
});
axisMaxMin.transition()
axisMaxMin.watchTransition(renderWatch, 'min-max right')
.attr('transform', function(d,i) {
return 'translate(0,' + scale.range()[i] + ')'
})
@ -303,7 +303,8 @@ nv.models.axis = function() {
scale0 = scale.copy();
});
renderWatch.renderEnd('axis immediate');
return chart;
}
@ -314,6 +315,7 @@ nv.models.axis = function() {
// expose chart's sub-components
chart.axis = axis;
chart.dispatch = dispatch;
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
@ -398,6 +400,14 @@ nv.models.axis = function() {
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
, noData = 'No Data Available.'
, average = function(d) { return d.average }
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
, transitionDuration = 250
, duration = 250
, 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
//------------------------------------------------------------
var dx = d3.scale.linear()
, index = {i: 0, x: 0}
;
var dx = d3.scale.linear()
, index = {i: 0, x: 0}
, renderWatch = nv.utils.renderWatch(dispatch, duration)
;
var showTooltip = function(e, offsetElement) {
var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
@ -73,6 +76,10 @@ nv.models.cumulativeLineChart = function() {
//============================================================
function chart(selection) {
renderWatch.reset();
renderWatch.models(lines);
if (showXAxis) renderWatch.models(xAxis);
if (showYAxis) renderWatch.models(yAxis);
selection.each(function(data) {
var container = d3.select(this).classed('nv-chart-' + id, true),
that = this;
@ -83,7 +90,12 @@ nv.models.cumulativeLineChart = function() {
- 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;
//set state.disabled
@ -394,7 +406,7 @@ nv.models.cumulativeLineChart = function() {
g.select('.nv-x.nv-axis')
.attr('transform', 'translate(0,' + y.range()[0] + ')');
d3.transition(g.select('.nv-x.nv-axis'))
g.select('.nv-x.nv-axis')
.call(xAxis);
}
@ -405,7 +417,7 @@ nv.models.cumulativeLineChart = function() {
.ticks( availableHeight / 36 )
.tickSize( -availableWidth, 0);
d3.transition(g.select('.nv-y.nv-axis'))
g.select('.nv-y.nv-axis')
.call(yAxis);
}
//------------------------------------------------------------
@ -422,10 +434,10 @@ nv.models.cumulativeLineChart = function() {
//When dragging the index line, turn off line transitions.
// Then turn them back on when done dragging.
var oldDuration = chart.transitionDuration();
chart.transitionDuration(0);
var oldDuration = chart.duration();
chart.duration(0);
chart.update();
chart.transitionDuration(oldDuration);
chart.duration(oldDuration);
}
g.select('.nv-background rect')
@ -564,6 +576,8 @@ nv.models.cumulativeLineChart = function() {
});
renderWatch.renderEnd('cumulativeLineChart immediate');
return chart;
}
@ -716,8 +730,17 @@ nv.models.cumulativeLineChart = function() {
};
chart.transitionDuration = function(_) {
if (!arguments.length) return transitionDuration;
transitionDuration = _;
nv.deprecated('cumulativeLineChart.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;
};

@ -13,6 +13,8 @@ nv.models.distribution = function() {
, color = nv.utils.defaultColor()
, scale = d3.scale.linear()
, domain
, duration = 250
, dispatch = d3.dispatch('renderEnd')
;
//============================================================
@ -23,11 +25,13 @@ nv.models.distribution = function() {
//------------------------------------------------------------
var scale0;
var renderWatch = nv.utils.renderWatch(dispatch, duration);
//============================================================
function chart(selection) {
renderWatch.reset();
selection.each(function(data) {
var availableLength = width - (axis === 'x' ? margin.left + margin.right : margin.top + margin.bottom),
naxis = axis == 'x' ? 'y' : 'x',
@ -68,8 +72,8 @@ nv.models.distribution = function() {
dist.enter().append('line')
.attr(axis + '1', 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)
.transition()
renderWatch.transition(distWrap.exit().selectAll('line.nv-dist' + axis), 'dist exit')
// .transition()
.attr(axis + '1', function(d,i) { return scale(getData(d,i)) })
.attr(axis + '2', function(d,i) { return scale(getData(d,i)) })
.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(naxis + '1', 0)
.attr(naxis + '2', size);
dist
.transition()
renderWatch.transition(dist, 'dist')
// .transition()
.attr(axis + '1', 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();
});
renderWatch.renderEnd('distribution immediate');
return chart;
}
@ -96,6 +100,7 @@ nv.models.distribution = function() {
// Expose Public Variables
//------------------------------------------------------------
chart.options = nv.utils.optionsFunc.bind(chart);
chart.dispatch = dispatch;
chart.margin = function(_) {
if (!arguments.length) return margin;
@ -141,6 +146,13 @@ nv.models.distribution = function() {
color = nv.utils.getColor(_);
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()
, y //can be accessed via chart.yScale()
, interpolate = "linear" // controls the line interpolation
, duration = 250
, dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
;
scatter
@ -35,12 +37,15 @@ nv.models.line = function() {
//------------------------------------------------------------
var x0, y0 //used to store previous scales
;
, renderWatch = nv.utils.renderWatch(dispatch, duration)
;
//============================================================
function chart(selection) {
renderWatch.reset();
renderWatch.models(scatter);
selection.each(function(data) {
var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom,
@ -84,7 +89,7 @@ nv.models.line = function() {
var scatterWrap = wrap.select('.nv-scatterWrap');
//.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')
.data(function(d) { return d }, function(d) { return d.key });
groups.enter().append('g')
@ -116,8 +120,7 @@ nv.models.line = function() {
.classed('hover', function(d) { return d.hover })
.style('fill', function(d,i){ return color(d, i) })
.style('stroke', function(d,i){ return color(d, i)});
groups
.transition()
groups.watchTransition(renderWatch, 'line: groups')
.style('stroke-opacity', 1)
.style('fill-opacity', .5);
@ -140,8 +143,7 @@ nv.models.line = function() {
groups.exit().selectAll('path.nv-area')
.remove();
areaPaths
.transition()
areaPaths.watchTransition(renderWatch, 'line: areaPaths')
.attr('d', function(d) {
return d3.svg.area()
.interpolate(interpolate)
@ -167,8 +169,7 @@ nv.models.line = function() {
.y(function(d,i) { return nv.utils.NaNtoZero(y0(getY(d,i))) })
);
linePaths
.transition()
linePaths.watchTransition(renderWatch, 'line: linePaths')
.attr('d',
d3.svg.line()
.interpolate(interpolate)
@ -184,7 +185,7 @@ nv.models.line = function() {
y0 = y.copy();
});
renderWatch.renderEnd('line immediate');
return chart;
}
@ -193,8 +194,12 @@ nv.models.line = function() {
// Expose Public Variables
//------------------------------------------------------------
chart.dispatch = scatter.dispatch;
chart.dispatch = dispatch;
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',
'sizeDomain', 'forceX', 'forceY', 'forceSize', 'clipVoronoi', 'useVoronoi', 'clipRadius', 'padData','highlightPoint','clearHighlights');
@ -267,6 +272,14 @@ nv.models.line = function() {
return chart;
};
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
renderWatch.reset(duration);
scatter.duration(duration);
return chart;
};
//============================================================

@ -1,467 +1,252 @@
nv.models.lineChart = function() {
"use strict";
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var lines = nv.models.line()
, xAxis = nv.models.axis()
, yAxis = nv.models.axis()
, legend = nv.models.legend()
, interactiveLayer = nv.interactiveGuideline()
;
var margin = {top: 30, right: 20, bottom: 50, left: 60}
, color = nv.utils.defaultColor()
, width = null
, height = null
, showLegend = true
, showXAxis = true
, showYAxis = true
, rightAlignYAxis = false
, useInteractiveGuideline = false
, tooltips = true
, tooltip = function(key, x, y, e, graph) {
return '<h3>' + key + '</h3>' +
'<p>' + y + ' at ' + x + '</p>'
}
, x
, y
, state = {}
, defaultState = null
, noData = 'No Data Available.'
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
, transitionDuration = 250
;
xAxis
.orient('bottom')
.tickPadding(7)
;
yAxis
.orient((rightAlignYAxis) ? 'right' : 'left')
;
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function(e, offsetElement) {
var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
top = e.pos[1] + ( offsetElement.offsetTop || 0),
x = xAxis.tickFormat()(lines.x()(e.point, e.pointIndex)),
y = yAxis.tickFormat()(lines.y()(e.point, e.pointIndex)),
content = tooltip(e.series.key, x, y, e, chart);
nv.tooltip.show([left, top], content, null, null, offsetElement);
};
//============================================================
function chart(selection) {
selection.each(function(data) {
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 });
/* global nv, d3, Chart */
nv.models.lineChart = function () {
'use strict';
var defaults = {
margin: {
top: 30,
right: 20,
bottom: 50,
left: 60
},
dispatch: [
'tooltipShow',
'tooltipHide',
'stateChange',
'changeState',
'renderEnd'
]
};
//============================================================
// Public Variables with Default Settings
//------------------------------------------------------------
var lines = nv.models.line(),
interactiveLayer = nv.interactiveGuideline(),
inst = Chart.call(null, defaults);
//============================================================
//============================================================
// Private Variables
//------------------------------------------------------------
var showTooltip = function(canvas){
return function (e, offsetElement) {
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)),
y = canvas.axis.y.tickFormat()(canvas.scale.y()(e.point, e.pointIndex)),
content = canvas.tooltip(e.series.key, x, y, e, chart);
nv.tooltip.show([left, top], content, null, null, offsetElement);
};
};
// var renderWatch = nv.utils.renderWatch(canvas.dispatch, otions.duration);
//============================================================
function chart(selection) {
// renderWatch.reset();
// renderWatch.models(lines);
// if (showXAxis) renderWatch.models(xAxis);
// if (showYAxis) renderWatch.models(yAxis);
selection.each(function (data) {
Chart.canvas(inst, this);
var canvas = inst;
var container = canvas.svg,
that = this;
canvas.update = chart.update = function () {
if (canvas.duration === 0)
canvas.svg.call(chart);
else
canvas.svg.transition().duration(canvas.duration).call(chart)
};
nv.models.lineChart.build(canvas, data, lines);
});
// renderWatch.renderEnd('lineChart immediate');
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) {
xAxis
.scale(x)
.ticks( availableWidth / 100 )
.tickSize(-availableHeight, 0);
nv.models.lineChart.build = function(chart, data, lines){
Chart.defaultState.set(chart, data);
g.select('.nv-x.nv-axis')
.attr('transform', 'translate(0,' + y.range()[0] + ')');
g.select('.nv-x.nv-axis')
.transition()
.call(xAxis);
}
if(Chart.checkData(chart, data)){
return chart;
}
if (showYAxis) {
yAxis
.scale(y)
.ticks( availableHeight / 36 )
.tickSize( -availableWidth, 0);
nv.models.lineChart.wrap(chart, data);
Chart.legend.build(chart, data);
g.select('.nv-y.nv-axis')
.transition()
.call(yAxis);
}
//------------------------------------------------------------
//------------------------------------------------------------
chart.wrap.attr('transform', 'translate(' + chart.margin.left + ',' + chart.margin.top + ')');
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);
}
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
nv.models.lineChart.lines(chart, data, lines);
legend.dispatch.on('stateChange', function(newState) {
state = newState;
dispatch.stateChange(state);
chart.update();
});
//------------------------------------------------------------
//------------------------------------------------------------
// Setup Axes
Chart.axis.build(chart, data);
interactiveLayer.dispatch.on('elementMousemove', function(e) {
lines.clearHighlights();
var singlePoint, pointIndex, pointXLocation, allData = [];
data
.filter(function(series, i) {
series.seriesIndex = i;
return !series.disabled;
})
.forEach(function(series,i) {
pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.x());
lines.highlightPoint(i, pointIndex, true);
var point = series.values[pointIndex];
if (typeof point === 'undefined') return;
if (typeof singlePoint === 'undefined') singlePoint = point;
if (typeof pointXLocation === 'undefined') pointXLocation = chart.xScale()(chart.x()(point,pointIndex));
allData.push({
key: series.key,
value: chart.y()(point, pointIndex),
color: color(series,series.seriesIndex)
});
});
//Highlight the tooltip entry based on which point the mouse is closest to.
if (allData.length > 2) {
//------------------------------------------------------------
//============================================================
// Event Handling/Dispatching (in chart's scope)
//------------------------------------------------------------
chart.legend.dispatch.on('stateChange', function (newState) {
chart.state = newState;
chart.dispatch.stateChange(chart.state);
chart.update();
});
chart.interactiveLayer.dispatch.on('elementMousemove', function (e) {
lines.clearHighlights();
var singlePoint, pointIndex, pointXLocation, allData = [];
data
.filter(function (series, i) {
series.seriesIndex = i;
return !series.disabled;
})
.forEach(function (series, i) {
pointIndex = nv.interactiveBisect(series.values, e.pointXValue, chart.scale.x());
lines.highlightPoint(i, pointIndex, true);
var point = series.values[pointIndex];
if (typeof point === 'undefined') return;
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 domainExtent = Math.abs(chart.yScale().domain()[0] - chart.yScale().domain()[1]);
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)
allData[indexToHighlight].highlight = true;
}
var xValue = xAxis.tickFormat()(chart.x()(singlePoint,pointIndex));
interactiveLayer.tooltip
.position({left: pointXLocation + margin.left, top: e.mouseY + margin.top})
.chartContainer(that.parentNode)
.enabled(tooltips)
.valueFormatter(function(d,i) {
return yAxis.tickFormat()(d);
})
.data(
{
value: xValue,
series: allData
}
)();
interactiveLayer.renderGuideLine(pointXLocation);
});
interactiveLayer.dispatch.on("elementMouseout",function(e) {
dispatch.tooltipHide();
lines.clearHighlights();
});
dispatch.on('tooltipShow', function(e) {
if (tooltips) showTooltip(e, that.parentNode);
});
dispatch.on('changeState', function(e) {
allData[indexToHighlight].highlight = true;
}
var xValue = chart.axis.x.tickFormat()(chart.scale.x()(singlePoint, pointIndex));
chart.interactiveLayer.tooltip
.position({
left: pointXLocation + chart.margin.left,
top: e.mouseY + chart.margin.top
})
.chartContainer(that.parentNode)
.enabled(chart.tooltips)
.valueFormatter(function (d, i) {
return chart.axis.y.tickFormat()(d);
})
.data({
value: xValue,
series: allData
})();
chart.interactiveLayer.renderGuideLine(pointXLocation);
});
chart.interactiveLayer.dispatch.on('elementMouseout', function (e) {
chart.dispatch.tooltipHide();
lines.clearHighlights();
});
chart.dispatch.on('tooltipShow', function (e) {
if (chart.tooltips) showTooltip(e, that.parentNode);
});
chart.dispatch.on('changeState', function (e) {
if (typeof e.disabled !== 'undefined' && data.length === e.disabled.length) {
data.forEach(function(series,i) {
series.disabled = e.disabled[i];
});
state.disabled = e.disabled;
data.forEach(function (series, i) {
series.disabled = e.disabled[i];
});
chart.state.disabled = e.disabled;
}
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
, 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
, delay = 1200
, duration = 1000
, xDomain
, yDomain
, xRange
, yRange
, 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
, renderWatch = nv.utils.renderWatch(dispatch, duration)
;
//============================================================
function chart(selection) {
renderWatch.reset();
selection.each(function(data) {
var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom,
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 = [{
values: data[0].values.map(function(d) {
return {
@ -156,31 +169,29 @@ nv.models.multiBar = function() {
g .attr('clip-path', clipEdge ? 'url(#nv-edge-clip-' + id + ')' : '');
var groups = wrap.select('.nv-groups').selectAll('.nv-group')
.data(function(d) { return d }, function(d,i) { return i });
groups.enter().append('g')
.style('stroke-opacity', 1e-6)
.style('fill-opacity', 1e-6);
groups.exit()
.transition()
.selectAll('rect.nv-bar')
.delay(function(d,i) {
return i * delay/ data[0].values.length;
})
var exitTransition = renderWatch
.transition(groups.exit().selectAll('rect.nv-bar'), 'multibarExit', Math.min(250, duration))
.attr('y', function(d) { return stacked ? y0(d.y0) : y0(0) })
.attr('height', 0)
.remove();
if (exitTransition.delay)
exitTransition.delay(function(d,i) {
return i * duration / data[0].values.length;
});
groups
.attr('class', function(d,i) { return 'nv-group nv-series-' + i })
.classed('hover', function(d) { return d.hover })
.style('fill', function(d,i){ return color(d, i) })
.style('stroke', function(d,i){ return color(d, i) });
groups
.transition()
.style('stroke-opacity', 1)
.style('fill-opacity', .75);
.style('fill-opacity', 0.75);
var bars = groups.selectAll('rect.nv-bar')
@ -251,7 +262,6 @@ nv.models.multiBar = function() {
});
bars
.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)'; })
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(); });
}
var barSelection =
bars.watchTransition(renderWatch, 'multibar', Math.min(250, duration))
.delay(function(d,i) {
return i * duration / data[0].values.length;
});
if (stacked)
bars.transition()
.delay(function(d,i) {
return i * delay / data[0].values.length;
})
barSelection
.attr('y', function(d,i) {
return y((stacked ? d.y1 : 0));
})
.attr('height', function(d,i) {
return Math.max(Math.abs(y(d.y + (stacked ? d.y0 : 0)) - y((stacked ? d.y0 : 0))),1);
})
.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) );
else
bars.transition()
.delay(function(d,i) {
return i * delay/ data[0].values.length;
})
barSelection
.attr('x', function(d,i) {
return d.series * x.rangeBand() / data.length
})
.attr('width', x.rangeBand() / data.length)
.attr('y', function(d,i) {
return getY(d,i) < 0 ?
y(0) :
y(0) - y(getY(d,i)) < 1 ?
y(0) - 1 :
y(getY(d,i)) || 0;
return getY(d,i) < 0 ?
y(0) :
y(0) - y(getY(d,i)) < 1 ?
y(0) - 1 :
y(getY(d,i)) || 0;
})
.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
x0 = x.copy();
y0 = y.copy();
});
renderWatch.renderEnd('multibar immediate');
return chart;
}
//============================================================
// 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(_) {
if (!arguments.length) return getX;
@ -442,20 +450,33 @@ nv.models.multiBar = function() {
return chart;
};
chart.delay = function(_) {
if (!arguments.length) return delay;
delay = _;
return chart;
};
chart.groupSpacing = function(_) {
if (!arguments.length) return groupSpacing;
groupSpacing = _;
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;
}

@ -34,9 +34,10 @@ nv.models.multiBarChart = function() {
, state = { stacked: false }
, defaultState = null
, 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 }
, transitionDuration = 250
, duration = 250
;
multibar
@ -61,7 +62,8 @@ nv.models.multiBarChart = function() {
//============================================================
// Private Variables
//------------------------------------------------------------
var renderWatch = nv.utils.renderWatch(dispatch);
var showTooltip = function(e, offsetElement) {
var left = e.pos[0] + ( offsetElement.offsetLeft || 0 ),
top = e.pos[1] + ( offsetElement.offsetTop || 0),
@ -76,6 +78,11 @@ nv.models.multiBarChart = function() {
function chart(selection) {
renderWatch.reset();
renderWatch.models(multibar);
if (showXAxis) renderWatch.models(xAxis);
if (showYAxis) renderWatch.models(yAxis);
selection.each(function(data) {
var container = d3.select(this),
that = this;
@ -85,7 +92,14 @@ nv.models.multiBarChart = function() {
availableHeight = (height || parseInt(container.style('height')) || 400)
- 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;
//set state.disabled
@ -219,7 +233,7 @@ nv.models.multiBarChart = function() {
var barsWrap = g.select('.nv-barsWrap')
.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')
.attr('transform', 'translate(0,' + y.range()[0] + ')');
g.select('.nv-x.nv-axis').transition()
g.select('.nv-x.nv-axis')
.call(xAxis);
var xTicks = g.select('.nv-x.nv-axis > g').selectAll('g');
@ -283,13 +297,13 @@ nv.models.multiBarChart = function() {
}
if (showYAxis) {
if (showYAxis) {
yAxis
.scale(y)
.ticks( availableHeight / 36 )
.tickSize( -availableWidth, 0);
g.select('.nv-y.nv-axis').transition()
g.select('.nv-y.nv-axis')
.call(yAxis);
}
@ -359,6 +373,8 @@ nv.models.multiBarChart = function() {
});
renderWatch.renderEnd('multibarchart immediate');
return chart;
}
@ -379,6 +395,7 @@ nv.models.multiBarChart = function() {
if (tooltips) nv.tooltip.cleanup();
});
//============================================================
@ -414,12 +431,14 @@ nv.models.multiBarChart = function() {
};
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);
@ -512,11 +531,20 @@ nv.models.multiBarChart = function() {
};
chart.transitionDuration = function(_) {
if (!arguments.length) return transitionDuration;
transitionDuration = _;
return chart;
nv.deprecated('multiBarChart.transitionDuration');
return chart.duration(_);
};
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
, sizeRange = null
, singlePoint = false
, dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout')
, dispatch = d3.dispatch('elementClick', 'elementMouseover', 'elementMouseout', 'renderEnd')
, useVoronoi = true
, duration = 250
;
//============================================================
@ -50,12 +51,14 @@ nv.models.scatter = function() {
var x0, y0, z0 // used to store previous scales
, timeoutID
, 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) {
renderWatch.reset();
selection.each(function(data) {
var availableWidth = width - margin.left - margin.right,
availableHeight = height - margin.top - margin.bottom,
@ -333,7 +336,6 @@ nv.models.scatter = function() {
}
needsUpdate = true;
var groups = wrap.select('.nv-groups').selectAll('.nv-group')
.data(function(d) { return d }, function(d) { return d.key });
groups.enter().append('g')
@ -344,8 +346,7 @@ nv.models.scatter = function() {
groups
.attr('class', function(d,i) { return 'nv-group nv-series-' + i })
.classed('hover', function(d) { return d.hover });
groups
.transition()
groups.watchTransition(renderWatch, 'scatter: groups')
.style('fill', function(d,i) { return color(d, i) })
.style('stroke', function(d,i) { return color(d, i) })
.style('stroke-opacity', 1)
@ -353,7 +354,6 @@ nv.models.scatter = function() {
if (onlyCircles) {
var points = groups.selectAll('circle.nv-point')
.data(function(d) { return d.values }, pointKey);
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('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
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('cy', function(d,i) { return nv.utils.NaNtoZero(y(getY(d,i))) })
.remove();
@ -374,7 +375,8 @@ nv.models.scatter = function() {
.classed('hover',false)
;
});
points.transition()
points
.watchTransition(renderWatch, 'scatter points')
.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('r', function(d,i) { return Math.sqrt(z(getSize(d,i))/Math.PI) });
@ -396,7 +398,7 @@ nv.models.scatter = function() {
);
points.exit().remove();
groups.exit().selectAll('path.nv-point')
.transition()
.watchTransition(renderWatch, 'scatter exit')
.attr('transform', function(d,i) {
return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
})
@ -408,7 +410,8 @@ nv.models.scatter = function() {
.classed('hover',false)
;
});
points.transition()
points
.watchTransition(renderWatch, 'scatter points')
.attr('transform', function(d,i) {
//nv.log(d,i,getX(d,i), x(getX(d,i)));
return 'translate(' + x(getX(d,i)) + ',' + y(getY(d,i)) + ')'
@ -432,7 +435,7 @@ nv.models.scatter = function() {
z0 = z.copy();
});
renderWatch.renderEnd('scatter immediate');
return chart;
}
@ -667,6 +670,13 @@ nv.models.scatter = function() {
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
, state = {}
, defaultState = null
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
, noData = "No Data Available."
, transitionDuration = 250
, duration = 250
;
scatter
@ -70,6 +71,7 @@ nv.models.scatterChart = function() {
//------------------------------------------------------------
var x0, y0;
var renderWatch = nv.utils.renderWatch(dispatch, duration);
var showTooltip = function(e, offsetElement) {
//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) {
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) {
var container = d3.select(this),
that = this;
@ -108,7 +117,12 @@ nv.models.scatterChart = function() {
availableHeight = (height || parseInt(container.style('height')) || 400)
- 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;
//set state.disabled
@ -239,7 +253,6 @@ nv.models.scatterChart = function() {
if (yPadding !== 0)
scatter.yDomain(null);
wrap.select('.nv-scatterWrap')
.datum(data.filter(function(d) { return !d.disabled }))
.call(scatter);
@ -440,7 +453,7 @@ nv.models.scatterChart = function() {
});
renderWatch.renderEnd('scatterChart immediate');
return chart;
}
@ -614,11 +627,20 @@ nv.models.scatterChart = function() {
noData = _;
return chart;
};
chart.transitionDuration = function(_) {
if (!arguments.length) return transitionDuration;
transitionDuration = _;
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
renderWatch.reset(duration);
scatter.duration(duration);
xAxis.duration(duration);
yAxis.duration(duration);
distX.duration(duration);
distY.duration(duration);
return chart;
}
chart.transitionDuration = function(_) {
nv.deprecated('scatterChart.transitionDuration');
return chart.duration(_);
};
//============================================================

@ -32,13 +32,13 @@ nv.models.scatterPlusLineChart = function() {
, tooltips = true
, tooltipX = function(key, x, y) { return '<strong>' + x + '</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>' }
, state = {}
, defaultState = null
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState')
, dispatch = d3.dispatch('tooltipShow', 'tooltipHide', 'stateChange', 'changeState', 'renderEnd')
, noData = "No Data Available."
, transitionDuration = 250
, duration = 250
;
scatter
@ -59,7 +59,7 @@ nv.models.scatterPlusLineChart = function() {
distY
.axis('y')
;
controls.updateState(false);
//============================================================
@ -68,7 +68,9 @@ nv.models.scatterPlusLineChart = function() {
// Private Variables
//------------------------------------------------------------
var x0, y0;
var x0, y0
, renderWatch = nv.utils.renderWatch(dispatch, duration)
;
var showTooltip = function(e, offsetElement) {
//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) {
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) {
var container = d3.select(this),
that = this;
@ -107,7 +116,12 @@ nv.models.scatterPlusLineChart = function() {
availableHeight = (height || parseInt(container.style('height')) || 400)
- 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;
//set state.disabled
@ -139,7 +153,9 @@ nv.models.scatterPlusLineChart = function() {
.attr('x', margin.left + availableWidth / 2)
.attr('y', margin.top + availableHeight / 2)
.text(function(d) { return d });
renderWatch.renderEnd('scatter immediate');
return chart;
} else {
container.selectAll('.nv-noData').remove();
@ -245,7 +261,7 @@ nv.models.scatterPlusLineChart = function() {
var regWrap = wrap.select('.nv-regressionLinesWrap').selectAll('.nv-regLines')
.data(function(d) {return d });
regWrap.enter().append('g').attr('class', 'nv-regLines');
var regLine = regWrap.selectAll('.nv-regLine').data(function(d){return [d]});
@ -254,14 +270,14 @@ nv.models.scatterPlusLineChart = function() {
.style('stroke-opacity', 0);
regLine
.transition()
.watchTransition(renderWatch, 'scatterPlusLineChart: regline')
.attr('x1', x.range()[0])
.attr('x2', x.range()[1])
.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) })
.style('stroke', function(d,i,j) { return color(d,j) })
.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() {
if (pauseFisheye) {
@ -364,7 +382,7 @@ nv.models.scatterPlusLineChart = function() {
if (showYAxis)
g.select('.nv-y.nv-axis').call(yAxis);
g.select('.nv-distributionX')
.datum(data.filter(function(d) { return !d.disabled }))
.call(distX);
@ -400,7 +418,7 @@ nv.models.scatterPlusLineChart = function() {
chart.update();
});
legend.dispatch.on('stateChange', function(newState) {
legend.dispatch.on('stateChange', function(newState) {
state = newState;
dispatch.stateChange(state);
chart.update();
@ -445,10 +463,10 @@ nv.models.scatterPlusLineChart = function() {
});
renderWatch.renderEnd('scatter with line immediate');
return chart;
}
//============================================================
// 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');
chart.options = nv.utils.optionsFunc.bind(chart);
chart.margin = function(_) {
if (!arguments.length) return margin;
margin.top = typeof _.top != 'undefined' ? _.top : margin.top;
@ -608,8 +626,13 @@ nv.models.scatterPlusLineChart = function() {
};
chart.transitionDuration = function(_) {
if (!arguments.length) return transitionDuration;
transitionDuration = _;
nv.deprecated('scatterPlusLineChart.transitionDuration')
return chart.duration(_);
};
chart.duration = function(_) {
if (!arguments.length) return duration;
duration = _;
return chart;
};

@ -337,7 +337,11 @@ window.nv.tooltip.* also has various helper methods.
container.style.left = 0;
container.style.top = 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);
//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;
};
// 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
do things like:
@ -142,6 +226,7 @@ To enable in the chart:
chart.options = nv.utils.optionsFunc.bind(chart);
*/
nv.utils.optionsFunc = function(args) {
nv.deprecated('nv.utils.optionsFunc');
if (args) {
d3.map(args).forEach((function(key,value) {
if (typeof this[key] === "function") {
@ -150,4 +235,5 @@ nv.utils.optionsFunc = function(args) {
}).bind(this));
}
return this;
};
};

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

Loading…
Cancel
Save