Skip to content

Commit 37e80cb

Browse files
authored
Merge pull request #4105 from plotly/datauri_images
Process data uri images synchronously
2 parents 060369a + 4a1afbd commit 37e80cb

File tree

2 files changed

+63
-29
lines changed

2 files changed

+63
-29
lines changed

Diff for: src/components/images/draw.js

+34-29
Original file line numberDiff line numberDiff line change
@@ -74,49 +74,54 @@ module.exports = function draw(gd) {
7474
function setImage(d) {
7575
var thisImage = d3.select(this);
7676

77-
if(this.img && this.img.src === d.source) {
77+
if(this._imgSrc === d.source) {
7878
return;
7979
}
8080

8181
thisImage.attr('xmlns', xmlnsNamespaces.svg);
8282

83-
var imagePromise = new Promise(function(resolve) {
84-
var img = new Image();
85-
this.img = img;
83+
if(d.source && d.source.slice(0, 5) === 'data:') {
84+
thisImage.attr('xlink:href', d.source);
85+
this._imgSrc = d.source;
86+
} else {
87+
var imagePromise = new Promise(function(resolve) {
88+
var img = new Image();
89+
this.img = img;
8690

87-
// If not set, a `tainted canvas` error is thrown
88-
img.setAttribute('crossOrigin', 'anonymous');
89-
img.onerror = errorHandler;
90-
img.onload = function() {
91-
var canvas = document.createElement('canvas');
92-
canvas.width = this.width;
93-
canvas.height = this.height;
91+
// If not set, a `tainted canvas` error is thrown
92+
img.setAttribute('crossOrigin', 'anonymous');
93+
img.onerror = errorHandler;
94+
img.onload = function() {
95+
var canvas = document.createElement('canvas');
96+
canvas.width = this.width;
97+
canvas.height = this.height;
9498

95-
var ctx = canvas.getContext('2d');
96-
ctx.drawImage(this, 0, 0);
99+
var ctx = canvas.getContext('2d');
100+
ctx.drawImage(this, 0, 0);
97101

98-
var dataURL = canvas.toDataURL('image/png');
102+
var dataURL = canvas.toDataURL('image/png');
99103

100-
thisImage.attr('xlink:href', dataURL);
104+
thisImage.attr('xlink:href', dataURL);
101105

102-
// resolve promise in onload handler instead of on 'load' to support IE11
103-
// see https://github.com/plotly/plotly.js/issues/1685
104-
// for more details
105-
resolve();
106-
};
106+
// resolve promise in onload handler instead of on 'load' to support IE11
107+
// see https://github.com/plotly/plotly.js/issues/1685
108+
// for more details
109+
resolve();
110+
};
107111

112+
thisImage.on('error', errorHandler);
108113

109-
thisImage.on('error', errorHandler);
114+
img.src = d.source;
115+
this._imgSrc = d.source;
110116

111-
img.src = d.source;
112-
113-
function errorHandler() {
114-
thisImage.remove();
115-
resolve();
116-
}
117-
}.bind(this));
117+
function errorHandler() {
118+
thisImage.remove();
119+
resolve();
120+
}
121+
}.bind(this));
118122

119-
gd._promises.push(imagePromise);
123+
gd._promises.push(imagePromise);
124+
}
120125
}
121126

122127
function applyAttributes(d) {

Diff for: test/jasmine/tests/layout_images_test.js

+29
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ var mouseEvent = require('../assets/mouse_event');
1212
var jsLogo = 'https://images.plot.ly/language-icons/api-home/js-logo.png';
1313
var pythonLogo = 'https://images.plot.ly/language-icons/api-home/python-logo.png';
1414

15+
// Single red pixel png generated with http://png-pixel.com/
16+
var dataUriImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfF' +
17+
'cSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg==';
18+
1519
describe('Layout images', function() {
1620
describe('supplyLayoutDefaults', function() {
1721
var layoutIn,
@@ -290,6 +294,31 @@ describe('Layout images', function() {
290294

291295
afterEach(destroyGraphDiv);
292296

297+
it('should only create canvas if url image', function(done) {
298+
var originalCreateElement = document.createElement;
299+
var newCanvasElement;
300+
spyOn(document, 'createElement').and.callFake(function(elementType) {
301+
var element = originalCreateElement.call(document, elementType);
302+
if(elementType === 'canvas') {
303+
newCanvasElement = element;
304+
spyOn(newCanvasElement, 'toDataURL');
305+
}
306+
return element;
307+
});
308+
309+
Plotly.relayout(gd, 'images[0].source', dataUriImage)
310+
.then(function() {
311+
expect(newCanvasElement).toBeUndefined();
312+
})
313+
.then(function() { return Plotly.relayout(gd, 'images[0].source', jsLogo); })
314+
.then(function() {
315+
expect(newCanvasElement).toBeDefined();
316+
expect(newCanvasElement.toDataURL).toHaveBeenCalledTimes(1);
317+
})
318+
.catch(failTest)
319+
.then(done);
320+
});
321+
293322
it('should update the image if changed', function(done) {
294323
var img = Plotly.d3.select('image');
295324
var url = img.attr('xlink:href');

0 commit comments

Comments
 (0)