Skip to content
This repository was archived by the owner on Jan 19, 2023. It is now read-only.

Commit 8fde526

Browse files
committed
Switch from positioning with left and top to transform and translate
1 parent eb62691 commit 8fde526

File tree

2 files changed

+126
-79
lines changed

2 files changed

+126
-79
lines changed

src/react-image-lightbox.js

Lines changed: 122 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -189,9 +189,6 @@ class ReactImageLightbox extends Component {
189189

190190
// Change zoom level
191191
changeZoom(zoomLevel, clientX, clientY) {
192-
const windowWidth = getWindowWidth();
193-
const windowHeight = getWindowHeight();
194-
195192
// Constrain zoom level to the set bounds
196193
const nextZoomLevel = Math.max(MIN_ZOOM_LEVEL, Math.min(MAX_ZOOM_LEVEL, zoomLevel));
197194

@@ -207,21 +204,31 @@ class ReactImageLightbox extends Component {
207204
});
208205
}
209206

207+
const imageBaseSize = this.getBestImageForType('mainSrc');
208+
if (imageBaseSize === null) {
209+
return;
210+
}
211+
210212
const currentZoomMultiplier = this.getZoomMultiplier();
211213
const nextZoomMultiplier = this.getZoomMultiplier(nextZoomLevel);
212214

213-
// Default to the center of the screen to zoom when no mouse position specified
214-
const percentXInCurrentBox = (typeof clientX !== 'undefined' ? clientX : windowWidth / 2) / windowWidth;
215-
const percentYInCurrentBox = (typeof clientY !== 'undefined' ? clientY : windowHeight / 2) / windowHeight;
215+
// Default to the center of the image to zoom when no mouse position specified
216+
const boxRect = this.getLightboxRect();
217+
const percentXInCurrentBox = typeof clientX !== 'undefined' ?
218+
((clientX - boxRect.left - ((boxRect.width - imageBaseSize.width) / 2)) / imageBaseSize.width) :
219+
0.5;
220+
const percentYInCurrentBox = typeof clientY !== 'undefined' ?
221+
((clientY - boxRect.top - ((boxRect.height - imageBaseSize.height) / 2)) / imageBaseSize.height) :
222+
0.5;
216223

217-
const currentBoxWidth = windowWidth / currentZoomMultiplier;
218-
const currentBoxHeight = windowHeight / currentZoomMultiplier;
224+
const currentImageWidth = imageBaseSize.width * currentZoomMultiplier;
225+
const currentImageHeight = imageBaseSize.height * currentZoomMultiplier;
219226

220-
const nextBoxWidth = windowWidth / nextZoomMultiplier;
221-
const nextBoxHeight = windowHeight / nextZoomMultiplier;
227+
const nextImageWidth = imageBaseSize.width * nextZoomMultiplier;
228+
const nextImageHeight = imageBaseSize.height * nextZoomMultiplier;
222229

223-
const deltaX = (nextBoxWidth - currentBoxWidth) * (percentXInCurrentBox - 0.5);
224-
const deltaY = (nextBoxHeight - currentBoxHeight) * (percentYInCurrentBox - 0.5);
230+
const deltaX = (currentImageWidth - nextImageWidth) * (percentXInCurrentBox - 0.5);
231+
const deltaY = (currentImageHeight - nextImageHeight) * (percentYInCurrentBox - 0.5);
225232

