Skip to content

[DO NOT MERGE] Animations #550

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
54bf57c
First cut at animations
rreusser May 18, 2016
c3517c2
Slightly ugly hack to animate point styles
rreusser May 31, 2016
dba188b
Merge remote-tracking branch 'upstream/master' into animations
rreusser Jun 2, 2016
2eae93a
Remove stray markTime
rreusser Jun 2, 2016
e9bcf90
A method to clone input traces
rreusser Jun 2, 2016
5497e7d
Make animations promise-based
rreusser Jun 8, 2016
c159dae
Non-working commit in the right direction...
rreusser Jun 8, 2016
1449cc5
Partial trace refactor
rreusser Jun 9, 2016
99b4bed
Scattertrace element persistence cleanup
rreusser Jun 10, 2016
7285a0a
Further animation progress and cleanup
rreusser Jun 11, 2016
8dc302c
Scatter trace optimization
rreusser Jun 12, 2016
5a624de
Scatter trace optimization
rreusser Jun 12, 2016
d580df3
Merge branch 'animations' of github.com:rreusser/plotly.js into anima…
rreusser Jun 13, 2016
81e2182
Refactor scatter traces
rreusser Jun 14, 2016
82071e3
Refactor scatter traces
rreusser Jun 14, 2016
710de7f
Merge branch 'animations' of github.com:rreusser/plotly.js into anima…
rreusser Jun 14, 2016
48e9b29
Merge remote-tracking branch 'upstream/master' into animations
rreusser Jun 14, 2016
aa19ac0
Add build
rreusser Jun 14, 2016
821c57e
Object persistence for error bars
rreusser Jun 16, 2016
a655a4b
Error bar transitions
rreusser Jun 17, 2016
c978b46
First cut at scale transitions
rreusser Jun 20, 2016
51672d8
Undo build
rreusser Jun 20, 2016
31f4f1b
Merge remote-tracking branch 'upstream/master' into animations
rreusser Jun 20, 2016
5c5c715
Animation interrupts
rreusser Jun 20, 2016
b75280f
Remove linepoint override
rreusser Jun 21, 2016
990787e
Removed unused logic
rreusser Jun 21, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[*.js]
indent_style = space
indent_size = 4
4 changes: 2 additions & 2 deletions dist/plotly-geo-assets.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2,150 changes: 1,112 additions & 1,038 deletions dist/plotly-with-meta.js

Large diffs are not rendered by default.

1,867 changes: 980 additions & 887 deletions dist/plotly.js

Large diffs are not rendered by default.

47 changes: 24 additions & 23 deletions dist/plotly.min.js

Large diffs are not rendered by default.

59 changes: 52 additions & 7 deletions src/components/drawing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,61 @@ drawing.setRect = function(s, x, y, w, h) {
s.call(drawing.setPosition, x, y).call(drawing.setSize, w, h);
};

