Skip to content

Commit ca6a886

Browse files
authored
Merge pull request #7277 from plotly/alex/image-ident
Set key function for layout images
2 parents 0d32211 + 148c693 commit ca6a886

File tree

5 files changed

+41
-12
lines changed

5 files changed

+41
-12
lines changed

draftlogs/7277_fix.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Maintain layout images element identity based on coordinates, for smoother updates when you add or remove images early in the list. [[#7277](https://github.com/plotly/plotly.js/pull/7277)]

src/components/images/draw.js

+12-3
Original file line numberDiff line numberDiff line change
@@ -203,10 +203,16 @@ module.exports = function draw(gd) {
203203
);
204204
}
205205

206+
function imgDataFunc(d) {
207+
return [d.xref, d.x, d.sizex, d.yref, d.y, d.sizey].join('_');
208+
}
209+
210+
function imgSort(a, b) { return a._index - b._index; }
211+
206212
var imagesBelow = fullLayout._imageLowerLayer.selectAll('image')
207-
.data(imageDataBelow);
213+
.data(imageDataBelow, imgDataFunc);
208214
var imagesAbove = fullLayout._imageUpperLayer.selectAll('image')
209-
.data(imageDataAbove);
215+
.data(imageDataAbove, imgDataFunc);
210216

211217
imagesBelow.enter().append('image');
212218
imagesAbove.enter().append('image');
@@ -222,6 +228,8 @@ module.exports = function draw(gd) {
222228
setImage.bind(this)(d);
223229
applyAttributes.bind(this)(d);
224230
});
231+
imagesBelow.sort(imgSort);
232+
imagesAbove.sort(imgSort);
225233

226234
var allSubplots = Object.keys(fullLayout._plots);
227235
for(i = 0; i < allSubplots.length; i++) {
@@ -234,7 +242,7 @@ module.exports = function draw(gd) {
234242
var imagesOnSubplot = subplotObj.imagelayer.selectAll('image')
235243
// even if there are no images on this subplot, we need to run
236244
// enter and exit in case there were previously
237-
.data(imageDataSubplot[subplot] || []);
245+
.data(imageDataSubplot[subplot] || [], imgDataFunc);
238246

239247
imagesOnSubplot.enter().append('image');
240248
imagesOnSubplot.exit().remove();
@@ -243,5 +251,6 @@ module.exports = function draw(gd) {
243251
setImage.bind(this)(d);
244252
applyAttributes.bind(this)(d);
245253
});
254+
imagesOnSubplot.sort(imgSort);
246255
}
247256
};

tasks/test_syntax.js

+1
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ function assertFileNames() {
199199
base === 'SECURITY.md' ||
200200
base === 'BUILDING.md' ||
201201
base === 'CUSTOM_BUNDLE.md' ||
202+
base === 'CITATION.cff' ||
202203
file.indexOf('mathjax') !== -1
203204
) return;
204205

test/jasmine/tests/layout_images_test.js

+26-8
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,8 @@ describe('Layout images', function() {
441441
var data = [{ x: [1, 2, 3], y: [1, 2, 3] }];
442442
var layout = { width: 500, height: 400 };
443443

444+
var imgEls;
445+
444446
function makeImage(source, x, y) {
445447
return {
446448
source: source,
@@ -451,8 +453,21 @@ describe('Layout images', function() {
451453
};
452454
}
453455

454-
function assertImages(cnt) {
456+
function getImageEls() {
457+
return Array.from(gd.querySelectorAll('image'));
458+
}
459+
460+
function assertImages(cnt, expectedEls, msg) {
455461
expect(d3SelectAll('image').size()).toEqual(cnt);
462+
463+
if (expectedEls) {
464+
var foundImageEls = getImageEls();
465+
expectedEls.forEach(function(expi, i) {
466+
if (expi) {
467+
expect(foundImageEls[i]).toBe(expi, msg + ': ' + i);
468+
}
469+
});
470+
}
456471
}
457472

458473
Plotly.newPlot(gd, data, layout).then(function() {
@@ -463,42 +478,45 @@ describe('Layout images', function() {
463478
})
464479
.then(function() {
465480
assertImages(1);
481+
imgEls = getImageEls();
466482

467483
return Plotly.relayout(gd, 'images[1]', makeImage(pythonLogo, 0.9, 0.9));
468484
})
469485
.then(function() {
470-
assertImages(2);
486+
assertImages(2, [imgEls[0], null], 'add second image');
487+
imgEls = getImageEls();
471488

472489
// insert an image not at the end of the array
473490
return Plotly.relayout(gd, 'images[0]', makeImage(pythonLogo, 0.2, 0.5));
474491
})
475492
.then(function() {
476-
assertImages(3);
493+
assertImages(3, [null, imgEls[0], imgEls[1]], 'add third at the start');
477494
expect(gd.layout.images.length).toEqual(3);
495+
imgEls = getImageEls();
478496

479497
return Plotly.relayout(gd, 'images[1].visible', false);
480498
})
481499
.then(function() {
482-
assertImages(2);
483-
expect(gd.layout.images.length).toEqual(3);
500+
assertImages(2, [imgEls[0], imgEls[2]], 'hide second');
484501

485502
return Plotly.relayout(gd, 'images[1].visible', true);
486503
})
487504
.then(function() {
488-
assertImages(3);
505+
assertImages(3, [imgEls[0], null, imgEls[2]], 'reshow second');
489506
expect(gd.layout.images.length).toEqual(3);
507+
imgEls = getImageEls();
490508

491509
// delete not from the end of the array
492510
return Plotly.relayout(gd, 'images[0]', null);
493511
})
494512
.then(function() {
495-
assertImages(2);
513+
assertImages(2, [imgEls[1], imgEls[2]], 'delete first');
496514
expect(gd.layout.images.length).toEqual(2);
497515

498516
return Plotly.relayout(gd, 'images[1]', null);
499517
})
500518
.then(function() {
501-
assertImages(1);
519+
assertImages(1, [imgEls[1]], 'delete last');
502520
expect(gd.layout.images.length).toEqual(1);
503521

504522
return Plotly.relayout(gd, 'images[0]', null);

test/jasmine/tests/transition_test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ describe('Plots.transition', function() {
106106
var pythonLogo = 'https://images.plot.ly/language-icons/api-home/python-logo.png';
107107

108108
function imageel() {
109-
return gd._fullLayout._imageUpperLayer.select('image').node();
109+
return gd._fullLayout._imageUpperLayer.node().querySelector('image');
110110
}
111111
function imagesrc() {
112112
return imageel().getAttribute('href');

0 commit comments

Comments
 (0)