Skip to content

Commit 06ca2c1

Browse files
authored
Merge pull request #3860 from plotly/animate-multiple-basePlotModules
Fix Plotly.animate on graphs with multiple basePlotModules
2 parents d617a1b + d5064ff commit 06ca2c1

File tree

4 files changed

+74
-20
lines changed

4 files changed

+74
-20
lines changed

Diff for: src/plots/cartesian/index.js

+2-7
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,9 @@ exports.finalizeSubplots = function(layoutIn, layoutOut) {
125125
* Cartesian.plot
126126
*
127127
* @param {DOM div | object} gd
128-
* @param {array | null} (optional) traces
128+
* @param {array (optional)} traces
129129
* array of traces indices to plot
130130
* if undefined, plots all cartesian traces,
131-
* if null, plots no traces
132131
* @param {object} (optional) transitionOpts
133132
* transition option object
134133
* @param {function} (optional) makeOnCompleteCallback
@@ -140,11 +139,7 @@ exports.plot = function(gd, traces, transitionOpts, makeOnCompleteCallback) {
140139
var calcdata = gd.calcdata;
141140
var i;
142141

143-
if(traces === null) {
144-
// this means no updates required, must return here
145-
// so that plotOne doesn't remove the trace layers
146-
return;
147-
} else if(!Array.isArray(traces)) {
142+
if(!Array.isArray(traces)) {
148143
// If traces is not provided, then it's a complete replot and missing
149144
// traces are removed
150145
traces = [];

Diff for: src/plots/plots.js

+17-13
Original file line numberDiff line numberDiff line change
@@ -2309,7 +2309,7 @@ plots.extendLayout = function(destLayout, srcLayout) {
23092309
*/
23102310
plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts) {
23112311
var opts = {redraw: frameOpts.redraw};
2312-
var transitionedTraces = [];
2312+
var transitionedTraces = {};
23132313
var axEdits = [];
23142314

23152315
opts.prepareFn = function() {
@@ -2319,16 +2319,18 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts)
23192319
for(var i = 0; i < traceIndices.length; i++) {
23202320
var traceIdx = traceIndices[i];
23212321
var trace = gd._fullData[traceIdx];
2322-
var module = trace._module;
2322+
var _module = trace._module;
23232323

23242324
// There's nothing to do if this module is not defined:
2325-
if(!module) continue;
2325+
if(!_module) continue;
23262326

23272327
// Don't register the trace as transitioned if it doesn't know what to do.
23282328
// If it *is* registered, it will receive a callback that it's responsible
23292329
// for calling in order to register the transition as having completed.
2330-
if(module.animatable) {
2331-
transitionedTraces.push(traceIdx);
2330+
if(_module.animatable) {
2331+
var n = _module.basePlotModule.name;
2332+
if(!transitionedTraces[n]) transitionedTraces[n] = [];
2333+
transitionedTraces[n].push(traceIdx);
23322334
}
23332335

23342336
gd.data[traceIndices[i]] = plots.extendTrace(gd.data[traceIndices[i]], data[i]);
@@ -2427,19 +2429,21 @@ plots.transition = function(gd, data, layout, traces, frameOpts, transitionOpts)
24272429
if(hasAxisTransition) {
24282430
traceTransitionOpts = Lib.extendFlat({}, transitionOpts);
24292431
traceTransitionOpts.duration = 0;
2430-
// This means do not transition traces,
2432+
// This means do not transition cartesian traces,
24312433
// this happens on layout-only (e.g. axis range) animations
2432-
transitionedTraces = null;
2434+
delete transitionedTraces.cartesian;
24332435
} else {
24342436
traceTransitionOpts = transitionOpts;
24352437
}
24362438

2437-
for(i = 0; i < basePlotModules.length; i++) {
2438-
// Note that we pass a callback to *create* the callback that must be invoked on completion.
2439-
// This is since not all traces know about transitions, so it greatly simplifies matters if
2440-
// the trace is responsible for creating a callback, if needed, and then executing it when
2441-
// the time is right.
2442-
basePlotModules[i].plot(gd, transitionedTraces, traceTransitionOpts, makeCallback);
2439+
// Note that we pass a callback to *create* the callback that must be invoked on completion.
2440+
// This is since not all traces know about transitions, so it greatly simplifies matters if
2441+
// the trace is responsible for creating a callback, if needed, and then executing it when
2442+
// the time is right.
2443+
for(var n in transitionedTraces) {
2444+
var traceIndices = transitionedTraces[n];
2445+
var _module = gd._fullData[traceIndices[0]]._module;
2446+
_module.basePlotModule.plot(gd, traceIndices, traceTransitionOpts, makeCallback);
24432447
}
24442448
};
24452449

Diff for: src/traces/sunburst/attributes.js

+1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ module.exports = {
6161
level: {
6262
valType: 'any',
6363
editType: 'plot',
64+
anim: true,
6465
role: 'info',
6566
description: [
6667
'Sets the level from which this sunburst trace hierarchy is rendered.',

Diff for: test/jasmine/tests/sunburst_test.js

+54
Original file line numberDiff line numberDiff line change
@@ -1160,4 +1160,58 @@ describe('Test sunburst interactions edge cases', function() {
11601160
})
11611161
.then(done);
11621162
});
1163+
1164+
it('should transition sunburst traces only', function(done) {
1165+
var mock = Lib.extendDeep({}, require('@mocks/display-text_zero-number.json'));
1166+
mock.data[0].visible = false;
1167+
1168+
function _assert(msg, exp) {
1169+
var gd3 = d3.select(gd);
1170+
expect(gd3.select('.cartesianlayer').selectAll('.trace').size())
1171+
.toBe(exp.cartesianTraceCnt, '# of cartesian traces');
1172+
expect(gd3.select('.pielayer').selectAll('.trace').size())
1173+
.toBe(exp.pieTraceCnt, '# of pie traces');
1174+
expect(gd3.select('.sunburstlayer').selectAll('.trace').size())
1175+
.toBe(exp.sunburstTraceCnt, '# of sunburst traces');
1176+
}
1177+
1178+
Plotly.plot(gd, mock)
1179+
.then(function() {
1180+
_assert('base', {
1181+
cartesianTraceCnt: 2,
1182+
pieTraceCnt: 0,
1183+
sunburstTraceCnt: 1
1184+
});
1185+
})
1186+
.then(click(gd, 2))
1187+
.then(delay(constants.CLICK_TRANSITION_TIME + 1))
1188+
.then(function() {
1189+
_assert('after sunburst click', {
1190+
cartesianTraceCnt: 2,
1191+
pieTraceCnt: 0,
1192+
sunburstTraceCnt: 1
1193+
});
1194+
})
1195+
.catch(failTest)
1196+
.then(done);
1197+
});
1198+
1199+
it('should be able to transition sunburst traces via `Plotly.react`', function(done) {
1200+
var mock = Lib.extendDeep({}, require('@mocks/display-text_zero-number.json'));
1201+
mock.layout.transition = {duration: 200};
1202+
1203+
spyOn(Plots, 'transitionFromReact').and.callThrough();
1204+
1205+
Plotly.plot(gd, mock)
1206+
.then(function() {
1207+
gd.data[1].level = 'B';
1208+
return Plotly.react(gd, gd.data, gd.layout);
1209+
})
1210+
.then(delay(202))
1211+
.then(function() {
1212+
expect(Plots.transitionFromReact).toHaveBeenCalledTimes(1);
1213+
})
1214+
.catch(failTest)
1215+
.then(done);
1216+
});
11631217
});

0 commit comments

Comments
 (0)