226233
let nextOffsetX = this.state.offsetX - deltaX;
227234
let nextOffsetY = this.state.offsetY - deltaY;
@@ -287,10 +294,9 @@ class ReactImageLightbox extends Component {
287294

288295
// Get sizing for when an image is larger than the window
289296
getFitSizes(width, height, stretch) {
290-
const windowHeight = getWindowHeight();
291-
const windowWidth = getWindowWidth();
292-
let maxHeight = windowHeight - (this.props.imagePadding * 2);
293-
let maxWidth = windowWidth - (this.props.imagePadding * 2);
297+
const boxSize = this.getLightboxRect();
298+
let maxHeight = boxSize.height - (this.props.imagePadding * 2);
299+
let maxWidth = boxSize.width - (this.props.imagePadding * 2);
294300

295301
if (!stretch) {
296302
maxHeight = Math.min(maxHeight, height);
@@ -300,16 +306,17 @@ class ReactImageLightbox extends Component {
300306
const maxRatio = maxWidth / maxHeight;
301307
const srcRatio = width / height;
302308

303-
const fitSizes = {};
304309
if (maxRatio > srcRatio) { // height is the constraining dimension of the photo
305-
fitSizes.width = width * maxHeight / height;
306-
fitSizes.height = maxHeight;
307-
} else {
308-
fitSizes.width = maxWidth;
309-
fitSizes.height = height * maxWidth / width;
310+
return {
311+
width: width * maxHeight / height,
312+
height: maxHeight,
313+
};
310314
}
311315

312-
return fitSizes;
316+
return {
317+
width: maxWidth,
318+
height: height * maxWidth / width,
319+
};
313320
}
314321

315322
getMaxOffsets(zoomLevel = this.state.zoomLevel) {
@@ -318,24 +325,23 @@ class ReactImageLightbox extends Component {
318325
return { maxX: 0, minX: 0, maxY: 0, minY: 0 };
319326
}
320327

321-
const windowWidth = getWindowWidth();
322-
const windowHeight = getWindowHeight();
328+
const boxSize = this.getLightboxRect();
323329
const zoomMultiplier = this.getZoomMultiplier(zoomLevel);
324330

325331
let maxX = 0;
326-
if (currentImageInfo.width - (windowWidth / zoomMultiplier) < 0) {
332+
if ((zoomMultiplier * currentImageInfo.width) - boxSize.width < 0) {
327333
// if there is still blank space in the X dimension, don't limit except to the opposite edge
328-
maxX = ((windowWidth / zoomMultiplier) - currentImageInfo.width) / 2;
334+
maxX = (boxSize.width - (zoomMultiplier * currentImageInfo.width)) / 2;
329335
} else {
330-
maxX = (currentImageInfo.width - (windowWidth / zoomMultiplier)) / 2;
336+
maxX = ((zoomMultiplier * currentImageInfo.width) - boxSize.width) / 2;
331337
}
332338

333339
let maxY = 0;
334-
if (currentImageInfo.height - (windowHeight / zoomMultiplier) < 0) {
340+
if ((zoomMultiplier * currentImageInfo.height) - boxSize.height < 0) {
335341
// if there is still blank space in the Y dimension, don't limit except to the opposite edge
336-
maxY = ((windowHeight / zoomMultiplier) - currentImageInfo.height) / 2;
342+
maxY = (boxSize.height - (zoomMultiplier * currentImageInfo.height)) / 2;
337343
} else {
338-
maxY = (currentImageInfo.height - (windowHeight / zoomMultiplier)) / 2;
344+
maxY = ((zoomMultiplier * currentImageInfo.height) - boxSize.height) / 2;
339345
}
340346

341347
return {
@@ -346,16 +352,6 @@ class ReactImageLightbox extends Component {
346352
};
347353
}
348354

349-
getOffsetXFromWindowCenter(x) {
350-
const windowWidth = getWindowWidth();
351-
return (windowWidth / 2) - x;
352-
}
353-
354-
getOffsetYFromWindowCenter(y) {
355-
const windowHeight = getWindowHeight();
356-
return (windowHeight / 2) - y;
357-
}
358-
359355
// Get image src types
360356
getSrcTypes() {
361357
return [
@@ -386,12 +382,34 @@ class ReactImageLightbox extends Component {
386382
];
387383
}
388384

389-
// Get sizing when the image is scaled
385+
/**
386+
* Get sizing when the image is scaled
387+
*/
390388
getZoomMultiplier(zoomLevel = this.state.zoomLevel) {
391389
return Math.pow(ZOOM_RATIO, zoomLevel);
392390
}
393391

394-
// Handle user keyboard actions
392+
/**
393+
* Get the size of the lightbox in pixels
394+
*/
395+
getLightboxRect() {
396+
if (this.outerEl) {
397+
return this.outerEl.getBoundingClientRect();
398+
}
399+
400+
return {
401+
width: getWindowWidth(),
402+
height: getWindowHeight(),
403+
top: 0,
404+
right: 0,
405+
bottom: 0,
406+
left: 0,
407+
};
408+
}
409+
410+
/**
411+
* Handle user keyboard actions
412+
*/
395413
handleKeyInput(event) {
396414
event.stopPropagation();
397415

@@ -451,7 +469,9 @@ class ReactImageLightbox extends Component {
451469
}
452470
}
453471

454-
// Handle a mouse wheel event over the lightbox container
472+
/**
473+
* Handle a mouse wheel event over the lightbox container
474+
*/
455475
handleOuterMousewheel(event) {
456476
// Prevent scrolling of the background
457477
event.preventDefault();
@@ -522,7 +542,9 @@ class ReactImageLightbox extends Component {
522542
}
523543
}
524544

525-
// Handle a double click on the current image
545+
/**
546+
* Handle a double click on the current image
547+
*/
526548
handleImageDoubleClick(event) {
527549
if (this.state.zoomLevel > MIN_ZOOM_LEVEL) {
528550
// A double click when zoomed in zooms all the way out
@@ -541,7 +563,9 @@ class ReactImageLightbox extends Component {
541563
}
542564
}
543565

544-
// Handle a mouse click ending in the lightbox container
566+
/**
567+
* Handle a mouse click ending in the lightbox container
568+
*/
545569
handleMouseUp() {
546570
if (!this.isDragging) {
547571
return;
@@ -604,10 +628,8 @@ class ReactImageLightbox extends Component {
604628
return;
605629
}
606630

607-
const zoomMultiplier = this.getZoomMultiplier();
608-
609-
const newOffsetX = ((this.dragStartX - clientX) / zoomMultiplier) + this.dragStartOffsetX;
610-
const newOffsetY = ((this.dragStartY - clientY) / zoomMultiplier) + this.dragStartOffsetY;
631+
const newOffsetX = (this.dragStartX - clientX) + this.dragStartOffsetX;
632+
const newOffsetY = (this.dragStartY - clientY) + this.dragStartOffsetY;
611633
if (this.state.offsetX !== newOffsetX || this.state.offsetY !== newOffsetY) {
612634
this.setState({
613635
offsetX: newOffsetX,
@@ -782,16 +804,39 @@ class ReactImageLightbox extends Component {
782804
this.requestMove('prev', event);
783805
}
784806

807+
// Request to transition to the previous image
808+
getTransform({ x = null, y = null, zoom = null }) {
809+
const isOldIE = _ieVersion < 10;
810+
const transforms = [];
811+
if (x !== null || y !== null) {
812+
transforms.push(isOldIE ?
813+
`translate(${x || 0}px,${y || 0}px)` :
814+
`translate3d(${x || 0}px,${y || 0}px,0)`
815+
);
816+
}
817+
818+
if (zoom !== null) {
819+
transforms.push(isOldIE ?
820+
`scale(${zoom})` :
821+
`scale3d(${zoom},${zoom},1)`
822+
);
823+
}
824+
825+
return {
826+
[isOldIE ? 'msTransform' : 'transform']:
827+
transforms.length === 0 ? 'none' : transforms.join(' '),
828+
};
829+
}
830+
785831
render() {
832+
const boxSize = this.getLightboxRect();
786833
let transitionStyle = {};
787834

788835
// Transition settings for sliding animations
789836
if (!this.props.animationDisabled && this.isAnimating()) {
790837
transitionStyle = {
791838
...transitionStyle,
792-
transition: ['transform', 'left', 'top', 'right', 'bottom']
793-
.map(x => `${x} ${this.props.animationDuration}ms`)
794-
.join(', '),
839+
transition: `transform ${this.props.animationDuration}ms`,
795840
};
796841
}
797842

@@ -891,26 +936,28 @@ class ReactImageLightbox extends Component {
891936
};
892937

893938
const zoomMultiplier = this.getZoomMultiplier();
894-
const zoomStyle = _ieVersion < 10 ?
895-
{ msTransform: `scale(${zoomMultiplier})`} :
896-
{ transform: `scale3d(${zoomMultiplier}, ${zoomMultiplier}, 1)` };
897-
898939
// Next Image (displayed on the right)
899-
addImage('nextSrc', `image-next ril-image-next ${styles.imageNext}`);
940+
addImage(
941+
'nextSrc',
942+
`image-next ril-image-next ${styles.imageNext}`,
943+
this.getTransform({ x: boxSize.width })
944+
);
900945
// Main Image
901946
addImage(
902947
'mainSrc',
903948
'image-current ril-image-current',
904-
{
905-
...zoomStyle,
906-
left: -1 * zoomMultiplier * this.state.offsetX,
907-
right: zoomMultiplier * this.state.offsetX,
908-
top: -1 * zoomMultiplier * this.state.offsetY,
909-
bottom: zoomMultiplier * this.state.offsetY,
910-
}
949+
this.getTransform({
950+
x: -1 * this.state.offsetX,
951+
y: -1 * this.state.offsetY,
952+
zoom: zoomMultiplier,
953+
})
911954
);
912955
// Previous Image (displayed on the left)
913-
addImage('prevSrc', `image-prev ril-image-prev ${styles.imagePrev}`);
956+
addImage(
957+
'prevSrc',
958+
`image-prev ril-image-prev ${styles.imagePrev}`,
959+
this.getTransform({ x: -1 * boxSize.width })
960+
);
914961

915962
const noop = () => {};
916963

@@ -945,13 +992,14 @@ class ReactImageLightbox extends Component {
945992
backgroundColor: 'transparent',
946993
},
947994
content: {
948-
backgroundColor: 'transparent',
949-
border: 'none',
950-
borderRadius: 0,
951-
top: 0,
952-
left: 0,
953-
right: 0,
954-
bottom: 0,
995+
overflow: 'hidden', // Needed, otherwise keyboard shortcuts scroll the page
996+
border: 'none',
997+
borderRadius: 0,
998+
padding: 0,
999+
top: 0,
1000+
left: 0,
1001+
right: 0,
1002+
bottom: 0,
9551003
},
9561004
};
9571005

@@ -967,7 +1015,7 @@ class ReactImageLightbox extends Component {
9671015
return (
9681016
<Modal
9691017
isOpen
970-
onRequestClose={noop}
1018+
onRequestClose={this.props.clickOutsideToClose ? this.requestClose : noop}
9711019
style={modalStyle}
9721020
onAfterOpen={() => this.outerEl && this.outerEl.focus()} // Focus on the div with key handlers
9731021
>

src/style.scss

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ $toolbarHeight: 50px;
77

88
.outer {
99
background-color: rgba(0, 0, 0, 0.85);
10-
position: fixed;
1110
top: 0;
1211
left: 0;
1312
right: 0;
@@ -32,20 +31,20 @@ $toolbarHeight: 50px;
3231
.image {
3332
position: absolute;
3433
top: 0;
34+
right: 0;
3535
bottom: 0;
36+
left: 0;
3637
margin: auto;
3738
max-width: 100%;
3839
max-height: 100%;
3940
}
4041

4142
.imagePrev {
42-
left: -100%;
43-
right: 100%;
43+
@extends .image;
4444
}
4545

4646
.imageNext {
47-
left: 100%;
48-
right: -100%;
47+
@extends .image;
4948
}
5049

5150
.imageDiscourager {

0 commit comments

Comments
 (0)