Skip to content

Commit 9c83914

Browse files
committed
Write Plotly.transition out of the picture yayyy
1 parent 14516cf commit 9c83914

File tree

8 files changed

+145
-76
lines changed

8 files changed

+145
-76
lines changed

src/components/errorbars/plot.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ module.exports = function plot(traces, plotinfo, transitionConfig) {
1919
var xa = plotinfo.x(),
2020
ya = plotinfo.y();
2121

22-
var hasAnimation = transitionConfig && transitionConfig.duration > 0;
22+
var hasAnimation = transitionConfig && transitionConfig.transitionduration > 0;
2323

2424
traces.each(function(d) {
2525
var trace = d[0].trace,
@@ -55,7 +55,7 @@ module.exports = function plot(traces, plotinfo, transitionConfig) {
5555

5656
if(hasAnimation) {
5757
enter.style('opacity', 0).transition()
58-
.duration(transitionConfig.duration)
58+
.duration(transitionConfig.transitionduration)
5959
.style('opacity', 1);
6060
}
6161

@@ -89,7 +89,7 @@ module.exports = function plot(traces, plotinfo, transitionConfig) {
8989
} else if(hasAnimation) {
9090
yerror = yerror
9191
.transition()
92-
.duration(transitionConfig.duration)
92+
.duration(transitionConfig.transitionduration)
9393
.ease(transitionConfig.ease);
9494
}
9595

@@ -117,7 +117,7 @@ module.exports = function plot(traces, plotinfo, transitionConfig) {
117117
} else if(hasAnimation) {
118118
xerror = xerror
119119
.transition()
120-
.duration(transitionConfig.duration)
120+
.duration(transitionConfig.transitionduration)
121121
.ease(transitionConfig.ease);
122122
}
123123

src/plot_api/plot_api.js

Lines changed: 88 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2628,7 +2628,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionOpts) {
26282628
// When instantaneous updates are coming through quickly, it's too much to simply disable
26292629
// all interaction, so store this flag so we can disambiguate whether mouse interactions
26302630
// should be fully disabled or not:
2631-
if(transitionOpts.duration > 0) {
2631+
if(transitionOpts.transitionduration > 0) {
26322632
gd._transitioningWithDuration = true;
26332633
}
26342634

@@ -2670,7 +2670,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionOpts) {
26702670
// to instantaneous.
26712671
if(hasAxisTransition) {
26722672
traceTransitionOpts = Lib.extendFlat({}, transitionOpts);
2673-
traceTransitionOpts.duration = 0;
2673+
traceTransitionOpts.transitionduration = 0;
26742674
} else {
26752675
traceTransitionOpts = transitionOpts;
26762676
}
@@ -2756,7 +2756,7 @@ Plotly.transition = function(gd, data, layout, traceIndices, transitionOpts) {
27562756
* @param {object} transitionOpts
27572757
* configuration for transition
27582758
*/
2759-
Plotly.animate = function(gd, groupNameOrFrameList, transitionOpts, animationOpts) {
2759+
Plotly.animate = function(gd, frameOrGroupNameOrFrameList, transitionOpts, animationOpts) {
27602760
gd = getGraphDiv(gd);
27612761
var trans = gd._transitionData;
27622762

@@ -2808,7 +2808,12 @@ Plotly.animate = function(gd, groupNameOrFrameList, transitionOpts, animationOpt
28082808
if(frameList.length === 0) return;
28092809

28102810
for(var i = 0; i < frameList.length; i++) {
2811-
var computedFrame = Plots.computeFrame(gd, frameList[i].name);
2811+
var computedFrame;
2812+
if(frameList[i].name) {
2813+
computedFrame = Plots.computeFrame(gd, frameList[i].name);
2814+
} else {
2815+
computedFrame = frameList[i].frame;
2816+
}
28122817

28132818
var opts = Plots.supplyTransitionDefaults(getTransitionOpts(i));
28142819

@@ -2836,9 +2841,54 @@ Plotly.animate = function(gd, groupNameOrFrameList, transitionOpts, animationOpt
28362841
trans._animationRaf = null;
28372842
}
28382843

2844+
function nextFrame() {
2845+
if(trans._currentFrame) {
2846+
if(trans._currentFrame.onComplete) {
2847+
trans._currentFrame.onComplete();
2848+
}
2849+
}
2850+
2851+
var newFrame = trans._currentFrame = trans._frameQueue.shift();
2852+
2853+
if(newFrame) {
2854+
trans._lastframeat = Date.now();
2855+
trans._timetonext = newFrame.transitionOpts.frameduration;
2856+
2857+
Plotly.transition(gd,
2858+
newFrame.frame.data,
2859+
newFrame.frame.layout,
2860+
newFrame.frame.traces,
2861+
newFrame.transitionOpts
2862+
).then(function() {
2863+
if(trans._frameQueue.length === 0) {
2864+
gd.emit('plotly_animated');
2865+
if(trans._currentFrame && trans._currentFrame.onComplete) {
2866+
trans._currentFrame.onComplete();
2867+
trans._currentFrame = null;
2868+
}
2869+
}
2870+
});
2871+
}
2872+
2873+
if(trans._frameQueue.length === 0) {
2874+
stopAnimationLoop();
2875+
return;
2876+
}
2877+
}
2878+
28392879
function beginAnimationLoop() {
28402880
gd.emit('plotly_animating');
28412881

2882+
var canAnimateSynchronously = !trans._animationRaf && trans._frameQueue.length === 1;
2883+
2884+
if(canAnimateSynchronously) {
2885+
// If there is no animation running and only one frame has been received, then
2886+
// simply transition this frame synchonously and avoid starting and stopping the
2887+
// timing loop.
2888+
nextFrame();
2889+
return;
2890+
}
2891+
28422892
// If no timer is running, then set last frame = long ago:
28432893
trans._lastframeat = 0;
28442894
trans._timetonext = 0;
@@ -2848,38 +2898,7 @@ Plotly.animate = function(gd, groupNameOrFrameList, transitionOpts, animationOpt
28482898
var doFrame = function() {
28492899
// Check if we need to pop a frame:
28502900
if(Date.now() - trans._lastframeat > trans._timetonext) {
2851-
if(trans._currentFrame) {
2852-
if(trans._currentFrame.onComplete) {
2853-
trans._currentFrame.onComplete();
2854-
}
2855-
}
2856-
2857-
var newFrame = trans._currentFrame = trans._frameQueue.shift();
2858-
2859-
if(newFrame) {
2860-
trans._lastframeat = Date.now();
2861-
trans._timetonext = newFrame.transitionOpts.frameduration;
2862-
2863-
Plotly.transition(gd,
2864-
newFrame.frame.data,
2865-
newFrame.frame.layout,
2866-
newFrame.frame.traces,
2867-
newFrame.transitionOpts
2868-
).then(function() {
2869-
if(trans._frameQueue.length === 0) {
2870-
gd.emit('plotly_animated');
2871-
if(trans._currentFrame && trans._currentFrame.onComplete) {
2872-
trans._currentFrame.onComplete();
2873-
trans._currentFrame = null;
2874-
}
2875-
}
2876-
});
2877-
}
2878-
2879-
if(trans._frameQueue.length === 0) {
2880-
stopAnimationLoop();
2881-
return;
2882-
}
2901+
nextFrame();
28832902
}
28842903

28852904
trans._animationRaf = requestAnimationFrame(doFrame);
@@ -2891,34 +2910,59 @@ Plotly.animate = function(gd, groupNameOrFrameList, transitionOpts, animationOpt
28912910
var counter = 0;
28922911
function setTransitionConfig(frame) {
28932912
if(Array.isArray(transitionOpts)) {
2894-
frame.transitionOpts = transitionOpts[counter];
2913+
if(counter >= transitionOpts.length) {
2914+
frame.transitionOpts = transitionOpts[counter];
2915+
} else {
2916+
frame.transitionOpts = transitionOpts[0];
2917+
}
28952918
} else {
28962919
frame.transitionOpts = transitionOpts;
28972920
}
28982921
counter++;
28992922
return frame;
29002923
}
29012924

2925+
// Disambiguate what's been received. The possibilities are:
2926+
//
2927+
// - group: 'groupname': select frames by group name
2928+
// - frames ['frame1', frame2']: a list of frames
2929+
// - object: {data: ...}: a single frame itself
2930+
// - frames [{data: ...}, {data: ...}]: a list of frames
2931+
//
29022932
var i, frame;
29032933
var frameList = [];
2904-
var allFrames = groupNameOrFrameList === undefined || groupNameOrFrameList === null;
2905-
if(allFrames || typeof groupNameOrFrameList === 'string') {
2934+
var allFrames = frameOrGroupNameOrFrameList === undefined || frameOrGroupNameOrFrameList === null;
2935+
var isFrameArray = Array.isArray(frameOrGroupNameOrFrameList);
2936+
var isSingleFrame = !allFrames && !isFrameArray && typeof frameOrGroupNameOrFrameList === 'object';
2937+
2938+
if(isSingleFrame) {
2939+
frameList.push(setTransitionConfig({
2940+
frame: Lib.extendFlat({}, frameOrGroupNameOrFrameList)
2941+
}));
2942+
} else if(allFrames || typeof frameOrGroupNameOrFrameList === 'string') {
29062943
for(i = 0; i < trans._frames.length; i++) {
29072944
frame = trans._frames[i];
29082945

2909-
if(allFrames || frame.group === groupNameOrFrameList) {
2946+
if(allFrames || frame.group === frameOrGroupNameOrFrameList) {
29102947
frameList.push(setTransitionConfig({name: frame.name}));
29112948
}
29122949
}
2913-
} else if(Array.isArray(groupNameOrFrameList)) {
2914-
for(i = 0; i < groupNameOrFrameList.length; i++) {
2915-
frameList.push(setTransitionConfig({name: groupNameOrFrameList[i]}));
2950+
} else if(isFrameArray) {
2951+
for(i = 0; i < frameOrGroupNameOrFrameList.length; i++) {
2952+
var frameOrName = frameOrGroupNameOrFrameList[i];
2953+
if(typeof frameOrName === 'string') {
2954+
frameList.push(setTransitionConfig({name: frameOrName}));
2955+
} else {
2956+
frameList.push(setTransitionConfig({
2957+
frame: Lib.extendFlat({}, frameOrName)
2958+
}));
2959+
}
29162960
}
29172961
}
29182962

29192963
// Verify that all of these frames actually exist; return and reject if not:
29202964
for(i = 0; i < frameList.length; i++) {
2921-
if(!trans._frameHash[frameList[i].name]) {
2965+
if(frameList[i].name && !trans._frameHash[frameList[i].name]) {
29222966
Lib.warn('animate failure: frame not found: "' + frameList[i].name + '"');
29232967
reject();
29242968
return;

src/plots/cartesian/transition_axes.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,14 +288,14 @@ module.exports = function transitionAxes(gd, newLayout, transitionConfig, makeOn
288288
function doFrame() {
289289
t2 = Date.now();
290290

291-
var tInterp = Math.min(1, (t2 - t1) / transitionConfig.duration);
291+
var tInterp = Math.min(1, (t2 - t1) / transitionConfig.transitionduration);
292292
var progress = easeFn(tInterp);
293293

294294
for(var i = 0; i < affectedSubplots.length; i++) {
295295
updateSubplot(affectedSubplots[i], progress);
296296
}
297297

298-
if(t2 - t1 > transitionConfig.duration) {
298+
if(t2 - t1 > transitionConfig.transitionduration) {
299299
transitionComplete();
300300
raf = cancelAnimationFrame(doFrame);
301301
} else {

src/plots/plots.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ plots.supplyTransitionDefaults = function(opts) {
634634
}
635635

636636
coerce('frameduration');
637-
coerce('duration');
637+
coerce('transitionduration');
638638
coerce('ease');
639639
coerce('redraw');
640640

src/plots/transition_attributes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = {
1616
dflt: 500,
1717
description: 'The duration in milliseconds of each frame.'
1818
},
19-
duration: {
19+
transitionduration: {
2020
valType: 'number',
2121
role: 'info',
2222
min: 0,

src/traces/scatter/plot.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module.exports = function plot(gd, plotinfo, cdscatter, transitionConfig, makeOn
2929
// If transition config is provided, then it is only a partial replot and traces not
3030
// updated are removed.
3131
var isFullReplot = !transitionConfig;
32-
var hasTransition = !!transitionConfig && transitionConfig.duration > 0;
32+
var hasTransition = !!transitionConfig && transitionConfig.transitionduration > 0;
3333

3434
selection = scatterlayer.selectAll('g.trace');
3535

@@ -71,7 +71,7 @@ module.exports = function plot(gd, plotinfo, cdscatter, transitionConfig, makeOn
7171
}
7272

7373
var transition = d3.transition()
74-
.duration(transitionConfig.duration)
74+
.duration(transitionConfig.transitionduration)
7575
.ease(transitionConfig.ease)
7676
.each('end', function() {
7777
onComplete && onComplete();
@@ -150,7 +150,7 @@ function plotOne(gd, idx, plotinfo, cdscatter, cdscatterAll, element, transition
150150
// since it does an internal n^2 loop over comparisons with other traces:
151151
selectMarkers(gd, idx, plotinfo, cdscatter, cdscatterAll);
152152

153-
var hasTransition = !!transitionConfig && transitionConfig.duration > 0;
153+
var hasTransition = !!transitionConfig && transitionConfig.transitionduration > 0;
154154

155155
function transition(selection) {
156156
return hasTransition ? selection.transition() : selection;

0 commit comments

Comments
 (0)