Skip to content

Commit

Permalink
Fix: Inconsistent natural image dimensions in IE (#324)
Browse files Browse the repository at this point in the history
* Fix: Inconsistent natural image dimensions in IE
* Update: use promise in ImageBaseViewer test
* Update: make setOriginalImageSize non-static
  • Loading branch information
Minh-Ng committed Aug 21, 2017
1 parent e27c18a commit 2889b0d
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 4 deletions.
37 changes: 37 additions & 0 deletions src/lib/viewers/image/ImageBaseViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ class ImageBaseViewer extends BaseViewer {
return;
}

this.setOriginalImageSize(this.imageEl);
this.loadUI();
this.zoom();

Expand Down Expand Up @@ -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
//--------------------------------------------------------------------------
Expand Down
8 changes: 4 additions & 4 deletions src/lib/viewers/image/ImageViewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
38 changes: 38 additions & 0 deletions src/lib/viewers/image/__tests__/ImageBaseViewer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const CSS_CLASS_PANNABLE = 'pannable';

let stubs = {};

const imageUrl = 'data:image/png;base64,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';

const sandbox = sinon.sandbox.create();
let imageBase;
let containerEl;
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -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', () => {
Expand All @@ -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;
});

Expand All @@ -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;
});
Expand Down

0 comments on commit 2889b0d

Please sign in to comment.