drawing.translatePoints = function(s, xa, ya) {
s.each(function(d) {
drawing.translatePoints = function(s, xa, ya, trace, transitionConfig, joinDirection) {

var hasTransition = transitionConfig && (transitionConfig || {}).duration > 0;

if (hasTransition) {
var size = s.size();
}

s.each(function(d, i) {
// put xp and yp into d if pixel scaling is already done
var x = d.xp || xa.c2p(d.x),
y = d.yp || ya.c2p(d.y),
p = d3.select(this);
if(isNumeric(x) && isNumeric(y)) {
// for multiline text this works better
if(this.nodeName === 'text') p.attr('x', x).attr('y', y);
else p.attr('transform', 'translate(' + x + ',' + y + ')');
if(this.nodeName==='text') {
p.attr('x',x).attr('y',y);
} else {
if (hasTransition) {
var trans;
if (!joinDirection) {
trans = p.transition()
.delay(transitionConfig.delay + transitionConfig.cascade / size * i)
.duration(transitionConfig.duration)
.ease(transitionConfig.easing)
.attr('transform', 'translate('+x+','+y+')')

if (trace) {
trans.call(drawing.pointStyle, trace)
}
} else if (joinDirection === -1) {
trans = p.style('opacity', 1)
.transition()
.duration(transitionConfig.duration)
.ease(transitionConfig.easing)
.style('opacity', 0)
.remove();
} else if (joinDirection === 1) {
trans = p.attr('transform', 'translate('+x+','+y+')')

if (trace) {
trans.call(drawing.pointStyle, trace)
}

trans.style('opacity', 0)
.transition()
.duration(transitionConfig.duration)
.ease(transitionConfig.easing)
.style('opacity', 1)
}

} else {
p.attr('transform', 'translate('+x+','+y+')');
}
}
}
else p.remove();
});
Expand Down Expand Up @@ -218,7 +263,7 @@ drawing.pointStyle = function(s, trace) {
markerScale = drawing.tryColorscale(marker, markerIn, ''),
lineScale = drawing.tryColorscale(marker, markerIn, 'line.');

s.each(function(d) {
s.each(function(d, i) {
// 'so' is suspected outliers, for box plots
var fillColor,
lineColor,
Expand All @@ -235,11 +280,11 @@ drawing.pointStyle = function(s, trace) {

if('mlc' in d) lineColor = d.mlcc = lineScale(d.mlc);
// weird case: array wasn't long enough to apply to every point
else if(Array.isArray(markerLine.color)) lineColor = Color.defaultLine;
else if(Array.isArray(markerLine.color)) lineColor = marker.color[i];
else lineColor = markerLine.color;

if('mc' in d) fillColor = d.mcc = markerScale(d.mc);
else if(Array.isArray(marker.color)) fillColor = Color.defaultLine;
else if(Array.isArray(marker.color)) fillColor = marker.color[i];
else fillColor = marker.color || 'rgba(0,0,0,0)';
}

Expand Down
88 changes: 79 additions & 9 deletions src/components/errorbars/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ var isNumeric = require('fast-isnumeric');

var Lib = require('../../lib');
var subTypes = require('../../traces/scatter/subtypes');
var styleError = require('./style');


module.exports = function plot(traces, plotinfo) {
module.exports = function plot(traces, plotinfo, transitionConfig) {
var xa = plotinfo.x(),
ya = plotinfo.y();


transitionConfig = transitionConfig || {};
var hasAnimation = isNumeric(transitionConfig.duration) && transitionConfig.duration > 0

traces.each(function(d) {
var trace = d[0].trace,
// || {} is in case the trace (specifically scatterternary)
Expand All @@ -29,19 +34,37 @@ module.exports = function plot(traces, plotinfo) {
xObj = trace.error_x || {},
yObj = trace.error_y || {};

var keyFunc;

if (trace.key) {
keyFunc = function (d) { return d.key };
}

var sparse = (
subTypes.hasMarkers(trace) &&
trace.marker.maxdisplayed > 0
);

if(!yObj.visible && !xObj.visible) return;


var errorbars = d3.select(this).selectAll('g.errorbar')
.data(Lib.identity);
.data(Lib.identity, keyFunc);

errorbars.enter().append('g')
.classed('errorbar', true);

if (hasAnimation) {
errorbars.exit()
.style('opacity', 1)
.transition()
.duration(transitionConfig.duration)
.style('opacity', 0)
.remove();
} else {
errorbars.exit().remove();
}

errorbars.each(function(d) {
var errorbar = d3.select(this);
var coords = errorCoords(d, xa, ya);
Expand All @@ -59,14 +82,37 @@ module.exports = function plot(traces, plotinfo) {
coords.yh + 'h' + (2 * yw) + // hat
'm-' + yw + ',0V' + coords.ys; // bar


if(!coords.noYS) path += 'm-' + yw + ',0h' + (2 * yw); // shoe

errorbar.append('path')
.classed('yerror', true)
.attr('d', path);
var yerror = errorbar.select('path.yerror');

var isNew = !yerror.size();

if (isNew) {
yerror = errorbar.append('path')
.classed('yerror', true);

if (hasAnimation) {
yerror = yerror.style('opacity', 0);
}
} else if (hasAnimation) {
yerror = yerror.transition()
.duration(transitionConfig.duration)
.ease(transitionConfig.easing)
.delay(transitionConfig.delay)
}

yerror.attr('d', path);

if (isNew && hasAnimation) {
yerror = yerror.transition()
.duration(transitionConfig.duration)
.style('opacity', 1);
}
}

if(xObj.visible && isNumeric(coords.y) &&
if(xObj.visible && isNumeric(coords.x) &&
isNumeric(coords.xh) &&
isNumeric(coords.xs)) {
var xw = (xObj.copy_ystyle ? yObj : xObj).width;
Expand All @@ -77,11 +123,35 @@ module.exports = function plot(traces, plotinfo) {

if(!coords.noXS) path += 'm0,-' + xw + 'v' + (2 * xw); // shoe

errorbar.append('path')
.classed('xerror', true)
.attr('d', path);
var xerror = errorbar.select('path.xerror');

var isNew = !xerror.size();

if (isNew) {
xerror = errorbar.append('path')
.classed('xerror', true);

if (hasAnimation) {
xerror = xerror.style('opacity', 0);
}
} else if (hasAnimation) {
xerror = xerror.transition()
.duration(transitionConfig.duration)
.ease(transitionConfig.easing)
.delay(transitionConfig.delay)
}

xerror.attr('d', path);

if (isNew && hasAnimation) {
xerror = xerror.transition()
.duration(transitionConfig.duration)
.style('opacity', 1);
}
}
});

d3.select(this).call(styleError);
});
};

Expand Down
1 change: 1 addition & 0 deletions src/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ exports.version = '1.13.0';
exports.plot = Plotly.plot;
exports.newPlot = Plotly.newPlot;
exports.restyle = Plotly.restyle;
exports.animate = Plotly.animate;
exports.relayout = Plotly.relayout;
exports.redraw = Plotly.redraw;
exports.extendTraces = Plotly.extendTraces;
Expand Down
86 changes: 86 additions & 0 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -585,3 +585,89 @@ lib.numSeparate = function(value, separators) {

return x1 + x2;
};

/*
* Deep copy of an object, subject to some caveats. Adapted from
* http://stackoverflow.com/questions/10728412/in-javascript-when-performing-a-deep-copy-how-do-i-avoid-a-cycle-due-to-a-pro#answer-10729242
*
* It does some basics like avoiding infinite loops, but leaves some gaps
* regarding prototypes and functions. But those shouldn't be part of the
* layout anyway, which is the intended use.
*
* @param {object} object the object to be cloned
* @param {function} shallowFilter a callback executed on each attribute name
* @param {Array} path a stack representing the current attr path
*
* @return {object} the cloned object
*/
lib.deepClone = function deepClone (object, shallowFilter, path) {
var nextPath, isShallow;
var gdcc = "__getDeepCircularCopy__";
if (object !== Object(object)) {
return object; // primitive value
}

var set = gdcc in object;
var cache = object[gdcc];
var result;
if (set && typeof cache == "function") {
return cache();
}
// else
object[gdcc] = function () { return result; }; // overwrite

if (object instanceof Array) {
result = [];
for (var i=0; i<object.length; i++) {
nextPath = path ? (path + '.' + i) : i;
result[i] = deepClone(object[i], shallowFilter, nextPath);
}
} else {
result = {};
for (var attr in object) {
nextPath = path ? (path + '.' + attr) : attr;

if (attr !== gdcc) {
isShallow = shallowFilter && shallowFilter(nextPath);

if (isShallow) {
result[attr] = object[attr];
} else {
result[attr] = deepClone(object[attr], shallowFilter, nextPath);
}
} else if (set) {
result[attr] = deepClone(cache, shallowFilter, nextPath);
}
}
}
if (set) {
object[gdcc] = cache; // reset
} else {
delete object[gdcc]; // unset again
}
return result;
};

/*
* Deep copy of a trace, excluding deep copies of data arrays, to which the
* original reference is preserved.
*
* @param {object} trace the trace to copy
* @param {object} schema the corresponding schema, e.g.
* Plotly.PlotSchema.get().traces['scatter']
*
* @return {object} the cloned trace
*/
lib.deepCloneTrace = function deepCloneTrace (trace, schema) {
// This is intentionally allowed to fail if the schema is bad because
// the schema shouldn't be bad:
//
// var attrs = (!!schema ? schema.attributes : {}) || {};
//
var attrs = schema.attributes;

return lib.deepClone(trace, function (attr) {
var props = lib.nestedProperty(attrs, attr).get();
return props && props.valType === 'data_array';
});
};
Loading