Skip to content

Commit 32c479b

Browse files
committed
Attempt simultaneous axis and data transitions
1 parent 89a4b0f commit 32c479b

File tree

9 files changed

+308
-216
lines changed

9 files changed

+308
-216
lines changed

src/lib/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -656,7 +656,7 @@ lib.numSeparate = function(value, separators) {
656656
* expand properties.
657657
*
658658
* @param {object} frameLookup
659-
* An object containing frames keyed by name (i.e. gd._frameData._frameHash)
659+
* An object containing frames keyed by name (i.e. gd._transitionData._frameHash)
660660
* @param {string} frame
661661
* The name of the keyframe to be computed
662662
*

src/plot_api/plot_api.js

+58-37
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');
@@ -2567,6 +2568,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
25672568
var transitionedTraces = [];
25682569

25692570
function prepareTransitions() {
2571+
var plotinfo, i;
25702572
for(i = 0; i < traceIndices.length; i++) {
25712573
var traceIdx = traceIndices[i];
25722574
var trace = gd._fullData[traceIdx];
@@ -2590,30 +2592,46 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
25902592
Lib.extendDeepNoArrays(gd.data[traceIndices[i]], update);
25912593
}
25922594

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

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

25992613
doCalcdata(gd);
26002614

26012615
ErrorBars.calc(gd);
2616+
}
26022617

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

2613-
var restyleList = [];
2614-
var completionTimeout = null;
2615-
var resolveTransitionCallback = null;
2627+
function flushCallbacks(list) {
2628+
if (!list) return;
2629+
while(list.length) {
2630+
list.shift();
2631+
}
2632+
}
26162633

2634+
var restyleList = [];
26172635
function executeTransitions() {
26182636
var hasTraceTransition = false;
26192637
var j;
@@ -2636,30 +2654,33 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26362654
}
26372655
}
26382656

2657+
gd._transitionData._completionTimeout = setTimeout(completeTransition, transitionConfig.duration);
2658+
26392659
if(!hasAxisTransition && !hasTraceTransition) {
26402660
return false;
26412661
}
2662+
}
26422663

2643-
return new Promise(function(resolve) {
2644-
resolveTransitionCallback = resolve;
2645-
completionTimeout = setTimeout(resolve, transitionConfig.duration);
2646-
});
2664+
function completeTransition() {
2665+
flushCallbacks(gd._transitionData._interruptCallbacks);
2666+
2667+
gd.emit('plotly_endtransition', []);
2668+
2669+
return executeCallbacks(gd._transitionData._cleanupCallbacks);
26472670
}
26482671

26492672
function interruptPreviousTransitions() {
2650-
clearTimeout(completionTimeout);
2651-
2652-
if(resolveTransitionCallback) {
2653-
resolveTransitionCallback();
2654-
}
2673+
if (gd._transitionData._completionTimeout) {
2674+
// Prevent the previous completion from occurring:
2675+
clearTimeout(gd._transitionData._completionTimeout);
2676+
gd._transitionData._completionTimeout = null;
26552677

2656-
while(gd._frameData._layoutInterrupts.length) {
2657-
(gd._frameData._layoutInterrupts.pop())();
2678+
// Interrupt an event to indicate that a transition was running:
2679+
gd.emit('plotly_interrupttransition', []);
26582680
}
26592681

2660-
while(gd._frameData._styleInterrupts.length) {
2661-
(gd._frameData._styleInterrupts.pop())();
2662-
}
2682+
flushCallbacks(gd._transitionData._cleanupCallbacks);
2683+
return executeCallbacks(gd._transitionData._interruptCallbacks);
26632684
}
26642685

26652686
for(i = 0; i < traceIndices.length; i++) {
@@ -2676,23 +2697,23 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26762697
thisUpdate[ai] = [data[i][ai]];
26772698
}
26782699

2679-
restyleList.push((function(md, data, traces) {
2700+
/*restyleList.push((function(md, data, traces) {
26802701
return function() {
26812702
return Plotly.restyle(gd, data, traces);
26822703
};
2683-
}(module, thisUpdate, [traceIdx])));
2704+
}(module, thisUpdate, [traceIdx])));*/
26842705
}
26852706
}
26862707

26872708
var seq = [Plots.previousPromises, interruptPreviousTransitions, prepareTransitions, executeTransitions];
2688-
seq = seq.concat(restyleList);
2709+
//seq = seq.concat(restyleList);
26892710

26902711
var plotDone = Lib.syncOrAsync(seq, gd);
26912712

26922713
if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
26932714

26942715
return plotDone.then(function() {
2695-
gd.emit('plotly_beginanimate', []);
2716+
gd.emit('plotly_begintransition', []);
26962717
return gd;
26972718
});
26982719
};
@@ -2708,7 +2729,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
27082729
Plotly.animate = function(gd, frameName, transitionConfig) {
27092730
gd = getGraphDiv(gd);
27102731

2711-
if(!gd._frameData._frameHash[frameName]) {
2732+
if(!gd._transitionData._frameHash[frameName]) {
27122733
Lib.warn('animateToFrame failure: keyframe does not exist', frameName);
27132734
return Promise.reject();
27142735
}
@@ -2738,8 +2759,8 @@ Plotly.addFrames = function(gd, frameList, indices) {
27382759
gd = getGraphDiv(gd);
27392760

27402761
var i, frame, j, idx;
2741-
var _frames = gd._frameData._frames;
2742-
var _hash = gd._frameData._frameHash;
2762+
var _frames = gd._transitionData._frames;
2763+
var _hash = gd._transitionData._frameHash;
27432764

27442765

27452766
if(!Array.isArray(frameList)) {
@@ -2779,7 +2800,7 @@ Plotly.addFrames = function(gd, frameList, indices) {
27792800
if(!frame.name) {
27802801
// Repeatedly assign a default name, incrementing the counter each time until
27812802
// we get a name that's not in the hashed lookup table:
2782-
while(_hash[(frame.name = 'frame ' + gd._frameData._counter++)]);
2803+
while(_hash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
27832804
}
27842805

27852806
if(_hash[frame.name]) {
@@ -2819,7 +2840,7 @@ Plotly.deleteFrames = function(gd, frameList) {
28192840
gd = getGraphDiv(gd);
28202841

28212842
var i, idx;
2822-
var _frames = gd._frameData._frames;
2843+
var _frames = gd._transitionData._frames;
28232844
var ops = [];
28242845
var revops = [];
28252846

0 commit comments

Comments
 (0)