@@ -9,6 +9,8 @@ import { Dots, Arrow } from './controls';
9
9
import areChildImagesEqual from './utils/areChildImagesEqual' ;
10
10
11
11
const SELECTED_CLASS = 'carousel-slide-selected' ;
12
+ const LOADING_CLASS = 'carousel-slide-loading' ;
13
+ const MAX_LOAD_RETRIES = 500 ;
12
14
13
15
/**
14
16
* React component class that renders a carousel, which can contain images or other content.
@@ -104,8 +106,7 @@ export default class Carousel extends Component {
104
106
slideDimensions : { } ,
105
107
dragOffset : 0 ,
106
108
transitionDuration : 0 ,
107
- transitioningFrom : null ,
108
- leftOffset : 0
109
+ transitioningFrom : null
109
110
} ;
110
111
autobind ( this ) ;
111
112
}
@@ -125,13 +126,15 @@ export default class Carousel extends Component {
125
126
126
127
componentDidUpdate ( prevProps , prevState ) {
127
128
const { children, autoplay, slideWidth } = this . props ;
128
- const { currentSlide, loadedImages, direction, loading } = this . state ;
129
+ const { currentSlide, loadedImages, direction, loading, slideDimensions } = this . state ;
129
130
const oldChildren = prevProps . children ;
130
131
131
132
if ( direction !== prevState . direction ||
132
133
currentSlide !== prevState . currentSlide ||
133
134
loadedImages !== prevState . loadedImages ||
134
- slideWidth !== prevProps . slideWidth ) {
135
+ slideWidth !== prevProps . slideWidth ||
136
+ slideDimensions . width !== prevState . slideDimensions . width ||
137
+ slideDimensions . height !== prevState . slideDimensions . height ) {
135
138
// Whenever new images are loaded, the current slide index changes, the transition direction changes, or the
136
139
// slide width changes, we need to recalculate the left offset positioning of the slides.
137
140
this . calcLeftOffset ( ) ;
@@ -154,11 +157,13 @@ export default class Carousel extends Component {
154
157
155
158
if ( lazyLoad ) {
156
159
this . fetchImages ( ) ;
157
- } else if ( autoplay ) {
158
- this . startAutoplay ( ) ;
160
+ } else {
161
+ if ( autoplay ) {
162
+ this . startAutoplay ( ) ;
163
+ }
164
+ this . calcLeftOffset ( ) ;
159
165
}
160
166
161
- this . calcLeftOffset ( ) ;
162
167
window . addEventListener ( 'resize' , this . calcLeftOffset , false ) ;
163
168
}
164
169
@@ -168,6 +173,8 @@ export default class Carousel extends Component {
168
173
window . removeEventListener ( 'resize' , this . calcLeftOffset , false ) ;
169
174
document . removeEventListener ( 'mousemove' , this . handleMovement , false ) ;
170
175
clearTimeout ( this . _autoplayTimer ) ;
176
+ clearTimeout ( this . _retryTimer ) ;
177
+ clearTimeout ( this . _initialLoadTimer ) ;
171
178
this . _isMounted = false ;
172
179
}
173
180
@@ -190,7 +197,7 @@ export default class Carousel extends Component {
190
197
*/
191
198
fetchImages ( ) {
192
199
const { children } = this . props ;
193
- const { loadedImages, currentSlide, loading } = this . state ;
200
+ const { loadedImages, currentSlide } = this . state ;
194
201
const slides = Children . toArray ( children ) ;
195
202
const imagesToPrefetch = Math . min ( this . props . imagesToPrefetch , slides . length ) ;
196
203
const startIndex = currentSlide - Math . floor ( imagesToPrefetch / 2 ) ;
@@ -224,10 +231,8 @@ export default class Carousel extends Component {
224
231
} ;
225
232
img . src = image ;
226
233
} ) ;
227
- } else if ( loading ) {
228
- this . setState ( {
229
- loading : false
230
- } ) ;
234
+ } else {
235
+ this . calcLeftOffset ( ) ;
231
236
}
232
237
}
233
238
@@ -238,26 +243,26 @@ export default class Carousel extends Component {
238
243
*/
239
244
handleInitialLoad ( ) {
240
245
const { currentSlide } = this . state ;
241
- const { slideWidth, slideHeight } = this . props ;
242
246
const slides = this . _track . childNodes ;
243
- const newState = {
244
- loading : false
245
- } ;
246
-
247
+ const { slideWidth, slideHeight } = this . props ;
247
248
if ( ! slideWidth || ! slideHeight ) {
248
249
for ( let i = 0 ; i < slides . length ; i ++ ) {
249
250
const slide = slides [ i ] ;
250
251
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
+ } ) ;
255
262
break ;
256
263
}
257
264
}
258
265
}
259
-
260
- this . setState ( newState ) ;
261
266
}
262
267
263
268
/**
@@ -540,7 +545,7 @@ export default class Carousel extends Component {
540
545
key = { key }
541
546
style = { loadingSlideStyle }
542
547
data-index = { index }
543
- className = { classnames ( slideClasses , 'carousel-slide-loading' ) }
548
+ className = { classnames ( slideClasses , LOADING_CLASS ) }
544
549
> </ li >
545
550
) ;
546
551
} ) ;
@@ -630,14 +635,17 @@ export default class Carousel extends Component {
630
635
631
636
/**
632
637
* 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
633
640
*/
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 ) ;
641
649
return ;
642
650
}
643
651
@@ -656,37 +664,38 @@ export default class Carousel extends Component {
656
664
657
665
let leftOffset = 0 ;
658
666
let selectedSlide ;
667
+ let foundZeroWidthSlide = false ;
668
+ let isCurrentSlideLoading = false ;
669
+ let currentSlideWidth ;
659
670
for ( let i = 0 ; i < slides . length ; i ++ ) {
660
671
selectedSlide = slides [ i ] ;
661
672
leftOffset -= cellPadding ;
673
+ isCurrentSlideLoading = selectedSlide . className . indexOf ( LOADING_CLASS ) !== - 1 ;
674
+ currentSlideWidth = selectedSlide . offsetWidth ;
675
+ foundZeroWidthSlide = foundZeroWidthSlide || ( ! currentSlideWidth && ! isCurrentSlideLoading ) ;
662
676
if ( parseInt ( selectedSlide . getAttribute ( 'data-index' ) , 10 ) === currentSlide ) {
663
677
break ;
664
678
}
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 ;
682
680
}
683
681
684
682
// Center the current slide within the viewport
685
683
leftOffset += ( viewportWidth - currentSlideWidth ) / 2 ;
684
+ const shouldRetry = foundZeroWidthSlide && retryCount < MAX_LOAD_RETRIES ;
686
685
687
686
if ( leftOffset !== this . state . leftOffset ) {
688
687
this . setState ( { leftOffset } ) ;
689
688
}
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
+ }
690
699
}
691
700
692
701
/**
0 commit comments