Skip to content

Commit 5478fe9

Browse files
Merge pull request #34 from godaddy/initial-size
Fix issue where selected slide is not centered
2 parents ca04b5f + 44339c9 commit 5478fe9

File tree

3 files changed

+83
-70
lines changed

3 files changed

+83
-70
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 1.5.2 - Sept 2019
2+
3+
***(Bug Fix)*** Fix issue that can cause the initial carousel positioning to be off
4+
15
# 1.5.1 - Sept 2019
26

37
***(Bug Fix)*** Fix issue that can cause the slideshow to never render

example.js

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ class CustomDots extends React.Component {
3838
}
3939

4040
const IMAGES = [
41-
'http://lorempixel.com/400/300',
42-
'http://lorempixel.com/275/300',
43-
'http://lorempixel.com/400/300',
44-
'http://lorempixel.com/350/300',
45-
'http://lorempixel.com/250/300',
46-
'http://lorempixel.com/375/300',
47-
'http://lorempixel.com/425/300',
48-
'http://lorempixel.com/325/300'
41+
'http://picsum.photos/400/300',
42+
'http://picsum.photos/275/300',
43+
'http://picsum.photos/400/300',
44+
'http://picsum.photos/350/300',
45+
'http://picsum.photos/250/300',
46+
'http://picsum.photos/375/300',
47+
'http://picsum.photos/425/300',
48+
'http://picsum.photos/325/300'
4949
];
5050
let imgIndex = 1;
5151

