From 2889b0d14770d2e3a48f37d2fadafb1bb68e7355 Mon Sep 17 00:00:00 2001 From: MinhHNguyen Date: Mon, 21 Aug 2017 16:35:19 -0700 Subject: [PATCH] Fix: Inconsistent natural image dimensions in IE (#324) * Fix: Inconsistent natural image dimensions in IE * Update: use promise in ImageBaseViewer test * Update: make setOriginalImageSize non-static --- src/lib/viewers/image/ImageBaseViewer.js | 37 ++++++++++++++++++ src/lib/viewers/image/ImageViewer.js | 8 ++-- .../image/__tests__/ImageBaseViewer-test.js | 38 +++++++++++++++++++ 3 files changed, 79 insertions(+), 4 deletions(-) diff --git a/src/lib/viewers/image/ImageBaseViewer.js b/src/lib/viewers/image/ImageBaseViewer.js index 5f764d49d..95f9d96bc 100644 --- a/src/lib/viewers/image/ImageBaseViewer.js +++ b/src/lib/viewers/image/ImageBaseViewer.js @@ -43,6 +43,7 @@ class ImageBaseViewer extends BaseViewer { return; } + this.setOriginalImageSize(this.imageEl); this.loadUI(); this.zoom(); @@ -167,6 +168,42 @@ class ImageBaseViewer extends BaseViewer { this.bindControlListeners(); } + /** + * Sets the original image width and height on the img element. Can be removed when + * naturalHeight and naturalWidth attributes work correctly in IE 11. + * + * @private + * @param {Image} imageEl - The image to set the original size attributes on + * @return {Promise} A promise that is resolved if the original image dimensions were set. + */ + setOriginalImageSize(imageEl) { + const image = imageEl; + const promise = new Promise((resolve, reject) => { + // Do not bother loading a new image when the natural size attributes exist + if (imageEl.naturalWidth > 0 && imageEl.naturalHeight > 0) { + image.originalWidth = imageEl.naturalWidth; + image.originalHeight = imageEl.naturalHeight; + resolve(); + } else { + const originalImg = new Image(); + image.originalWidth = 1; + image.originalHeight = 1; + + originalImg.error = () => { + reject(); + }; + originalImg.onload = () => { + image.originalWidth = originalImg.width || 1; + image.originalHeight = originalImg.height || 1; + resolve(); + }; + originalImg.src = imageEl.src; + } + }); + + return promise; + } + //-------------------------------------------------------------------------- // Event Listeners //-------------------------------------------------------------------------- diff --git a/src/lib/viewers/image/ImageViewer.js b/src/lib/viewers/image/ImageViewer.js index 43a091d16..e4107e91d 100644 --- a/src/lib/viewers/image/ImageViewer.js +++ b/src/lib/viewers/image/ImageViewer.js @@ -183,10 +183,10 @@ class ImageViewer extends ImageBaseViewer { // If the image is smaller than the new viewport, zoom up to a // max of the original file size } else if (modifyWidthInsteadOfHeight) { - const originalWidth = this.isRotated() ? this.imageEl.naturalHeight : this.imageEl.naturalWidth; + const originalWidth = this.isRotated() ? this.imageEl.originalHeight : this.imageEl.originalWidth; newWidth = Math.min(viewport.width, originalWidth); } else { - const originalHeight = this.isRotated() ? this.imageEl.naturalWidth : this.imageEl.naturalHeight; + const originalHeight = this.isRotated() ? this.imageEl.originalWidth : this.imageEl.originalHeight; newHeight = Math.min(viewport.height, originalHeight); } } @@ -231,7 +231,7 @@ class ImageViewer extends ImageBaseViewer { * @return {void} */ setScale(width, height) { - this.scale = width ? width / this.imageEl.naturalWidth : height / this.imageEl.naturalHeight; + this.scale = width ? width / this.imageEl.originalWidth : height / this.imageEl.originalHeight; this.rotationAngle = this.currentRotationAngle % 3600 % 360; this.emit('scale', { scale: this.scale, @@ -390,7 +390,7 @@ class ImageViewer extends ImageBaseViewer { handleOrientationChange() { this.adjustImageZoomPadding(); - this.scale = this.imageEl.clientWidth / this.imageEl.naturalWidth; + this.scale = this.imageEl.clientWidth / this.imageEl.originalWidth; this.rotationAngle = this.currentRotationAngle % 3600 % 360; this.emit('scale', { scale: this.scale, diff --git a/src/lib/viewers/image/__tests__/ImageBaseViewer-test.js b/src/lib/viewers/image/__tests__/ImageBaseViewer-test.js index 97c4cd08b..98aa59848 100644 --- a/src/lib/viewers/image/__tests__/ImageBaseViewer-test.js +++ b/src/lib/viewers/image/__tests__/ImageBaseViewer-test.js @@ -11,6 +11,8 @@ const CSS_CLASS_PANNABLE = 'pannable'; let stubs = {}; +const imageUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAKQWlDQ1BJQ0MgUHJvZmlsZQAASA2dlndUU9kWh8+9N73QEiIgJfQaegkg0jtIFQRRiUmAUAKGhCZ2RAVGFBEpVmRUwAFHhyJjRRQLg4Ji1wnyEFDGwVFEReXdjGsJ7601896a/cdZ39nnt9fZZ+9917oAUPyCBMJ0WAGANKFYFO7rwVwSE8vE9wIYEAEOWAHA4WZmBEf4RALU/L09mZmoSMaz9u4ugGS72yy/UCZz1v9/kSI3QyQGAApF1TY8fiYX5QKUU7PFGTL/BMr0lSkyhjEyFqEJoqwi48SvbPan5iu7yZiXJuShGlnOGbw0noy7UN6aJeGjjAShXJgl4GejfAdlvVRJmgDl9yjT0/icTAAwFJlfzOcmoWyJMkUUGe6J8gIACJTEObxyDov5OWieAHimZ+SKBIlJYqYR15hp5ejIZvrxs1P5YjErlMNN4Yh4TM/0tAyOMBeAr2+WRQElWW2ZaJHtrRzt7VnW5mj5v9nfHn5T/T3IevtV8Sbsz55BjJ5Z32zsrC+9FgD2JFqbHbO+lVUAtG0GQOXhrE/vIADyBQC03pzzHoZsXpLE4gwnC4vs7GxzAZ9rLivoN/ufgm/Kv4Y595nL7vtWO6YXP4EjSRUzZUXlpqemS0TMzAwOl89k/fcQ/+PAOWnNycMsnJ/AF/GF6FVR6JQJhIlou4U8gViQLmQKhH/V4X8YNicHGX6daxRodV8AfYU5ULhJB8hvPQBDIwMkbj96An3rWxAxCsi+vGitka9zjzJ6/uf6Hwtcim7hTEEiU+b2DI9kciWiLBmj34RswQISkAd0oAo0gS4wAixgDRyAM3AD3iAAhIBIEAOWAy5IAmlABLJBPtgACkEx2AF2g2pwANSBetAEToI2cAZcBFfADXALDIBHQAqGwUswAd6BaQiC8BAVokGqkBakD5lC1hAbWgh5Q0FQOBQDxUOJkBCSQPnQJqgYKoOqoUNQPfQjdBq6CF2D+qAH0CA0Bv0BfYQRmALTYQ3YALaA2bA7HAhHwsvgRHgVnAcXwNvhSrgWPg63whfhG/AALIVfwpMIQMgIA9FGWAgb8URCkFgkAREha5EipAKpRZqQDqQbuY1IkXHkAwaHoWGYGBbGGeOHWYzhYlZh1mJKMNWYY5hWTBfmNmYQM4H5gqVi1bGmWCesP3YJNhGbjS3EVmCPYFuwl7ED2GHsOxwOx8AZ4hxwfrgYXDJuNa4Etw/XjLuA68MN4SbxeLwq3hTvgg/Bc/BifCG+Cn8cfx7fjx/GvyeQCVoEa4IPIZYgJGwkVBAaCOcI/YQRwjRRgahPdCKGEHnEXGIpsY7YQbxJHCZOkxRJhiQXUiQpmbSBVElqIl0mPSa9IZPJOmRHchhZQF5PriSfIF8lD5I/UJQoJhRPShxFQtlOOUq5QHlAeUOlUg2obtRYqpi6nVpPvUR9Sn0vR5Mzl/OX48mtk6uRa5Xrl3slT5TXl3eXXy6fJ18hf0r+pvy4AlHBQMFTgaOwVqFG4bTCPYVJRZqilWKIYppiiWKD4jXFUSW8koGStxJPqUDpsNIlpSEaQtOledK4tE20Otpl2jAdRzek+9OT6cX0H+i99AllJWVb5SjlHOUa5bPKUgbCMGD4M1IZpYyTjLuMj/M05rnP48/bNq9pXv+8KZX5Km4qfJUilWaVAZWPqkxVb9UU1Z2qbapP1DBqJmphatlq+9Uuq43Pp893ns+dXzT/5PyH6rC6iXq4+mr1w+o96pMamhq+GhkaVRqXNMY1GZpumsma5ZrnNMe0aFoLtQRa5VrntV4wlZnuzFRmJbOLOaGtru2nLdE+pN2rPa1jqLNYZ6NOs84TXZIuWzdBt1y3U3dCT0svWC9fr1HvoT5Rn62fpL9Hv1t/ysDQINpgi0GbwaihiqG/YZ5ho+FjI6qRq9Eqo1qjO8Y4Y7ZxivE+41smsImdSZJJjclNU9jU3lRgus+0zwxr5mgmNKs1u8eisNxZWaxG1qA5wzzIfKN5m/krCz2LWIudFt0WXyztLFMt6ywfWSlZBVhttOqw+sPaxJprXWN9x4Zq42Ozzqbd5rWtqS3fdr/tfTuaXbDdFrtOu8/2DvYi+yb7MQc9h3iHvQ732HR2KLuEfdUR6+jhuM7xjOMHJ3snsdNJp9+dWc4pzg3OowsMF/AX1C0YctFx4bgccpEuZC6MX3hwodRV25XjWuv6zE3Xjed2xG3E3dg92f24+ysPSw+RR4vHlKeT5xrPC16Il69XkVevt5L3Yu9q76c+Oj6JPo0+E752vqt9L/hh/QL9dvrd89fw5/rX+08EOASsCegKpARGBFYHPgsyCRIFdQTDwQHBu4IfL9JfJFzUFgJC/EN2hTwJNQxdFfpzGC4sNKwm7Hm4VXh+eHcELWJFREPEu0iPyNLIR4uNFksWd0bJR8VF1UdNRXtFl0VLl1gsWbPkRoxajCCmPRYfGxV7JHZyqffS3UuH4+ziCuPuLjNclrPs2nK15anLz66QX8FZcSoeGx8d3xD/iRPCqeVMrvRfuXflBNeTu4f7kufGK+eN8V34ZfyRBJeEsoTRRJfEXYljSa5JFUnjAk9BteB1sl/ygeSplJCUoykzqdGpzWmEtPi000IlYYqwK10zPSe9L8M0ozBDuspp1e5VE6JA0ZFMKHNZZruYjv5M9UiMJJslg1kLs2qy3mdHZZ/KUcwR5vTkmuRuyx3J88n7fjVmNXd1Z752/ob8wTXuaw6thdauXNu5Tnddwbrh9b7rj20gbUjZ8MtGy41lG99uit7UUaBRsL5gaLPv5sZCuUJR4b0tzlsObMVsFWzt3WazrWrblyJe0fViy+KK4k8l3JLr31l9V/ndzPaE7b2l9qX7d+B2CHfc3em681iZYlle2dCu4F2t5czyovK3u1fsvlZhW3FgD2mPZI+0MqiyvUqvakfVp+qk6oEaj5rmvep7t+2d2sfb17/fbX/TAY0DxQc+HhQcvH/I91BrrUFtxWHc4azDz+ui6rq/Z39ff0TtSPGRz0eFR6XHwo911TvU1zeoN5Q2wo2SxrHjccdv/eD1Q3sTq+lQM6O5+AQ4ITnx4sf4H++eDDzZeYp9qukn/Z/2ttBailqh1tzWibakNml7THvf6YDTnR3OHS0/m/989Iz2mZqzymdLz5HOFZybOZ93fvJCxoXxi4kXhzpXdD66tOTSna6wrt7LgZevXvG5cqnbvfv8VZerZ645XTt9nX297Yb9jdYeu56WX+x+aem172296XCz/ZbjrY6+BX3n+l37L972un3ljv+dGwOLBvruLr57/17cPel93v3RB6kPXj/Mejj9aP1j7OOiJwpPKp6qP6391fjXZqm99Oyg12DPs4hnj4a4Qy//lfmvT8MFz6nPK0a0RupHrUfPjPmM3Xqx9MXwy4yX0+OFvyn+tveV0auffnf7vWdiycTwa9HrmT9K3qi+OfrW9m3nZOjk03dp76anit6rvj/2gf2h+2P0x5Hp7E/4T5WfjT93fAn88ngmbWbm3/eE8/syOll+AAAA4klEQVQoFWNkQABG7QlvU/7/Z0xmYGTQBgv/Z7jKyPh/7tUC4TlA/n+QGCOI0Ox/LcnIwLKEkZHBCcRHB///M+z7z/An5nqh6HMmoCRQHapiN1VWhlZXLrg+kEEgNSC1LCBnABkoJvOyMTKIcoMtR9EEUsuo1f/uBCMjozlcBg/j////J5ngHkRS6KrCylDvxIkkAmUCAwPkBwygIszE4KDEiiEOEmACBtZVrDLYBIFqmUDhjE0OmxhILSgogB5/vwdXHMA0guLiWqGgC8gPQPafGJAATBKdhkUcSC1yYBOVNAAVx0qxuz8xqgAAAABJRU5ErkJggg=='; + const sandbox = sinon.sandbox.create(); let imageBase; let containerEl; @@ -218,6 +220,39 @@ describe('lib/viewers/image/ImageBaseViewer', () => { }); }); + describe('setOriginalImageSize()', () => { + it('should use the naturalHeight and naturalWidth when available', (done) => { + const imageEl = { + naturalWidth: 100, + naturalHeight: 100 + }; + + const promise = imageBase.setOriginalImageSize(imageEl); + promise.should.be.fulfilled.then(() => { + expect(imageEl.originalWidth).to.equal(imageEl.naturalWidth); + expect(imageEl.originalHeight).to.equal(imageEl.naturalHeight); + done(); + }); + }); + + it('should work when naturalHeight and naturalWidth are undefined', (done) => { + const imageEl = { + naturalWidth: undefined, + naturalHeight: undefined, + src: imageUrl + }; + + const imageUrlWidth = 12; + const imageUrlHeight = 12; + const promise = imageBase.setOriginalImageSize(imageEl); + promise.should.be.fulfilled.then(() => { + expect(imageEl.originalWidth).to.equal(imageUrlWidth); + expect(imageEl.originalHeight).to.equal(imageUrlHeight); + done(); + }); + }); + }); + describe('bindControlListeners()', () => { it('should add the correct controls', () => { imageBase.controls = { @@ -500,6 +535,7 @@ describe('lib/viewers/image/ImageBaseViewer', () => { stubs.emit = sandbox.stub(imageBase, 'emit'); stubs.zoom = sandbox.stub(imageBase, 'zoom'); stubs.loadUI = sandbox.stub(imageBase, 'loadUI'); + stubs.setOriginalImageSize = sandbox.stub(imageBase, 'setOriginalImageSize'); }); it('should do nothing if already destroyed', () => { @@ -509,6 +545,7 @@ describe('lib/viewers/image/ImageBaseViewer', () => { expect(imageBase.loaded).to.be.false; expect(stubs.emit).to.not.have.been.called; expect(stubs.zoom).to.not.have.been.called; + expect(stubs.setOriginalImageSize).to.not.have.been.called; expect(stubs.loadUI).to.not.have.been.called; }); @@ -518,6 +555,7 @@ describe('lib/viewers/image/ImageBaseViewer', () => { expect(imageBase.loaded).to.be.true; expect(stubs.emit).to.have.been.calledWith('load'); expect(stubs.emit).to.have.been.calledWith('load'); + expect(stubs.setOriginalImageSize).to.have.been.called; expect(stubs.zoom).to.have.been.called; expect(stubs.loadUI).to.have.been.called; });