index.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
  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(
  41. 'data-id'
  42. ) as string;
  43. const coord = getAbsoluteCoordinate(document.body, e);
  44. setRef(document.body);
  45. setState({
  46. start,
  47. end,
  48. operator: operatorId,
  49. clickX: coord.x,
  50. clickY: coord.y,
  51. });
  52. };
  53. const handleMouseUp = (): void => {
  54. setRef(null);
  55. setState(initState);
  56. };
  57. useEffect(() => {
  58. if (cursorPosition.x && cursorPosition.y) {
  59. if (state.operator === 'start') {
  60. const x1 = cursorPosition.x - (state.clickX - state.start.x);
  61. const y1 = cursorPosition.y - (state.clickY - state.start.y);
  62. onMove({
  63. start: { x: x1, y: y1 },
  64. end,
  65. });
  66. } else if (state.operator === 'end') {
  67. const x2 = cursorPosition.x - (state.clickX - state.end.x);
  68. const y2 = cursorPosition.y - (state.clickY - state.end.y);
  69. onMove({
  70. start,
  71. end: { x: x2, y: y2 },
  72. });
  73. } else if (state.operator === 'move') {
  74. const x1 = cursorPosition.x - (state.clickX - state.start.x);
  75. const y1 = cursorPosition.y - (state.clickY - state.start.y);
  76. const x2 = cursorPosition.x - (state.clickX - state.end.x);
  77. const y2 = cursorPosition.y - (state.clickY - state.end.y);
  78. onMove({
  79. start: { x: x1, y: y1 },
  80. end: { x: x2, y: y2 },
  81. });
  82. }
  83. }
  84. }, [cursorPosition, state]);
  85. useEffect(() => {
  86. window.addEventListener('mouseup', handleMouseUp);
  87. return (): void => {
  88. window.removeEventListener('mouseup', handleMouseUp);
  89. };
  90. }, []);
  91. return (
  92. <SVG
  93. data-id="move"
  94. viewBox={`${left} ${top} ${width + 10} ${height + 10}`}
  95. style={{
  96. left: `${left - MARGIN_DISTANCE}px`,
  97. top: `${top - MARGIN_DISTANCE}px`,
  98. width: `${width + 10}px`,
  99. height: `${height + 10}px`,
  100. }}
  101. onClick={onClick}
  102. onMouseDown={handleMouseDown}
  103. >
  104. <Circle
  105. data-id="start"
  106. onMouseDown={handleMouseDown}
  107. fill={color.primary}
  108. cx={completeStart.x + MARGIN_DISTANCE}
  109. cy={completeStart.y + MARGIN_DISTANCE}
  110. r={6}
  111. />
  112. <Circle
  113. data-id="end"
  114. onMouseDown={handleMouseDown}
  115. fill={color.primary}
  116. cx={completeEnd.x + MARGIN_DISTANCE}
  117. cy={completeEnd.y + MARGIN_DISTANCE}
  118. r={6}
  119. />
  120. </SVG>
  121. );
  122. };
  123. export default index;