Skip to content

Commit

Permalink
Merge pull request #7277 from plotly/alex/image-ident
Browse files Browse the repository at this point in the history
Set key function for layout images
  • Loading branch information
alexcjohnson authored Nov 21, 2024
2 parents 0d32211 + 148c693 commit ca6a886
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 12 deletions.
1 change: 1 addition & 0 deletions draftlogs/7277_fix.md
Original file line number Diff line number Diff line change
@@ -0,0 +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)]
15 changes: 12 additions & 3 deletions src/components/images/draw.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,16 @@ module.exports = function draw(gd) {
);
}

function imgDataFunc(d) {
return [d.xref, d.x, d.sizex, d.yref, d.y, d.sizey].join('_');
}

function imgSort(a, b) { return a._index - b._index; }

var imagesBelow = fullLayout._imageLowerLayer.selectAll('image')
.data(imageDataBelow);
.data(imageDataBelow, imgDataFunc);
var imagesAbove = fullLayout._imageUpperLayer.selectAll('image')
.data(imageDataAbove);
.data(imageDataAbove, imgDataFunc);

imagesBelow.enter().append('image');
imagesAbove.enter().append('image');
Expand All @@ -222,6 +228,8 @@ module.exports = function draw(gd) {
setImage.bind(this)(d);
applyAttributes.bind(this)(d);
});
imagesBelow.sort(imgSort);
imagesAbove.sort(imgSort);

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

imagesOnSubplot.enter().append('image');
imagesOnSubplot.exit().remove();
Expand All @@ -243,5 +251,6 @@ module.exports = function draw(gd) {
setImage.bind(this)(d);
applyAttributes.bind(this)(d);
});
imagesOnSubplot.sort(imgSort);
}
};
1 change: 1 addition & 0 deletions tasks/test_syntax.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ function assertFileNames() {
base === 'SECURITY.md' ||
base === 'BUILDING.md' ||
base === 'CUSTOM_BUNDLE.md' ||
base === 'CITATION.cff' ||
file.indexOf('mathjax') !== -1
) return;

Expand Down
34 changes: 26 additions & 8 deletions test/jasmine/tests/layout_images_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ describe('Layout images', function() {
var data = [{ x: [1, 2, 3], y: [1, 2, 3] }];
var layout = { width: 500, height: 400 };

var imgEls;

function makeImage(source, x, y) {
return {
source: source,
Expand All @@ -451,8 +453,21 @@ describe('Layout images', function() {
};
}

function assertImages(cnt) {
function getImageEls() {
return Array.from(gd.querySelectorAll('image'));
}

function assertImages(cnt, expectedEls, msg) {
expect(d3SelectAll('image').size()).toEqual(cnt);

if (expectedEls) {
var foundImageEls = getImageEls();
expectedEls.forEach(function(expi, i) {
if (expi) {
expect(foundImageEls[i]).toBe(expi, msg + ': ' + i);
}
});
}
}

Plotly.newPlot(gd, data, layout).then(function() {
Expand All @@ -463,42 +478,45 @@ describe('Layout images', function() {
})
.then(function() {
assertImages(1);
imgEls = getImageEls();

return Plotly.relayout(gd, 'images[1]', makeImage(pythonLogo, 0.9, 0.9));
})
.then(function() {
assertImages(2);
assertImages(2, [imgEls[0], null], 'add second image');
imgEls = getImageEls();

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

return Plotly.relayout(gd, 'images[1].visible', false);
})
.then(function() {
assertImages(2);
expect(gd.layout.images.length).toEqual(3);
assertImages(2, [imgEls[0], imgEls[2]], 'hide second');

return Plotly.relayout(gd, 'images[1].visible', true);
})
.then(function() {
assertImages(3);
assertImages(3, [imgEls[0], null, imgEls[2]], 'reshow second');
expect(gd.layout.images.length).toEqual(3);
imgEls = getImageEls();

// delete not from the end of the array
return Plotly.relayout(gd, 'images[0]', null);
})
.then(function() {
assertImages(2);
assertImages(2, [imgEls[1], imgEls[2]], 'delete first');
expect(gd.layout.images.length).toEqual(2);

return Plotly.relayout(gd, 'images[1]', null);
})
.then(function() {
assertImages(1);
assertImages(1, [imgEls[1]], 'delete last');
expect(gd.layout.images.length).toEqual(1);

return Plotly.relayout(gd, 'images[0]', null);
Expand Down
2 changes: 1 addition & 1 deletion test/jasmine/tests/transition_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ describe('Plots.transition', function() {
var pythonLogo = 'https://images.plot.ly/language-icons/api-home/python-logo.png';

function imageel() {
return gd._fullLayout._imageUpperLayer.select('image').node();
return gd._fullLayout._imageUpperLayer.node().querySelector('image');
}
function imagesrc() {
return imageel().getAttribute('href');
Expand Down

0 comments on commit ca6a886

Please sign in to comment.