forked from cybrespace/mastodon
		
	Use preview image in <ImageLoader /> to provide immediate visual feedback (#3595)
before the full-size image is loaded
This commit is contained in:
		
							parent
							
								
									722d152082
								
							
						
					
					
						commit
						b623dd12c1
					
				
					 3 changed files with 44 additions and 16 deletions
				
			
		| 
						 | 
				
			
			@ -5,6 +5,9 @@ class ImageLoader extends React.PureComponent {
 | 
			
		|||
 | 
			
		||||
  static propTypes = {
 | 
			
		||||
    src: PropTypes.string.isRequired,
 | 
			
		||||
    previewSrc: PropTypes.string.isRequired,
 | 
			
		||||
    width: PropTypes.number.isRequired,
 | 
			
		||||
    height: PropTypes.number.isRequired,
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  state = {
 | 
			
		||||
| 
						 | 
				
			
			@ -13,31 +16,45 @@ class ImageLoader extends React.PureComponent {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  componentWillMount() {
 | 
			
		||||
    this.loadImage(this.props.src);
 | 
			
		||||
    this._loadImage(this.props.src);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  componentWillReceiveProps(props) {
 | 
			
		||||
    this.loadImage(props.src);
 | 
			
		||||
    this._loadImage(props.src);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  loadImage(src) {
 | 
			
		||||
  _loadImage(src) {
 | 
			
		||||
    const image = new Image();
 | 
			
		||||
 | 
			
		||||
    image.onerror = () => this.setState({ loading: false, error: true });
 | 
			
		||||
    image.onload = () => this.setState({ loading: false, error: false });
 | 
			
		||||
    image.onload  = () => this.setState({ loading: false, error: false });
 | 
			
		||||
 | 
			
		||||
    image.src = src;
 | 
			
		||||
    this.lastSrc = src;
 | 
			
		||||
 | 
			
		||||
    this.setState({ loading: true });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  render() {
 | 
			
		||||
    const { src } = this.props;
 | 
			
		||||
    const { src, previewSrc, width, height } = this.props;
 | 
			
		||||
    const { loading, error } = this.state;
 | 
			
		||||
 | 
			
		||||
    // TODO: handle image error state
 | 
			
		||||
    return (
 | 
			
		||||
      <div className='image-loader'>
 | 
			
		||||
        <img // eslint-disable-line jsx-a11y/img-has-alt
 | 
			
		||||
          className='image-loader__img'
 | 
			
		||||
          src={src}
 | 
			
		||||
          width={width}
 | 
			
		||||
          height={height}
 | 
			
		||||
        />
 | 
			
		||||
 | 
			
		||||
    const imageClass = `image-loader__img ${loading ? 'image-loader__img-loading' : ''}`;
 | 
			
		||||
 | 
			
		||||
    return <img className={imageClass} src={src} />; // eslint-disable-line jsx-a11y/img-has-alt
 | 
			
		||||
        {loading &&
 | 
			
		||||
          <img // eslint-disable-line jsx-a11y/img-has-alt
 | 
			
		||||
            src={previewSrc}
 | 
			
		||||
            className='image-loader__preview-img'
 | 
			
		||||
          />
 | 
			
		||||
        }
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -73,7 +73,7 @@ class MediaModal extends ImmutablePureComponent {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    if (attachment.get('type') === 'image') {
 | 
			
		||||
      content = <ImageLoader src={url} />;
 | 
			
		||||
      content = <ImageLoader previewSrc={attachment.get('preview_url')} src={url} width={attachment.getIn(['meta', 'original', 'width'])} height={attachment.getIn(['meta', 'original', 'height'])} />;
 | 
			
		||||
    } else if (attachment.get('type') === 'gifv') {
 | 
			
		||||
      content = <ExtendedVideoPlayer src={url} muted controls={false} />;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1137,13 +1137,22 @@
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.image-loader__img {
 | 
			
		||||
  transition: opacity 0.3s linear;
 | 
			
		||||
  opacity: 1;
 | 
			
		||||
.image-loader {
 | 
			
		||||
  position: relative;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.image-loader__img-loading {
 | 
			
		||||
  opacity: 0.7;
 | 
			
		||||
.image-loader__preview-img {
 | 
			
		||||
  position: absolute;
 | 
			
		||||
  top: 0;
 | 
			
		||||
  left: 0;
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
  filter: blur(2px);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.media-modal img.image-loader__preview-img {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
  height: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.navigation-bar {
 | 
			
		||||
| 
						 | 
				
			
			@ -2951,6 +2960,8 @@ button.icon-button.active i.fa-retweet {
 | 
			
		|||
  video {
 | 
			
		||||
    max-width: 80vw;
 | 
			
		||||
    max-height: 80vh;
 | 
			
		||||
    width: auto;
 | 
			
		||||
    height: auto;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  img {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue