-
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 15 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 |
---|---|---|
@@ -0,0 +1,95 @@ | ||
/** | ||
* 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'); | ||
|
||
/** | ||
* @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 |
||
*/ | ||
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'; | ||
|
||
// 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 |
---|---|---|
|
@@ -79,6 +79,12 @@ exports.register(require('./traces/scatter')); | |
// plot api | ||
require('./plot_api/plot_api'); | ||
exports.PlotSchema = require('./plot_api/plot_schema'); | ||
// toImage to attach directly to Plotly | ||
// to allow us to get rid of Snapshot below | ||
// for version 2.0.0 | ||
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. no need for this line. Your patch in 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. If read correctly, then if I eliminate 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. Never mind, I can just do the |
||
|
||
|
||
// imaging routines | ||
exports.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. 🐄 no need for this blank line. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* 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'); | ||
var Lib = require('../lib'); | ||
|
||
/** | ||
* @param {object} gd figure Object | ||
* @param {object} opts option object | ||
* @param opts.format 'jpeg' | 'png' | 'webp' | 'svg' | ||
*/ | ||
function downloadImage(gd, opts) { | ||
|
||
// check for undefined opts | ||
opts = opts || {}; | ||
|
||
// default to png | ||
opts.format = opts.format || 'png'; | ||
|
||
if(Lib.isIE()) { | ||
Lib.notifier('Snapshotting is unavailable in Internet Explorer. ' + | ||
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. We make this an option. I think the mode bar |
||
'Consider exporting your images using the Plotly Cloud', 'long'); | ||
return; | ||
} | ||
|
||
if(gd._snapshotInProgress) { | ||
Lib.notifier('Snapshotting is still in progress - please hold', 'long'); | ||
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. Similarly, |
||
return; | ||
} | ||
|
||
gd._snapshotInProgress = true; | ||
Lib.notifier('Taking snapshot - this may take a few seconds', 'long'); | ||
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. this notifier should be omitted in |
||
|
||
var promise = toImage(gd, opts); | ||
|
||
var filename = gd.fn || 'newplot'; | ||
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. we should add an Plotly.plot(gd, data, layout).then(function(gd) {
return Plotly.downloadImage(gd, { filename: 'my-graph', format: 'png' });
}); |
||
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); | ||
}) | ||
.catch(function(err) { | ||
gd._snapshotInProgress = false; | ||
|
||
Lib.notifier('Sorry there was a problem downloading your ' + opts.format, 'long'); | ||
console.error(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.
function(gd) {