Skip to content

Commit 587d206

Browse files
committed
Attempt simultaneous axis and data transitions
1 parent 35680d3 commit 587d206

File tree

9 files changed

+308
-216
lines changed

9 files changed

+308
-216
lines changed

src/lib/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -666,7 +666,7 @@ lib.numSeparate = function(value, separators, separatethousands) {
666666
* expand properties.
667667
*
668668
* @param {object} frameLookup
669-
* An object containing frames keyed by name (i.e. gd._frameData._frameHash)
669+
* An object containing frames keyed by name (i.e. gd._transitionData._frameHash)
670670
* @param {string} frame
671671
* The name of the keyframe to be computed
672672
*

src/plot_api/plot_api.js

Lines changed: 58 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
'use strict';
1111

12+
1213
var d3 = require('d3');
1314
var m4FromQuat = require('gl-mat4/fromQuat');
1415
var isNumeric = require('fast-isnumeric');
@@ -2569,6 +2570,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
25692570
var transitionedTraces = [];
25702571

25712572
function prepareTransitions() {
2573+
var plotinfo, i;
25722574
for(i = 0; i < traceIndices.length; i++) {
25732575
var traceIdx = traceIndices[i];
25742576
var trace = gd._fullData[traceIdx];
@@ -2592,30 +2594,46 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
25922594
Lib.extendDeepNoArrays(gd.data[traceIndices[i]], update);
25932595
}
25942596

2595-
Plots.supplyDataDefaults(gd.data, gd._fullData, gd._fullLayout);
2597+
// Supply defaults after applying the incoming properties. Note that any attempt
2598+
// to simplify this step and reduce the amount of work resulted in the reconstruction
2599+
// of essentially the whole supplyDefaults step, so that it seems sensible to just use
2600+
// supplyDefaults even though it's heavier than would otherwise be desired for
2601+
// transitions:
2602+
Plots.supplyDefaults(gd);
25962603

2597-
// TODO: Add logic that computes transitionedTraces to avoid unnecessary work while
2598-
// still handling things like box plots that are interrelated.
2599-
// doCalcdata(gd, transitionedTraces);
2604+
//Plotly.Axes.saveRangeInitial(gd, true);
2605+
2606+
// This step fies the .xaxis and .yaxis references that otherwise
2607+
// aren't updated by the supplyDefaults step:
2608+
var subplots = Plotly.Axes.getSubplots(gd);
2609+
for(i = 0; i < subplots.length; i++) {
2610+
plotinfo = gd._fullLayout._plots[subplots[i]];
2611+
plotinfo.xaxis = plotinfo.x();
2612+
plotinfo.yaxis = plotinfo.y();
2613+
}
26002614

26012615
doCalcdata(gd);
26022616

26032617
ErrorBars.calc(gd);
2618+
}
26042619

2605-
// While transitions are occuring, occurring, we get a double-transform
2606-
// issue if we transform the drawn layer *and* use the new axis range to
2607-
// draw the data. This causes setConvert to use the pre-interaction values
2608-
// of the axis range:
2609-
var axList = Plotly.Axes.list(gd);
2610-
for(i = 0; i < axList.length; i++) {
2611-
axList[i].setScale(true);
2620+
function executeCallbacks(list) {
2621+
var p = Promise.resolve();
2622+
if (!list) return p;
2623+
while(list.length) {
2624+
p = p.then((list.shift()));
26122625
}
2626+
return p;
26132627
}
26142628

2615-
var restyleList = [];
2616-
var completionTimeout = null;
2617-
var resolveTransitionCallback = null;
2629+
function flushCallbacks(list) {
2630+
if (!list) return;
2631+
while(list.length) {
2632+
list.shift();
2633+
}
2634+
}
26182635

2636+
var restyleList = [];
26192637
function executeTransitions() {
26202638
var hasTraceTransition = false;
26212639
var j;
@@ -2638,30 +2656,33 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26382656
}
26392657
}
26402658

2659+
gd._transitionData._completionTimeout = setTimeout(completeTransition, transitionConfig.duration);
2660+
26412661
if(!hasAxisTransition && !hasTraceTransition) {
26422662
return false;
26432663
}
2664+
}
26442665

