previewImage.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  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. translate: {
  62. x: 0,
  63. y: 0
  64. },
  65. currZoom: this.props.zoom,
  66. };
  67. this.containerRef = React.createRef<HTMLDivElement>();
  68. this.imageRef = React.createRef<HTMLImageElement>();
  69. this.foundation = new PreviewImageFoundation(this.adapter);
  70. }
  71. componentDidMount() {
  72. this.foundation.init();
  73. window.addEventListener("resize", this.onWindowResize);
  74. }
  75. componentWillUnmount() {
  76. window.removeEventListener("resize", this.onWindowResize);
  77. }
  78. componentDidUpdate(prevProps: PreviewImageProps, prevStates: PreviewImageStates) {
  79. // If src changes, start a new loading
  80. const zoomChange = "zoom" in this.props && this.props.zoom !== this.state.currZoom;
  81. const srcChange = this.props.src && this.props.src !== prevProps.src;
  82. if (srcChange) {
  83. this.foundation.setLoading(true);
  84. }
  85. if (!zoomChange && !srcChange && prevProps) {
  86. if ("ratio" in this.props && this.props.ratio !== prevProps.ratio) {
  87. this.foundation.handleRatioChange();
  88. }
  89. if ("rotation" in this.props && this.props.rotation !== prevProps.rotation) {
  90. this.onWindowResize();
  91. }
  92. }
  93. }
  94. onWindowResize = (): void => {
  95. this.foundation.handleWindowResize();
  96. };
  97. // Determine the response method of right click according to the disableDownload parameter in props
  98. handleRightClickImage = (e) => {
  99. this.foundation.handleRightClickImage(e);
  100. };
  101. handleLoad = (e): void => {
  102. this.foundation.handleLoad(e);
  103. }
  104. handleError = (e): void => {
  105. this.foundation.handleError(e);
  106. }
  107. handleImageMove = (e): void => {
  108. this.foundation.handleImageMove(e);
  109. };
  110. handleMouseDown = (e: React.MouseEvent<HTMLImageElement>): void => {
  111. this.foundation.handleImageMouseDown(e);
  112. };
  113. render() {
  114. const { src, rotation, crossOrigin } = this.props;
  115. const { loading, width, height, translate } = this.state;
  116. const imgStyle = {
  117. position: "absolute",
  118. visibility: loading ? "hidden" : "visible",
  119. transform: `translate(${translate.x}px, ${translate.y}px) rotate(${rotation}deg)`,
  120. width,
  121. height
  122. };
  123. return (
  124. <div
  125. className={`${preViewImgPrefixCls}`}
  126. ref={this.containerRef}
  127. >
  128. {/* eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions */}
  129. <img
  130. ref={this.imageRef}
  131. src={src}
  132. alt="previewImag"
  133. className={`${preViewImgPrefixCls}-img`}
  134. key={src}
  135. onMouseMove={this.handleImageMove}
  136. onMouseDown={this.handleMouseDown}
  137. onContextMenu={this.handleRightClickImage}
  138. onDragStart={(e): void => e.preventDefault()}
  139. onLoad={this.handleLoad}
  140. onError={this.handleError}
  141. style={imgStyle as React.CSSProperties}
  142. crossOrigin={crossOrigin}
  143. />
  144. {loading && <Spin size={"large"} wrapperClassName={`${preViewImgPrefixCls}-spin`} />}
  145. </div>
  146. );
  147. }
  148. }