@@ -78,29 +78,29 @@ class TestPage extends Component {
7878
</Carousel>
7979
<h2 style={ { marginTop: 80 } }>Infinite with only 2 slides</h2>
8080
<Carousel width='450px' arrows={ false } slideHeight='300px'>
81-
<img src='http://lorempixel.com/325/300'/>
82-
<img src='http://lorempixel.com/350/300'/>
81+
<img src='http://picsum.photos/325/300'/>
82+
<img src='http://picsum.photos/350/300'/>
8383
</Carousel>
8484
<h2 style={ { marginTop: 80 } }>Infinite with only 1 slide</h2>
8585
<Carousel width='450px' infinite={ true } arrows={ false } dots={ false }>
86-
<img src='http://lorempixel.com/325/300'/>
86+
<img src='http://picsum.photos/325/300'/>
8787
</Carousel>
8888
<h2 style={ { marginTop: 80 } }>Autoplay with background images</h2>
8989
<Carousel width='100%' slideWidth='100%' slideHeight='70vh' arrows={ false } autoplay={ true }>
90-
<div style={ { backgroundImage: 'url(http://lorempixel.com/600/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
91-
<div style={ { backgroundImage: 'url(http://lorempixel.com/650/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
92-
<div style={ { backgroundImage: 'url(http://lorempixel.com/675/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
93-
<div style={ { backgroundImage: 'url(http://lorempixel.com/700/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
90+
<div style={ { backgroundImage: 'url(http://picsum.photos/600/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
91+
<div style={ { backgroundImage: 'url(http://picsum.photos/650/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
92+
<div style={ { backgroundImage: 'url(http://picsum.photos/675/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
93+
<div style={ { backgroundImage: 'url(http://picsum.photos/700/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
9494
</Carousel>
9595
<h2 style={ { marginTop: 80 } }>Background images with fade</h2>
9696
<Carousel width='100%' slideWidth='100%' slideHeight='70vh' transition='fade' transitionDuration={ 1000 } autoplay={ true } arrows={ true }>
97-
<div style={ { backgroundImage: 'url(http://lorempixel.com/600/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
98-
<div style={ { backgroundImage: 'url(http://lorempixel.com/650/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
99-
<div style={ { backgroundImage: 'url(http://lorempixel.com/675/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
100-
<div style={ { backgroundImage: 'url(http://lorempixel.com/700/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
101-
<div style={ { backgroundImage: 'url(http://lorempixel.com/750/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
102-
<div style={ { backgroundImage: 'url(http://lorempixel.com/725/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
103-
<div style={ { backgroundImage: 'url(http://lorempixel.com/625/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
97+
<div style={ { backgroundImage: 'url(http://picsum.photos/600/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
98+
<div style={ { backgroundImage: 'url(http://picsum.photos/650/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
99+
<div style={ { backgroundImage: 'url(http://picsum.photos/675/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
100+
<div style={ { backgroundImage: 'url(http://picsum.photos/700/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
101+
<div style={ { backgroundImage: 'url(http://picsum.photos/750/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
102+
<div style={ { backgroundImage: 'url(http://picsum.photos/725/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
103+
<div style={ { backgroundImage: 'url(http://picsum.photos/625/300)', backgroundSize: 'cover', height: '100%', width: '100%' } }/>
104104
</Carousel>
105105
<h2 style={ { marginTop: 80 } }>Custom dots component</h2>
106106
<Carousel

src/index.js

Lines changed: 57 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import { Dots, Arrow } from './controls';
99
import areChildImagesEqual from './utils/areChildImagesEqual';
1010

1111
const SELECTED_CLASS = 'carousel-slide-selected';
12+
const LOADING_CLASS = 'carousel-slide-loading';
13+
const MAX_LOAD_RETRIES = 500;
1214

1315
/**
1416
* React component class that renders a carousel, which can contain images or other content.
@@ -104,8 +106,7 @@ export default class Carousel extends Component {
104106
slideDimensions: {},
105107
dragOffset: 0,
106108
transitionDuration: 0,
107-
transitioningFrom: null,
108-
leftOffset: 0
109+
transitioningFrom: null
109110
};
110111
autobind(this);
111112
}
@@ -125,13 +126,15 @@ export default class Carousel extends Component {
125126

126127
componentDidUpdate(prevProps, prevState) {
127128
const { children, autoplay, slideWidth } = this.props;
128-
const { currentSlide, loadedImages, direction, loading } = this.state;
129+
const { currentSlide, loadedImages, direction, loading, slideDimensions } = this.state;
129130
const oldChildren = prevProps.children;
130131

131132
if (direction !== prevState.direction ||
132133
currentSlide !== prevState.currentSlide ||
133134
loadedImages !== prevState.loadedImages ||
134-
slideWidth !== prevProps.slideWidth) {
135+
slideWidth !== prevProps.slideWidth ||
136+
slideDimensions.width !== prevState.slideDimensions.width ||
137+
slideDimensions.height !== prevState.slideDimensions.height) {
135138
// Whenever new images are loaded, the current slide index changes, the transition direction changes, or the
136139
// slide width changes, we need to recalculate the left offset positioning of the slides.
137140
this.calcLeftOffset();
@@ -154,11 +157,13 @@ export default class Carousel extends Component {
154157

155158
if (lazyLoad) {
156159
this.fetchImages();
157-
} else if (autoplay) {
158-
this.startAutoplay();
160+
} else {
161+
if (autoplay) {
162+
this.startAutoplay();
163+
}
164+
this.calcLeftOffset();
159165
}
160166

161-
this.calcLeftOffset();
162167
window.addEventListener('resize', this.calcLeftOffset, false);
163168
}
164169

@@ -168,6 +173,8 @@ export default class Carousel extends Component {
168173
window.removeEventListener('resize', this.calcLeftOffset, false);
169174
document.removeEventListener('mousemove', this.handleMovement, false);
170175
clearTimeout(this._autoplayTimer);
176+
clearTimeout(this._retryTimer);
177+
clearTimeout(this._initialLoadTimer);
171178
this._isMounted = false;
172179
}
173180

@@ -190,7 +197,7 @@ export default class Carousel extends Component {
190197
*/
191198
fetchImages() {
192199
const { children } = this.props;
193-
const { loadedImages, currentSlide, loading } = this.state;
200+
const { loadedImages, currentSlide } = this.state;
194201
const slides = Children.toArray(children);
195202
const imagesToPrefetch = Math.min(this.props.imagesToPrefetch, slides.length);
196203
const startIndex = currentSlide - Math.floor(imagesToPrefetch / 2);
@@ -224,10 +231,8 @@ export default class Carousel extends Component {
224231
};
225232
img.src = image;
226233
});
227-
} else if (loading) {
228-
this.setState({
229-
loading: false
230-
});
234+
} else {
235+
this.calcLeftOffset();
231236
}
232237
}
233238

@@ -238,26 +243,26 @@ export default class Carousel extends Component {
238243
*/
239244
handleInitialLoad() {
240245
const { currentSlide } = this.state;
241-
const { slideWidth, slideHeight } = this.props;
242246
const slides = this._track.childNodes;
243-
const newState = {
244-
loading: false
245-
};
246-
247+
const { slideWidth, slideHeight } = this.props;
247248
if (!slideWidth || !slideHeight) {
248249
for (let i = 0; i < slides.length; i++) {
249250
const slide = slides[i];
250251
if (parseInt(slide.getAttribute('data-index'), 10) === currentSlide) {
251-
newState.slideDimensions = {
252-
width: slide.offsetWidth,
253-
height: slide.offsetHeight
254-
};
252+
if (!slide.offsetWidth || !slide.offsetHeight) {
253+
this._initialLoadTimer = setTimeout(this.handleInitialLoad, 10);
254+
return;
255+
}
256+
this.setState({
257+
slideDimensions: {
258+
width: slide.offsetWidth,
259+
height: slide.offsetHeight
260+
}
261+
});
255262
break;
256263
}
257264
}
258265
}
259-
260-
this.setState(newState);
261266
}
262267

263268
/**
@@ -540,7 +545,7 @@ export default class Carousel extends Component {
540545
key={ key }
541546
style={ loadingSlideStyle }
542547
data-index={ index }
543-
className={ classnames(slideClasses, 'carousel-slide-loading') }
548+
className={ classnames(slideClasses, LOADING_CLASS) }
544549
></li>
545550
);
546551
});
@@ -630,14 +635,17 @@ export default class Carousel extends Component {
630635

631636
/**
632637
* Updates the component state with the correct left offset position so that the slides will be positioned correctly.
638+
*
639+
* @param {Number} retryCount Used when retries are needed due to slow slide loading
633640
*/
634-
calcLeftOffset() {
635-
const { loading, direction } = this.state;
636-
if (loading || !this._track || !this._viewport) {
637-
clearTimeout(this._retryTimer);
638-
if (this._isMounted) {
639-
this._retryTimer = setTimeout(this.calcLeftOffset, 10);
640-
}
641+
calcLeftOffset(retryCount = 0) {
642+
const { direction, loading } = this.state;
643+
const viewportWidth = this._viewport && this._viewport.offsetWidth;
644+
645+
clearTimeout(this._retryTimer);
646+
647+
if (!this._track || !viewportWidth) {
648+
this._retryTimer = setTimeout(this.calcLeftOffset, 10);
641649
return;
642650
}
643651

@@ -656,37 +664,38 @@ export default class Carousel extends Component {
656664

657665
let leftOffset = 0;
658666
let selectedSlide;
667+
let foundZeroWidthSlide = false;
668+
let isCurrentSlideLoading = false;
669+
let currentSlideWidth;
659670
for (let i = 0; i < slides.length; i++) {
660671
selectedSlide = slides[i];
661672
leftOffset -= cellPadding;
673+
isCurrentSlideLoading = selectedSlide.className.indexOf(LOADING_CLASS) !== -1;
674+
currentSlideWidth = selectedSlide.offsetWidth;
675+
foundZeroWidthSlide = foundZeroWidthSlide || (!currentSlideWidth && !isCurrentSlideLoading);
662676
if (parseInt(selectedSlide.getAttribute('data-index'), 10) === currentSlide) {
663677
break;
664678
}
665-
leftOffset -= selectedSlide.offsetWidth;
666-
}
667-
const currentSlideWidth = selectedSlide.offsetWidth;
668-
const viewportWidth = this._viewport.offsetWidth;
669-
670-
if (currentSlideWidth === 0 &&
671-
viewportWidth === 0) {
672-
// Sometimes there is a delay when the carousel is first rendering, so do a few retries
673-
this._retryCount = this._retryCount || 0;
674-
if (this._retryCount < 5) {
675-
this._retryCount++;
676-
setTimeout(this.calcLeftOffset, 100);
677-
} else {
678-
this._retryCount = 0;
679-
}
680-
681-
return;
679+
leftOffset -= currentSlideWidth;
682680
}
683681

684682
// Center the current slide within the viewport
685683
leftOffset += (viewportWidth - currentSlideWidth) / 2;
684+
const shouldRetry = foundZeroWidthSlide && retryCount < MAX_LOAD_RETRIES;
686685

687686
if (leftOffset !== this.state.leftOffset) {
688687
this.setState({ leftOffset });
689688
}
689+
690+
if (shouldRetry) {
691+
this._retryTimer = setTimeout(this.calcLeftOffset.bind(this, ++retryCount), 10);
692+
return;
693+
}
694+
695+
if (loading) {
696+
// We have correctly positioned the slides and are done loading images, so reveal the carousel
697+
this.setState({ loading: false });
698+
}
690699
}
691700

692701
/**

0 commit comments

Comments
 (0)