Photo.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import React from 'react';
  2. import classNames from 'classnames';
  3. import throttle from './utils/throttle';
  4. import Spinner from './components/Spinner';
  5. import getSuitableImageSize from './utils/getSuitableImageSize';
  6. import './Photo.less';
  7. export interface IPhotoProps extends React.HTMLAttributes<any> {
  8. src: string;
  9. className?: string;
  10. onPhotoResize: () => void;
  11. loadingElement?: JSX.Element;
  12. brokenElement?: JSX.Element;
  13. }
  14. type PhotoState = {
  15. loaded: boolean;
  16. broken: boolean;
  17. naturalWidth: number;
  18. naturalHeight: number;
  19. width: number;
  20. height: number;
  21. };
  22. export default class Photo extends React.PureComponent<
  23. IPhotoProps,
  24. PhotoState
  25. > {
  26. static displayName = 'Photo';
  27. readonly state = {
  28. loaded: false,
  29. broken: false,
  30. naturalWidth: 1,
  31. naturalHeight: 1,
  32. width: 1,
  33. height: 1,
  34. };
  35. private isMount = true;
  36. constructor(props: IPhotoProps) {
  37. super(props);
  38. this.handleResize = throttle(this.handleResize, 8);
  39. }
  40. componentDidMount() {
  41. const currPhoto = new Image();
  42. currPhoto.src = this.props.src;
  43. currPhoto.onload = this.handleImageLoaded;
  44. currPhoto.onerror = this.handleImageBroken;
  45. window.addEventListener('resize', this.handleResize);
  46. }
  47. componentWillUnmount() {
  48. this.isMount = false;
  49. window.removeEventListener('resize', this.handleResize);
  50. }
  51. handleImageLoaded = e => {
  52. const { naturalWidth, naturalHeight } = e.target;
  53. if (this.isMount) {
  54. this.setState({
  55. loaded: true,
  56. naturalWidth,
  57. naturalHeight,
  58. ...getSuitableImageSize(naturalWidth, naturalHeight),
  59. });
  60. }
  61. };
  62. handleImageBroken = () => {
  63. if (this.isMount) {
  64. this.setState({
  65. broken: true,
  66. });
  67. }
  68. };
  69. handleResize = () => {
  70. const { loaded, naturalWidth, naturalHeight } = this.state;
  71. if (loaded && this.isMount) {
  72. this.setState(
  73. getSuitableImageSize(naturalWidth, naturalHeight),
  74. this.props.onPhotoResize,
  75. );
  76. }
  77. };
  78. render() {
  79. const {
  80. src,
  81. className,
  82. loadingElement,
  83. brokenElement,
  84. onPhotoResize,
  85. ...restProps
  86. } = this.props;
  87. const { loaded, broken, width, height } = this.state;
  88. if (src && !broken) {
  89. if (loaded) {
  90. return (
  91. <img
  92. className={classNames('PhotoView__Photo', className)}
  93. src={src}
  94. width={width}
  95. height={height}
  96. alt=""
  97. {...restProps}
  98. />
  99. );
  100. }
  101. return loadingElement || <Spinner />;
  102. }
  103. return brokenElement || null;
  104. }
  105. }