Maintain aspect ratio for preview image (#3966)
This commit is contained in:
		
							parent
							
								
									98eaa2aa27
								
							
						
					
					
						commit
						8f2c91568c
					
				|  | @ -1,5 +1,6 @@ | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import classNames from 'classnames'; | ||||
| 
 | ||||
| export default class ImageLoader extends React.PureComponent { | ||||
| 
 | ||||
|  | @ -20,46 +21,121 @@ export default class ImageLoader extends React.PureComponent { | |||
|     error: false, | ||||
|   } | ||||
| 
 | ||||
|   componentWillMount() { | ||||
|     this._loadImage(this.props.src); | ||||
|   removers = []; | ||||
| 
 | ||||
|   get canvasContext() { | ||||
|     if (!this.canvas) { | ||||
|       return null; | ||||
|     } | ||||
|     this._canvasContext = this._canvasContext || this.canvas.getContext('2d'); | ||||
|     return this._canvasContext; | ||||
|   } | ||||
| 
 | ||||
|   componentWillReceiveProps(props) { | ||||
|     this._loadImage(props.src); | ||||
|   componentDidMount () { | ||||
|     this.loadImage(this.props); | ||||
|   } | ||||
| 
 | ||||
|   _loadImage(src) { | ||||
|   componentWillReceiveProps (nextProps) { | ||||
|     if (this.props.src !== nextProps.src) { | ||||
|       this.loadImage(nextProps); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   loadImage (props) { | ||||
|     this.removeEventListeners(); | ||||
|     this.setState({ loading: true, error: false }); | ||||
|     Promise.all([ | ||||
|       this.loadPreviewCanvas(props), | ||||
|       this.loadOriginalImage(props), | ||||
|     ]) | ||||
|       .then(() => { | ||||
|         this.setState({ loading: false, error: false }); | ||||
|         this.clearPreviewCanvas(); | ||||
|       }) | ||||
|       .catch(() => this.setState({ loading: false, error: true })); | ||||
|   } | ||||
| 
 | ||||
|   loadPreviewCanvas = ({ previewSrc, width, height }) => new Promise((resolve, reject) => { | ||||
|     const image = new Image(); | ||||
|     const removeEventListeners = () => { | ||||
|       image.removeEventListener('error', handleError); | ||||
|       image.removeEventListener('load', handleLoad); | ||||
|     }; | ||||
|     const handleError = () => { | ||||
|       removeEventListeners(); | ||||
|       reject(); | ||||
|     }; | ||||
|     const handleLoad = () => { | ||||
|       removeEventListeners(); | ||||
|       this.canvasContext.drawImage(image, 0, 0, width, height); | ||||
|       resolve(); | ||||
|     }; | ||||
|     image.addEventListener('error', handleError); | ||||
|     image.addEventListener('load', handleLoad); | ||||
|     image.src = previewSrc; | ||||
|     this.removers.push(removeEventListeners); | ||||
|   }) | ||||
| 
 | ||||
|     image.onerror = () => this.setState({ loading: false, error: true }); | ||||
|     image.onload  = () => this.setState({ loading: false, error: false }); | ||||
| 
 | ||||
|     image.src = src; | ||||
| 
 | ||||
|     this.setState({ loading: true }); | ||||
|   clearPreviewCanvas () { | ||||
|     const { width, height } = this.canvas; | ||||
|     this.canvasContext.clearRect(0, 0, width, height); | ||||
|   } | ||||
| 
 | ||||
|   render() { | ||||
|     const { alt, src, previewSrc, width, height } = this.props; | ||||
|   loadOriginalImage = ({ src }) => new Promise((resolve, reject) => { | ||||
|     const image = new Image(); | ||||
|     const removeEventListeners = () => { | ||||
|       image.removeEventListener('error', handleError); | ||||
|       image.removeEventListener('load', handleLoad); | ||||
|     }; | ||||
|     const handleError = () => { | ||||
|       removeEventListeners(); | ||||
|       reject(); | ||||
|     }; | ||||
|     const handleLoad = () => { | ||||
|       removeEventListeners(); | ||||
|       resolve(); | ||||
|     }; | ||||
|     image.addEventListener('error', handleError); | ||||
|     image.addEventListener('load', handleLoad); | ||||
|     image.src = src; | ||||
|     this.removers.push(removeEventListeners); | ||||
|   }); | ||||
| 
 | ||||
|   removeEventListeners () { | ||||
|     this.removers.forEach(listeners => listeners()); | ||||
|     this.removers = []; | ||||
|   } | ||||
| 
 | ||||
|   setCanvasRef = c => { | ||||
|     this.canvas = c; | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { alt, src, width, height } = this.props; | ||||
|     const { loading } = this.state; | ||||
| 
 | ||||
|     const className = classNames('image-loader', { | ||||
|       'image-loader--loading': loading, | ||||
|     }); | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='image-loader'> | ||||
|         <img | ||||
|           alt={alt} | ||||
|           className='image-loader__img' | ||||
|           src={src} | ||||
|       <div className={className}> | ||||
|         <canvas | ||||
|           className='image-loader__preview-canvas' | ||||
|           width={width} | ||||
|           height={height} | ||||
|           ref={this.setCanvasRef} | ||||
|         /> | ||||
| 
 | ||||
|         {loading && | ||||
|         {!loading && ( | ||||
|           <img | ||||
|             alt='' | ||||
|             src={previewSrc} | ||||
|             className='image-loader__preview-img' | ||||
|             alt={alt} | ||||
|             className='image-loader__img' | ||||
|             src={src} | ||||
|             width={width} | ||||
|             height={height} | ||||
|           /> | ||||
|         } | ||||
|         )} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  |  | |||
|  | @ -1099,20 +1099,22 @@ | |||
| 
 | ||||
| .image-loader { | ||||
|   position: relative; | ||||
| } | ||||
| 
 | ||||
| .image-loader__preview-img { | ||||
|   position: absolute; | ||||
|   top: 0; | ||||
|   left: 0; | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   filter: blur(2px); | ||||
| } | ||||
|   &.image-loader--loading { | ||||
|     .image-loader__preview-canvas { | ||||
|       filter: blur(2px); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| .media-modal img.image-loader__preview-img { | ||||
|   width: 100%; | ||||
|   height: 100%; | ||||
|   .image-loader__img { | ||||
|     position: absolute; | ||||
|     top: 0; | ||||
|     left: 0; | ||||
|     right: 0; | ||||
|     width: 100%; | ||||
|     height: 100%; | ||||
|     background-image: none; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .navigation-bar { | ||||
|  | @ -2933,6 +2935,7 @@ button.icon-button.active i.fa-retweet { | |||
|   position: relative; | ||||
| 
 | ||||
|   img, | ||||
|   canvas, | ||||
|   video { | ||||
|     max-width: 80vw; | ||||
|     max-height: 80vh; | ||||
|  | @ -2940,7 +2943,8 @@ button.icon-button.active i.fa-retweet { | |||
|     height: auto; | ||||
|   } | ||||
| 
 | ||||
|   img { | ||||
|   img, | ||||
|   canvas { | ||||
|     display: block; | ||||
|     background: url('../images/void.png') repeat; | ||||
|   } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue