index.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import React, { useEffect, useState } from 'react';
  2. import { color } from '../../constants/style';
  3. import useCursorPosition from '../../hooks/useCursorPosition';
  4. import { getAbsoluteCoordinate } from '../../helpers/position';
  5. import { calcDragAndDropScale } from '../../helpers/utility';
  6. import { CoordType, PointType, CircleType } from '../../constants/type';
  7. import generateCirclesData from './data';
  8. import { SVG, Rect, Circle } from './styled';
  9. type Props = {
  10. left: number;
  11. top: number;
  12. width: number;
  13. height: number;
  14. onMove?: (moveCoord: CoordType) => void;
  15. onScale?: (scaleCoord: CoordType) => void;
  16. onClick?: (event: React.MouseEvent) => void;
  17. onDoubleClick?: () => void;
  18. };
  19. type ObjPositionType = {
  20. top: number;
  21. left: number;
  22. width: number;
  23. height: number;
  24. operator: string;
  25. clickX: number;
  26. clickY: number;
  27. };
  28. const initState = {
  29. top: 0,
  30. left: 0,
  31. width: 0,
  32. height: 0,
  33. operator: '',
  34. clickX: 0,
  35. clickY: 0,
  36. };
  37. const index: React.FC<Props> = ({
  38. left,
  39. top,
  40. width,
  41. height,
  42. onMove,
  43. onScale,
  44. onClick,
  45. onDoubleClick,
  46. }: Props) => {
  47. const data = generateCirclesData(width, height);
  48. const [state, setState] = useState(initState);
  49. const [cursorPosition, setRef] = useCursorPosition(40);
  50. const handleMouseDown = (e: React.MouseEvent | React.TouchEvent): void => {
  51. e.preventDefault();
  52. const operatorId = (e.target as HTMLElement).getAttribute('data-id') as string;
  53. const coord = getAbsoluteCoordinate(document.body, e);
  54. setRef(document.body);
  55. setState({
  56. top,
  57. left,
  58. width,
  59. height,
  60. operator: operatorId,
  61. clickX: coord.x,
  62. clickY: coord.y,
  63. });
  64. };
  65. const handleMouseUp = (): void => {
  66. setRef(null);
  67. setState(initState);
  68. };
  69. const calcMoveResult = (
  70. currentPosition: PointType,
  71. startPosition: PointType,
  72. objPosition: ObjPositionType,
  73. ): CoordType => ({
  74. left: currentPosition.x - (startPosition.x - objPosition.left),
  75. top: currentPosition.y - (startPosition.y - objPosition.top),
  76. });
  77. const calcScaleResult = (
  78. currentPosition: PointType,
  79. objPosition: ObjPositionType,
  80. ): CoordType => {
  81. const scaleData = calcDragAndDropScale({
  82. ...objPosition,
  83. moveX: currentPosition.x || 0,
  84. moveY: currentPosition.y || 0,
  85. });
  86. const scaleWidth = scaleData.width || 0;
  87. const scaleHeight = scaleData.height || 0;
  88. const maxTop = objPosition.top + objPosition.height;
  89. const maxLeft = objPosition.left + objPosition.width;
  90. scaleData.left = scaleData.left > maxLeft ? maxLeft : scaleData.left;
  91. scaleData.top = scaleData.top > maxTop ? maxTop : scaleData.top;
  92. scaleData.width = scaleWidth > 0 ? scaleWidth : 0;
  93. scaleData.height = scaleHeight > 0 ? scaleHeight : 0;
  94. return scaleData;
  95. };
  96. useEffect(() => {
  97. if (cursorPosition.x && cursorPosition.y && state.clickX) {
  98. if (state.operator === 'move' && onMove) {
  99. onMove(calcMoveResult(
  100. { x: cursorPosition.x, y: cursorPosition.y },
  101. { x: state.clickX, y: state.clickY },
  102. state,
  103. ));
  104. } else if (onScale) {
  105. onScale(calcScaleResult(
  106. {
  107. x: cursorPosition.x,
  108. y: cursorPosition.y,
  109. },
  110. state,
  111. ));
  112. }
  113. }
  114. }, [cursorPosition, state]);
  115. useEffect(() => {
  116. window.addEventListener('mouseup', handleMouseUp);
  117. window.addEventListener('touchend', handleMouseUp);
  118. return (): void => {
  119. window.removeEventListener('mouseup', handleMouseUp);
  120. window.removeEventListener('touchend', handleMouseUp);
  121. };
  122. }, []);
  123. return (
  124. <SVG
  125. style={{
  126. left: `${left - 12}px`,
  127. top: `${top - 12}px`,
  128. width: `${width + 24}px`,
  129. height: `${height + 24}px`,
  130. }}
  131. onClick={onClick}
  132. onDoubleClick={onDoubleClick}
  133. >
  134. <Rect
  135. x={6}
  136. y={6}
  137. width={width + 12}
  138. height={height + 12}
  139. stroke={onScale ? color.primary : 'transparency'}
  140. onMouseDown={handleMouseDown}
  141. onTouchStart={handleMouseDown}
  142. data-id="move"
  143. />
  144. {
  145. onScale && data.map((attr: CircleType) => (
  146. <Circle
  147. key={attr.direction}
  148. data-id={attr.direction}
  149. onMouseDown={handleMouseDown}
  150. onTouchStart={handleMouseDown}
  151. fill={color.primary}
  152. {...attr}
  153. />
  154. ))
  155. }
  156. </SVG>
  157. );
  158. };
  159. export default index;