previewImage.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import React from "react";
  2. import BaseComponent from "../_base/baseComponent";
  3. import { cssClasses } from "@douyinfe/semi-foundation/image/constants";
  4. import { PreviewImageProps, PreviewImageStates } from "./interface";
  5. import PropTypes from "prop-types";
  6. import Spin from "../spin";
  7. import PreviewImageFoundation, { PreviewImageAdapter } from "@douyinfe/semi-foundation/image/previewImageFoundation";
  8. const prefixCls = cssClasses.PREFIX;
  9. const preViewImgPrefixCls = `${prefixCls}-preview-image`;
  10. export default class PreviewImage extends BaseComponent<PreviewImageProps, PreviewImageStates> {
  11. static propTypes = {
  12. src: PropTypes.string,
  13. rotation: PropTypes.number,
  14. style: PropTypes.object,
  15. // maxZoom: PropTypes.number,
  16. // minZoom: PropTypes.number,
  17. // zoomStep: PropTypes.number,
  18. zoom: PropTypes.number,
  19. ratio: PropTypes.string,
  20. disableDownload: PropTypes.bool,
  21. clickZoom: PropTypes.number,
  22. setRatio: PropTypes.func,
  23. onZoom: PropTypes.func,
  24. onLoad: PropTypes.func,
  25. onError: PropTypes.func,
  26. }
  27. static defaultProps = {
  28. // maxZoom: 5,
  29. // minZoom: 0.1,
  30. // zoomStep: 0.1,
  31. zoom: undefined,
  32. };
  33. get adapter(): PreviewImageAdapter<PreviewImageProps, PreviewImageStates> {
  34. return {
  35. ...super.adapter,
  36. getContainer: () => {
  37. return this.containerRef.current;
  38. },
  39. getImage: () => {
  40. return this.imageRef.current;
  41. },
  42. setLoading: (loading: boolean) => {
  43. this.setState({
  44. loading,
  45. });
  46. },
  47. setImageCursor: (canDrag: boolean) => {
  48. this.imageRef.current.style.cursor = canDrag ? "grab" : "default";
  49. }
  50. };
  51. }
  52. containerRef: React.RefObject<HTMLDivElement>;
  53. imageRef: React.RefObject<HTMLImageElement>;
  54. foundation: PreviewImageFoundation;
  55. constructor(props) {
  56. super(props);
  57. this.state = {
  58. width: 0,
  59. height: 0,
  60. loading: true,
  61. offset: { x: 0, y: 0 },
  62. currZoom: 0,
  63. top: 0,
  64. left: 0,
  65. };
  66. this.containerRef = React.createRef<HTMLDivElement>();
  67. this.imageRef = React.createRef<HTMLImageElement>();
  68. this.foundation = new PreviewImageFoundation(this.adapter);
  69. }
  70. componentDidMount() {
  71. window.addEventListener("resize", this.onWindowResize);
  72. }
  73. componentWillUnmount() {
  74. window.removeEventListener("resize", this.onWindowResize);
  75. }
  76. componentDidUpdate(prevProps: PreviewImageProps, prevStates: PreviewImageStates) {
  77. // If src changes, start a new loading
  78. const zoomChange = "zoom" in this.props && this.props.zoom !== this.state.currZoom;
  79. const srcChange = this.props.src && this.props.src !== prevProps.src;
  80. if (srcChange) {
  81. this.foundation.setLoading(true);
  82. }
  83. // If the incoming zoom changes, other content changes are determined based on the new zoom value
  84. if (zoomChange) {
  85. this.foundation.calculatePreviewImage(this.props.zoom, null);
  86. }
  87. if (!zoomChange && !srcChange && prevProps) {
  88. if ("ratio" in this.props && this.props.ratio !== prevProps.ratio) {
  89. this.foundation.handleRatioChange();
  90. }
  91. if ("rotation" in this.props && this.props.rotation !== prevProps.rotation) {
  92. this.onWindowResize();
  93. }
  94. }
  95. }
  96. onWindowResize = (): void => {
  97. this.foundation.handleWindowResize();
  98. };
  99. // Determine the response method of right click according to the disableDownload parameter in props
  100. handleRightClickImage = (e) => {
  101. this.foundation.handleRightClickImage(e);
  102. };
  103. handleLoad = (e): void => {
  104. this.foundation.handleLoad(e);
  105. }
  106. handleError = (e): void => {
  107. this.foundation.handleError(e);
  108. }
  109. handleMoveImage = (e): void => {
  110. this.foundation.handleMoveImage(e);
  111. };
  112. onImageMouseDown = (e: React.MouseEvent<HTMLImageElement>): void => {
  113. this.foundation.handleImageMouseDown(e);
  114. };
  115. render() {
  116. const { src, rotation, crossOrigin } = this.props;
  117. const { loading, width, height, top, left } = this.state;
  118. const imgStyle = {
  119. position: "absolute",
  120. visibility: loading ? "hidden" : "visible",
  121. transform: `rotate(${-rotation}deg)`,
  122. top,
  123. left,
  124. width,
  125. height,
  126. };
  127. return (
  128. <div
  129. className={`${preViewImgPrefixCls}`}
  130. ref={this.containerRef}
  131. >
  132. {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
  133. <img
  134. ref={this.imageRef}
  135. src={src}
  136. alt="previewImag"
  137. className={`${preViewImgPrefixCls}-img`}
  138. key={src}
  139. onMouseMove={this.handleMoveImage}
  140. onMouseDown={this.onImageMouseDown}
  141. onContextMenu={this.handleRightClickImage}
  142. onDragStart={(e): void => e.preventDefault()}
  143. onLoad={this.handleLoad}
  144. onError={this.handleError}
  145. style={imgStyle as React.CSSProperties}
  146. crossOrigin={crossOrigin}
  147. />
  148. {loading && <Spin size={"large"} wrapperClassName={`${preViewImgPrefixCls}-spin`}/>}
  149. </div>
  150. );
  151. }
  152. }