Skip to content

Commit 3ea4bfc

Browse files
committed
Attempt simultaneous axis and data transitions
1 parent f4c631e commit 3ea4bfc

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');
@@ -2563,6 +2564,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
25632564
var transitionedTraces = [];
25642565

25652566
function prepareTransitions() {
2567+
var plotinfo, i;
25662568
for(i = 0; i < traceIndices.length; i++) {
25672569
var traceIdx = traceIndices[i];
25682570
var trace = gd._fullData[traceIdx];
@@ -2586,30 +2588,46 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
25862588
Lib.extendDeepNoArrays(gd.data[traceIndices[i]], update);
25872589
}
25882590

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

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

25952609
doCalcdata(gd);
25962610

25972611
ErrorBars.calc(gd);
2612+
}
25982613

2599-
// While transitions are occuring, occurring, we get a double-transform
2600-
// issue if we transform the drawn layer *and* use the new axis range to
2601-
// draw the data. This causes setConvert to use the pre-interaction values
2602-
// of the axis range:
2603-
var axList = Plotly.Axes.list(gd);
2604-
for(i = 0; i < axList.length; i++) {
2605-
axList[i].setScale(true);
2614+
function executeCallbacks(list) {
2615+
var p = Promise.resolve();
2616+
if (!list) return p;
2617+
while(list.length) {
2618+
p = p.then((list.shift()));
26062619
}
2620+
return p;
26072621
}
26082622

2609-
var restyleList = [];
2610-
var completionTimeout = null;
2611-
var resolveTransitionCallback = null;
2623+
function flushCallbacks(list) {
2624+
if (!list) return;
2625+
while(list.length) {
2626+
list.shift();
2627+
}
2628+
}
26122629

2630+
var restyleList = [];
26132631
function executeTransitions() {
26142632
var hasTraceTransition = false;
26152633
var j;
@@ -2632,30 +2650,33 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26322650
}
26332651
}
26342652

2653+
gd._transitionData._completionTimeout = setTimeout(completeTransition, transitionConfig.duration);
2654+
26352655
if(!hasAxisTransition && !hasTraceTransition) {
26362656
return false;
26372657
}
2658+
}
26382659

2639-
return new Promise(function(resolve) {
2640-
resolveTransitionCallback = resolve;
2641-
completionTimeout = setTimeout(resolve, transitionConfig.duration);
2642-
});
2660+
function completeTransition() {
2661+
flushCallbacks(gd._transitionData._interruptCallbacks);
2662+
2663+
gd.emit('plotly_endtransition', []);
2664+
2665+
return executeCallbacks(gd._transitionData._cleanupCallbacks);
26432666
}
26442667

26452668
function interruptPreviousTransitions() {
2646-
clearTimeout(completionTimeout);
2647-
2648-
if(resolveTransitionCallback) {
2649-
resolveTransitionCallback();
2650-
}
2669+
if (gd._transitionData._completionTimeout) {
2670+
// Prevent the previous completion from occurring:
2671+
clearTimeout(gd._transitionData._completionTimeout);
2672+
gd._transitionData._completionTimeout = null;
26512673

2652-
while(gd._frameData._layoutInterrupts.length) {
2653-
(gd._frameData._layoutInterrupts.pop())();
2674+
// Interrupt an event to indicate that a transition was running:
2675+
gd.emit('plotly_interrupttransition', []);
26542676
}
26552677

2656-
while(gd._frameData._styleInterrupts.length) {
2657-
(gd._frameData._styleInterrupts.pop())();
2658-
}
2678+
flushCallbacks(gd._transitionData._cleanupCallbacks);
2679+
return executeCallbacks(gd._transitionData._interruptCallbacks);
26592680
}
26602681

26612682
for(i = 0; i < traceIndices.length; i++) {
@@ -2672,23 +2693,23 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
26722693
thisUpdate[ai] = [data[i][ai]];
26732694
}
26742695

2675-
restyleList.push((function(md, data, traces) {
2696+
/*restyleList.push((function(md, data, traces) {
26762697
return function() {
26772698
return Plotly.restyle(gd, data, traces);
26782699
};
2679-
}(module, thisUpdate, [traceIdx])));
2700+
}(module, thisUpdate, [traceIdx])));*/
26802701
}
26812702
}
26822703

26832704
var seq = [Plots.previousPromises, interruptPreviousTransitions, prepareTransitions, executeTransitions];
2684-
seq = seq.concat(restyleList);
2705+
//seq = seq.concat(restyleList);
26852706

26862707
var plotDone = Lib.syncOrAsync(seq, gd);
26872708

26882709
if(!plotDone || !plotDone.then) plotDone = Promise.resolve();
26892710

26902711
return plotDone.then(function() {
2691-
gd.emit('plotly_beginanimate', []);
2712+
gd.emit('plotly_begintransition', []);
26922713
return gd;
26932714
});
26942715
};
@@ -2704,7 +2725,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionConfig) {
27042725
Plotly.animate = function(gd, frameName, transitionConfig) {
27052726
gd = getGraphDiv(gd);
27062727

2707-
if(!gd._frameData._frameHash[frameName]) {
2728+
if(!gd._transitionData._frameHash[frameName]) {
27082729
Lib.warn('animateToFrame failure: keyframe does not exist', frameName);
27092730
return Promise.reject();
27102731
}
@@ -2734,8 +2755,8 @@ Plotly.addFrames = function(gd, frameList, indices) {
27342755
gd = getGraphDiv(gd);
27352756

27362757
var i, frame, j, idx;
2737-
var _frames = gd._frameData._frames;
2738-
var _hash = gd._frameData._frameHash;
2758+
var _frames = gd._transitionData._frames;
2759+
var _hash = gd._transitionData._frameHash;
27392760

27402761

27412762
if(!Array.isArray(frameList)) {
@@ -2775,7 +2796,7 @@ Plotly.addFrames = function(gd, frameList, indices) {
27752796
if(!frame.name) {
27762797
// Repeatedly assign a default name, incrementing the counter each time until
27772798
// we get a name that's not in the hashed lookup table:
2778-
while(_hash[(frame.name = 'frame ' + gd._frameData._counter++)]);
2799+
while(_hash[(frame.name = 'frame ' + gd._transitionData._counter++)]);
27792800
}
27802801

27812802
if(_hash[frame.name]) {
@@ -2815,7 +2836,7 @@ Plotly.deleteFrames = function(gd, frameList) {
28152836
gd = getGraphDiv(gd);
28162837

28172838
var i, idx;
2818-
var _frames = gd._frameData._frames;
2839+
var _frames = gd._transitionData._frames;
28192840
var ops = [];
28202841
var revops = [];
28212842

0 commit comments

Comments
 (0)