index.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  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 { LinePositionType, PointType } from '../../constants/type';
  6. import { SVG, Circle } from './styled';
  7. type Props = LinePositionType & {
  8. top: number;
  9. left: number;
  10. width: number;
  11. height: number;
  12. onMove: (position: LinePositionType) => void;
  13. onClick?: (event: React.MouseEvent) => void;
  14. completeStart: PointType;
  15. completeEnd: PointType;
  16. };
  17. const MARGIN_DISTANCE = 18;
  18. const initState = {
  19. start: { x: 0, y: 0 },
  20. end: { x: 0, y: 0 },
  21. operator: '',
  22. clickX: 0,
  23. clickY: 0,
  24. };
  25. const index: React.FC<Props> = ({
  26. top,
  27. left,
  28. width,
  29. height,
  30. start,
  31. end,
  32. onMove,
  33. onClick,
  34. completeStart,
  35. completeEnd,
  36. }: Props) => {
  37. const [state, setState] = useState(initState);
  38. const [cursorPosition, setRef] = useCursorPosition();
  39. const handleMouseDown = (e: React.MouseEvent): void => {
  40. const operatorId = (e.target as HTMLElement).getAttribute('data-id') as string;
  41. const coord = getAbsoluteCoordinate(document.body, e);
  42. setRef(document.body);
  43. setState({
  44. start,
  45. end,
  46. operator: operatorId,
  47. clickX: coord.x,
  48. clickY: coord.y,
  49. });
  50. };
  51. const handleMouseUp = (): void => {
  52. setRef(null);
  53. setState(initState);
  54. };
  55. useEffect(() => {
  56. if (cursorPosition.x && cursorPosition.y) {
  57. if (state.operator === 'start') {
  58. const x1 = cursorPosition.x - (state.clickX - state.start.x);
  59. const y1 = cursorPosition.y - (state.clickY - state.start.y);
  60. onMove({
  61. start: { x: x1, y: y1 },
  62. end,
  63. });
  64. } else if (state.operator === 'end') {
  65. const x2 = cursorPosition.x - (state.clickX - state.end.x);
  66. const y2 = cursorPosition.y - (state.clickY - state.end.y);
  67. onMove({
  68. start,
  69. end: { x: x2, y: y2 },
  70. });
  71. } else if (state.operator === 'move') {
  72. const x1 = cursorPosition.x - (state.clickX - state.start.x);
  73. const y1 = cursorPosition.y - (state.clickY - state.start.y);
  74. const x2 = cursorPosition.x - (state.clickX - state.end.x);
  75. const y2 = cursorPosition.y - (state.clickY - state.end.y);
  76. onMove({
  77. start: { x: x1, y: y1 },
  78. end: { x: x2, y: y2 },
  79. });
  80. }
  81. }
  82. }, [cursorPosition, state]);
  83. useEffect(() => {
  84. window.addEventListener('mouseup', handleMouseUp);
  85. return (): void => {
  86. window.removeEventListener('mouseup', handleMouseUp);
  87. };
  88. }, []);
  89. return (
  90. <SVG
  91. data-id="move"
  92. viewBox={`${left} ${top} ${width + 10} ${height + 10}`}
  93. style={{
  94. left: `${left - MARGIN_DISTANCE}px`,
  95. top: `${top - MARGIN_DISTANCE}px`,
  96. width: `${width + 10}px`,
  97. height: `${height + 10}px`,
  98. }}
  99. onClick={onClick}
  100. onMouseDown={handleMouseDown}
  101. >
  102. <Circle
  103. data-id="start"
  104. onMouseDown={handleMouseDown}
  105. fill={color.primary}
  106. cx={completeStart.x + MARGIN_DISTANCE}
  107. cy={completeStart.y + MARGIN_DISTANCE}
  108. r={6}
  109. />
  110. <Circle
  111. data-id="end"
  112. onMouseDown={handleMouseDown}
  113. fill={color.primary}
  114. cx={completeEnd.x + MARGIN_DISTANCE}
  115. cy={completeEnd.y + MARGIN_DISTANCE}
  116. r={6}
  117. />
  118. </SVG>
  119. );
  120. };
  121. export default index;