diff --git a/src/components/ImageCarousel/ImageCarousel.css b/src/components/ImageCarousel/ImageCarousel.css index 6584a0f7..92bafc6b 100644 --- a/src/components/ImageCarousel/ImageCarousel.css +++ b/src/components/ImageCarousel/ImageCarousel.css @@ -96,7 +96,27 @@ } } +.loading { + position: absolute; + top: 50%; + left: 50%; + width: 50px; + height: 50px; + margin-left: -25px; + margin-top: -25px; + opacity: 0; +} + +.loadingVisible { + opacity: 1; + transition: opacity var(--transitionStyle); +} + .image { /* Fit image in the available space as big as possible, keeping the aspect ratio */ object-fit: contain; } + +.imageLoading { + opacity: 0.5; +} diff --git a/src/components/ImageCarousel/ImageCarousel.js b/src/components/ImageCarousel/ImageCarousel.js index 4183916e..0e9aa72e 100644 --- a/src/components/ImageCarousel/ImageCarousel.js +++ b/src/components/ImageCarousel/ImageCarousel.js @@ -2,7 +2,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { injectIntl, intlShape } from 'react-intl'; import classNames from 'classnames'; -import { ResponsiveImage } from '../../components'; +import { ResponsiveImage, IconSpinner } from '../../components'; import { propTypes } from '../../util/types'; import css from './ImageCarousel.css'; @@ -18,7 +18,7 @@ const imageScaledXLarge = 'scaled-xlarge'; // width 2400 class ImageCarousel extends Component { constructor(props) { super(props); - this.state = { selectedImageIndex: 0 }; + this.state = { selectedImageIndex: 0, selectedImageLoaded: false }; this.onKeyUp = this.onKeyUp.bind(this); this.prev = this.prev.bind(this); this.next = this.next.bind(this); @@ -38,16 +38,22 @@ class ImageCarousel extends Component { } prev() { const count = this.props.images.length; + if (count < 2) { + return; + } this.setState(prevState => { const newIndex = count > 0 ? (count + prevState.selectedImageIndex - 1) % count : 0; - return { selectedImageIndex: newIndex }; + return { selectedImageIndex: newIndex, selectedImageLoaded: false }; }); } next() { const count = this.props.images.length; + if (count < 2) { + return; + } this.setState(prevState => { const newIndex = count > 0 ? (count + prevState.selectedImageIndex + 1) % count : 0; - return { selectedImageIndex: newIndex }; + return { selectedImageIndex: newIndex, selectedImageLoaded: false }; }); } render() { @@ -76,13 +82,36 @@ class ImageCarousel extends Component { } ); + const markImageLoaded = index => () => { + this.setState(prevState => { + if (prevState.selectedImageIndex === index) { + // Only mark the image loaded if the current index hasn't + // changed, i.e. user hasn't already changed to another + // image index. + return { selectedImageLoaded: true }; + } + return {}; + }); + }; + + const currentImageIsLoaded = images.length === 0 || this.state.selectedImageLoaded; + const loadingIconClasses = classNames(css.loading, { + [css.loadingVisible]: !currentImageIsLoaded, + }); + const imageClasses = classNames(css.image, { + [css.imageLoading]: !currentImageIsLoaded, + }); + return (
+