The code powering m.abunchtell.com https://m.abunchtell.com
Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

161 righe
4.0 KiB

  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import classNames from 'classnames';
  4. import { LoadingBar } from 'react-redux-loading-bar';
  5. import ZoomableImage from './zoomable_image';
  6. export default class ImageLoader extends React.PureComponent {
  7. static propTypes = {
  8. alt: PropTypes.string,
  9. src: PropTypes.string.isRequired,
  10. previewSrc: PropTypes.string,
  11. width: PropTypes.number,
  12. height: PropTypes.number,
  13. onClick: PropTypes.func,
  14. }
  15. static defaultProps = {
  16. alt: '',
  17. width: null,
  18. height: null,
  19. };
  20. state = {
  21. loading: true,
  22. error: false,
  23. width: null,
  24. }
  25. removers = [];
  26. canvas = null;
  27. get canvasContext() {
  28. if (!this.canvas) {
  29. return null;
  30. }
  31. this._canvasContext = this._canvasContext || this.canvas.getContext('2d');
  32. return this._canvasContext;
  33. }
  34. componentDidMount () {
  35. this.loadImage(this.props);
  36. }
  37. componentWillReceiveProps (nextProps) {
  38. if (this.props.src !== nextProps.src) {
  39. this.loadImage(nextProps);
  40. }
  41. }
  42. componentWillUnmount () {
  43. this.removeEventListeners();
  44. }
  45. loadImage (props) {
  46. this.removeEventListeners();
  47. this.setState({ loading: true, error: false });
  48. Promise.all([
  49. props.previewSrc && this.loadPreviewCanvas(props),
  50. this.hasSize() && this.loadOriginalImage(props),
  51. ].filter(Boolean))
  52. .then(() => {
  53. this.setState({ loading: false, error: false });
  54. this.clearPreviewCanvas();
  55. })
  56. .catch(() => this.setState({ loading: false, error: true }));
  57. }
  58. loadPreviewCanvas = ({ previewSrc, width, height }) => new Promise((resolve, reject) => {
  59. const image = new Image();
  60. const removeEventListeners = () => {
  61. image.removeEventListener('error', handleError);
  62. image.removeEventListener('load', handleLoad);
  63. };
  64. const handleError = () => {
  65. removeEventListeners();
  66. reject();
  67. };
  68. const handleLoad = () => {
  69. removeEventListeners();
  70. this.canvasContext.drawImage(image, 0, 0, width, height);
  71. resolve();
  72. };
  73. image.addEventListener('error', handleError);
  74. image.addEventListener('load', handleLoad);
  75. image.src = previewSrc;
  76. this.removers.push(removeEventListeners);
  77. })
  78. clearPreviewCanvas () {
  79. const { width, height } = this.canvas;
  80. this.canvasContext.clearRect(0, 0, width, height);
  81. }
  82. loadOriginalImage = ({ src }) => new Promise((resolve, reject) => {
  83. const image = new Image();
  84. const removeEventListeners = () => {
  85. image.removeEventListener('error', handleError);
  86. image.removeEventListener('load', handleLoad);
  87. };
  88. const handleError = () => {
  89. removeEventListeners();
  90. reject();
  91. };
  92. const handleLoad = () => {
  93. removeEventListeners();
  94. resolve();
  95. };
  96. image.addEventListener('error', handleError);
  97. image.addEventListener('load', handleLoad);
  98. image.src = src;
  99. this.removers.push(removeEventListeners);
  100. });
  101. removeEventListeners () {
  102. this.removers.forEach(listeners => listeners());
  103. this.removers = [];
  104. }
  105. hasSize () {
  106. const { width, height } = this.props;
  107. return typeof width === 'number' && typeof height === 'number';
  108. }
  109. setCanvasRef = c => {
  110. this.canvas = c;
  111. if (c) this.setState({ width: c.offsetWidth });
  112. }
  113. render () {
  114. const { alt, src, width, height, onClick } = this.props;
  115. const { loading } = this.state;
  116. const className = classNames('image-loader', {
  117. 'image-loader--loading': loading,
  118. 'image-loader--amorphous': !this.hasSize(),
  119. });
  120. return (
  121. <div className={className}>
  122. <LoadingBar loading={loading ? 1 : 0} className='loading-bar' style={{ width: this.state.width || width }} />
  123. {loading ? (
  124. <canvas
  125. className='image-loader__preview-canvas'
  126. ref={this.setCanvasRef}
  127. width={width}
  128. height={height}
  129. />
  130. ) : (
  131. <ZoomableImage
  132. alt={alt}
  133. src={src}
  134. onClick={onClick}
  135. />
  136. )}
  137. </div>
  138. );
  139. }
  140. }