-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/snapshot #1
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
Changes from all commits
547f2b5
89e05ce
fb52fe7
4436f3c
760a93b
cff741a
937340c
ac86931
ef09066
099acf3
a223546
1f09a8e
00623e2
1f7c8e3
4b618db
e1b031e
dc33644
5116a84
8fbf716
e7da65d
74e9bf0
9a8559e
166724b
9dc9423
13eef9f
f124a03
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
# Plotly Snapshots | ||
|
||
## Purpose | ||
The purpose of this markdown document is to document exploration of how to best attach the `Plotly.Snapshot.toImage` function to the plot/`div` itself most fully discussed in [issue 83](https://github.com/plotly/plotly.js/issues/83). Another very nice ability would be to offer resize options for the snapshot. | ||
|
||
|
||
|
||
## Questions | ||
Where do we attach toImage on the graph div? | ||
Is it _toImage? | ||
Do we just require /snapshot and bind to `this`? | ||
|
||
Will any of the chart types require special snapshot abilities or features? | ||
|
||
What is the expected use case of our new ability? | ||
|
||
How do we piggyback on the snapshot button in the toolbar? | ||
|
||
How do we ask for new size? | ||
|
||
Are there reference points from other libraries that we could mimic or learn from? | ||
|
||
|
||
## Thoughts | ||
|
||
- `Plotly.Snapshot.clone` could be used to resize by adding this to `options` when/if we use `Plotly.plot` with our cloned `div`. We could also dynamically show a resulting view in a modal or something similar and adjust with `Plotly.relayout`. | ||
|
||
- `Plotly.Snapshot.clone` by default sets `staticPlot:true` in `config`. | ||
|
||
- A very basic way to attach this assuming there is a modebar would be to do something like this. See [codepen](http://codepen.io/timelyportfolio/pen/ZWvyYM). | ||
``` | ||
gd._toImage = function(){ | ||
this._fullLayout._modeBar.buttons.filter( | ||
function(btn){return btn[0].name==="toImage" | ||
})[0][0].click(this) | ||
} | ||
``` | ||
|
||
- `Plotly.Snapshot.clone` already has thumbnail ability by specifying [options tileClass:"thumbnail"](https://github.com/plotly/plotly.js/blob/master/src/snapshot/cloneplot.js#L76) for the specific thumbnail use case. | ||
|
||
|
||
|
||
- Quick code to experiment from R | ||
``` | ||
library(plotly) | ||
|
||
ggplotly(ggplot(cars,aes(speed,dist))+geom_point()) | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,8 @@ exports.moveTraces = Plotly.moveTraces; | |
exports.purge = Plotly.purge; | ||
exports.setPlotConfig = require('./plot_api/set_plot_config'); | ||
exports.register = Plotly.register; | ||
exports.toImage = require('./plot_api/to_image'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @timelyportfolio any objections to add There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the way you wrote exports.downloadImage = require('./snapshot/download'); below this line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. makes sense, and I am sure it would get used. I'll just need to add some tests to feel good about it if it is an "official" function. |
||
exports.downloadImage = require('./snapshot/download'); | ||
|
||
// plot icons | ||
exports.Icons = require('../build/ploticon'); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/** | ||
* Copyright 2012-2016, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
/*eslint dot-notation: [2, {"allowPattern": "^catch$"}]*/ | ||
|
||
'use strict'; | ||
|
||
var Plotly = require('../plotly'); | ||
|
||
var isNumeric = require('fast-isnumeric'); | ||
|
||
/** | ||
* @param {object} gd figure Object | ||
* @param {object} opts option object | ||
* @param opts.format 'jpeg' | 'png' | 'webp' | 'svg' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. add info about |
||
* @param opts.width width of snapshot in px | ||
* @param opts.height height of snapshot in px | ||
*/ | ||
function toImage(gd, opts) { | ||
var Snapshot = require('../snapshot'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. bring There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. embarrassed to say, but when I do this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh. That mean there's a circular dependency somewhere between Removing that circular dependency might be tricky. I think it would require splitting up You should be able to get away with using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok |
||
|
||
var promise = new Promise(function(resolve, reject) { | ||
// check for undefined opts | ||
opts = opts || {}; | ||
// default to png | ||
opts.format = opts.format || 'png'; | ||
|
||
if( | ||
(opts.width && isNumeric(opts.width) && opts.width < 1) || | ||
(opts.height && isNumeric(opts.height) && opts.height < 1) | ||
) { | ||
reject(new Error('Height and width should be pixel values.')); | ||
} | ||
|
||
// first clone the GD so we can operate in a clean environment | ||
var clone = Snapshot.clone(gd, {format: 'png', height: opts.height, width: opts.width}); | ||
var clonedGd = clone.td; | ||
|
||
// put the cloned div somewhere off screen before attaching to DOM | ||
clonedGd.style.position = 'absolute'; | ||
clonedGd.style.left = '-5000px'; | ||
document.body.appendChild(clonedGd); | ||
|
||
function wait() { | ||
var delay = Snapshot.getDelay(clonedGd._fullLayout); | ||
|
||
return new Promise(function(resolve, reject) { | ||
setTimeout(function() { | ||
var svg = Snapshot.toSVG(clonedGd); | ||
|
||
var canvasContainer = window.document.createElement('div'); | ||
var canvas = window.document.createElement('canvas'); | ||
|
||
// window.document.body.appendChild(canvasContainer); | ||
canvasContainer.appendChild(canvas); | ||
|
||
canvasContainer.id = Plotly.Lib.randstr(); | ||
canvas.id = Plotly.Lib.randstr(); | ||
|
||
Snapshot.svgToImg({ | ||
format: opts.format, | ||
width: clonedGd._fullLayout.width, | ||
height: clonedGd._fullLayout.height, | ||
canvas: canvas, | ||
svg: svg, | ||
// ask svgToImg to return a Promise | ||
// rather than EventEmitter | ||
// leave EventEmitter for backward | ||
// compatibility | ||
promise: true | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nicely done. |
||
}).then(function(url) { | ||
if(clonedGd) clonedGd.remove(); | ||
resolve(url); | ||
}).catch(function(err) { | ||
reject(err); | ||
}); | ||
}, delay); | ||
}); | ||
} | ||
|
||
var redrawFunc = Snapshot.getRedrawFunc(clonedGd); | ||
|
||
Plotly.plot(clonedGd, clone.data, clone.layout, clone.config) | ||
// TODO: the following is Plotly.Plots.redrawText but without the waiting. | ||
// we shouldn't need to do this, but in *occasional* cases we do. Figure | ||
// out why and take it out. | ||
|
||
// not sure the above TODO makes sense anymore since | ||
// we have converted to promises | ||
.then(redrawFunc) | ||
.then(wait) | ||
.then(function(url) { resolve(url); }) | ||
.catch(function(err) { | ||
reject(err); | ||
}); | ||
}); | ||
|
||
return promise; | ||
} | ||
|
||
module.exports = toImage; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
/** | ||
* Copyright 2012-2016, Plotly, Inc. | ||
* All rights reserved. | ||
* | ||
* This source code is licensed under the MIT license found in the | ||
* LICENSE file in the root directory of this source tree. | ||
*/ | ||
|
||
|
||
'use strict'; | ||
|
||
var toImage = require('../plot_api/to_image'); | ||
|
||
/** | ||
* @param {object} gd figure Object | ||
* @param {object} opts option object | ||
* @param opts.format 'jpeg' | 'png' | 'webp' | 'svg' | ||
* @param opts.width width of snapshot in px | ||
* @param opts.height height of snapshot in px | ||
* @param opts.filename name of file excluding extension | ||
*/ | ||
function downloadImage(gd, opts) { | ||
|
||
// check for undefined opts | ||
opts = opts || {}; | ||
|
||
// default to png | ||
opts.format = opts.format || 'png'; | ||
|
||
return new Promise(function(resolve,reject){ | ||
if(gd._snapshotInProgress){ | ||
reject(new Error('Snapshotting is unavailable in Internet Explorer. ' + | ||
'Consider exporting your images using the Plotly Cloud')); | ||
} | ||
|
||
gd._snapshotInProgress = true; | ||
var promise = toImage(gd, opts); | ||
|
||
var filename = opts.filename || gd.fn || 'newplot'; | ||
filename += '.' + opts.format; | ||
|
||
promise.then(function(result) { | ||
gd._snapshotInProgress = false; | ||
|
||
var downloadLink = document.createElement('a'); | ||
downloadLink.href = result; | ||
downloadLink.download = filename; // only supported by FF and Chrome | ||
|
||
document.body.appendChild(downloadLink); | ||
downloadLink.click(); | ||
document.body.removeChild(downloadLink); | ||
resolve(); | ||
}) | ||
.catch(function(err) { | ||
gd._snapshotInProgress = false; | ||
console.error(err); | ||
reject(err); | ||
}); | ||
}); | ||
}; | ||
|
||
module.exports = downloadImage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice 👍