import React from 'react';
import PropTypes from 'prop-types';
+import classNames from 'classnames';
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;
+ }
+
+ componentDidMount () {
+ this.loadImage(this.props);
+ }
+
+ componentWillReceiveProps (nextProps) {
+ if (this.props.src !== nextProps.src) {
+ this.loadImage(nextProps);
+ }
}
- componentWillReceiveProps(props) {
- this._loadImage(props.src);
+ 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 }));
}
- _loadImage(src) {
+ 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 });
+ clearPreviewCanvas () {
+ const { width, height } = this.canvas;
+ this.canvasContext.clearRect(0, 0, width, height);
+ }
+ 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);
+ });
- this.setState({ loading: true });
+ removeEventListeners () {
+ this.removers.forEach(listeners => listeners());
+ this.removers = [];
}
- render() {
- const { alt, src, previewSrc, width, height } = this.props;
+ 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>
);
}