2645-
return new Promise(function(resolve) {
2646-
resolveTransitionCallback = resolve;
2647-
completionTimeout = setTimeout(resolve, transitionConfig.duration);
2648-
});
2666+
function completeTransition() {
2667+
flushCallbacks(gd._transitionData._interruptCallbacks);
2668+
2669+
gd.emit('plotly_endtransition', []);
2670+
2671+
return executeCallbacks(gd._transitionData._cleanupCallbacks);
26492672
}
26502673

26512674
function interruptPreviousTransitions() {
2652-
clearTimeout(completionTimeout);
2653-
2654-
if(resolveTransitionCallback) {
2655-
resolveTransitionCallback();
2656-
}
2675+
if (gd._transitionData._completionTimeout) {
2676+
// Prevent the previous completion from occurring:
2677+
clearTimeout(gd._transitionData._completionTimeout);
2678+
gd._transitionData._completionTimeout = null;
26572679

2658-
while(gd._frameData._layoutInterrupts.length) {
2659-
(gd._frameData._layoutInterrupts.pop())();
2680+
// Interrupt an event to indicate that a transition was running:
2681+
gd.emit('plotly_interrupttransition', []);
26602682
}
26612683

2662-
while(gd._frameData._styleInterrupts.length) {
2663-
(gd._frameData._styleInterrupts.pop())();
2664-
}
2684+
flushCallbacks(gd._transitionData._cleanupCallbacks);
2685+
return executeCallbacks(gd._transitionData._interruptCallbacks);
26652686
}
26662687

26672688
for(i = 0; i < traceIndices.length; i++) {
@@ -2678,23 +2699,23 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26782699
thisUpdate[ai] = [data[i][ai]];
26792700
}
26802701

2681-
restyleList.push((function(md, data, traces) {
2702+
/*restyleList.push((function(md, data, traces) {
26822703
return function() {
26832704
return Plotly.restyle(gd, data, traces);
26842705
};
2685-
}(module, thisUpdate, [traceIdx])));
2706+
}(module, thisUpdate, [traceIdx])));*/
26862707
}
26872708
}
26882709

26892710
var seq = [Plots.previousPromises, interruptPreviousTransitions, prepareTransitions, executeTransitions];
2690-
seq = seq.concat(restyleList);
2711+
//seq = seq.concat(restyleList);
26912712

26922713
var plotDone = Lib.syncOrAsync(seq, gd);
26932714

26942715
if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
26952716

26962717
return plotDone.then(function() {
2697-
gd.emit('plotly_beginanimate', []);
2718+
gd.emit('plotly_begintransition', []);
26982719
return gd;
26992720
});
27002721
};
@@ -2710,7 +2731,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
27102731
Plotly.animate = function(gd, frameName, transitionConfig) {
27112732
gd = getGraphDiv(gd);
27122733

2713-
if(!gd._frameData._frameHash[frameName]) {
2734+
if(!gd._transitionData._frameHash[frameName]) {
27142735
Lib.warn('animateToFrame failure: keyframe does not exist', frameName);
27152736
return Promise.reject();
27162737
}
@@ -2740,8 +2761,8 @@ Plotly.addFrames = function(gd, frameList, indices) {
27402761
gd = getGraphDiv(gd);
27412762

27422763
var i, frame, j, idx;
2743-
var _frames = gd._frameData._frames;
2744-
var _hash = gd._frameData._frameHash;
2764+
var _frames = gd._transitionData._frames;
2765+
var _hash = gd._transitionData._frameHash;
27452766

27462767

27472768
if(!Array.isArray(frameList)) {
@@ -2781,7 +2802,7 @@ Plotly.addFrames = function(gd, frameList, indices) {
27812802
if(!frame.name) {
27822803
// Repeatedly assign a default name, incrementing the counter each time until
27832804
// we get a name that's not in the hashed lookup table:
2784-
while(_hash[(frame.name = 'frame ' + gd._frameData._counter++)]);
2805+
while(_hash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
27852806
}
27862807

27872808
if(_hash[frame.name]) {
@@ -2821,7 +2842,7 @@ Plotly.deleteFrames = function(gd, frameList) {
28212842
gd = getGraphDiv(gd);
28222843

28232844
var i, idx;
2824-
var _frames = gd._frameData._frames;
2845+
var _frames = gd._transitionData._frames;
28252846
var ops = [];
28262847
var revops = [];
28272848

0 commit comments

Comments
 (0)