Spaces:
No application file
No application file
| import { createElement, getElementAtPosition, adjustElementCoordinates, cursorForPosition, resizedCoordinates, adjustmentRequired } from './element-utils'; | |
| export const createMouseHandlers = ({ | |
| action, | |
| setAction, | |
| tool, | |
| setTool, | |
| elements, | |
| setElements, | |
| selectedElement, | |
| setSelectedElement, | |
| panOffset, | |
| setPanOffset, | |
| scale, | |
| scaleOffset, | |
| startPanMousePosition, | |
| setStartPanMousePosition, | |
| pressedKeys, | |
| pencilSize, | |
| setCaptureArea, | |
| isDrawing, | |
| setIsDrawing | |
| }) => { | |
| const getMouseCoordinates = event => { | |
| const clientX = (event.clientX - panOffset.x * scale + scaleOffset.x) / scale; | |
| const clientY = (event.clientY - panOffset.y * scale + scaleOffset.y) / scale; | |
| return { clientX, clientY }; | |
| }; | |
| const updateElement = (id, x1, y1, x2, y2, type, options) => { | |
| const elementsCopy = [...elements]; | |
| switch (type) { | |
| case "line": | |
| case "rectangle": | |
| elementsCopy[id] = createElement(id, x1, y1, x2, y2, type); | |
| elementsCopy[id].size = pencilSize; | |
| break; | |
| case "pencil": | |
| elementsCopy[id].points = [...elementsCopy[id].points, { x: x2, y: y2 }]; | |
| break; | |
| case "text": | |
| const textWidth = document | |
| .getElementById("canvas") | |
| .getContext("2d") | |
| .measureText(options.text).width; | |
| const textHeight = 24; | |
| elementsCopy[id] = { | |
| ...createElement(id, x1, y1, x1 + textWidth, y1 + textHeight, type), | |
| text: options.text, | |
| }; | |
| break; | |
| default: | |
| throw new Error(`Type not recognised: ${type}`); | |
| } | |
| setElements(elementsCopy, true); | |
| }; | |
| const handleMouseDown = event => { | |
| if (action === "writing") return; | |
| const { clientX, clientY } = getMouseCoordinates(event); | |
| if (event.button === 1 || pressedKeys.has(" ")) { | |
| setAction("panning"); | |
| setStartPanMousePosition({ x: clientX, y: clientY }); | |
| return; | |
| } | |
| setIsDrawing(true); | |
| if (tool === "capture") { | |
| const id = elements.length; | |
| const element = createElement(id, clientX, clientY, clientX, clientY, "capture"); | |
| setElements(prevState => [...prevState, element]); | |
| setSelectedElement(element); | |
| setAction("drawing"); | |
| return; | |
| } | |
| if (tool === "eraser") { | |
| setAction("erasing"); | |
| const elementToErase = getElementAtPosition(clientX, clientY, elements); | |
| if (elementToErase) { | |
| setElements(prevState => | |
| prevState.filter(el => el.id !== elementToErase.id) | |
| ); | |
| } | |
| return; | |
| } | |
| if (tool === "selection") { | |
| const element = getElementAtPosition(clientX, clientY, elements); | |
| if (element) { | |
| if (element.type === "pencil") { | |
| const xOffsets = element.points.map(point => clientX - point.x); | |
| const yOffsets = element.points.map(point => clientY - point.y); | |
| setSelectedElement({ ...element, xOffsets, yOffsets }); | |
| } else { | |
| const offsetX = clientX - element.x1; | |
| const offsetY = clientY - element.y1; | |
| setSelectedElement({ ...element, offsetX, offsetY }); | |
| } | |
| setElements(prevState => prevState); | |
| if (element.position === "inside") { | |
| setAction("moving"); | |
| } else { | |
| setAction("resizing"); | |
| } | |
| } | |
| } else { | |
| const id = elements.length; | |
| const element = createElement(id, clientX, clientY, clientX, clientY, tool, pencilSize); | |
| setElements(prevState => [...prevState, element]); | |
| setSelectedElement(element); | |
| setAction(tool === "text" ? "writing" : "drawing"); | |
| } | |
| }; | |
| const handleMouseMove = event => { | |
| const { clientX, clientY } = getMouseCoordinates(event); | |
| if (action === "panning") { | |
| const deltaX = clientX - startPanMousePosition.x; | |
| const deltaY = clientY - startPanMousePosition.y; | |
| setPanOffset({ | |
| x: panOffset.x + deltaX, | |
| y: panOffset.y + deltaY, | |
| }); | |
| return; | |
| } | |
| if (action === "erasing") { | |
| const elementToErase = getElementAtPosition(clientX, clientY, elements); | |
| if (elementToErase) { | |
| setElements(prevState => | |
| prevState.filter(el => el.id !== elementToErase.id) | |
| ); | |
| } | |
| return; | |
| } | |
| if (tool === "selection") { | |
| const element = getElementAtPosition(clientX, clientY, elements); | |
| event.target.style.cursor = element ? cursorForPosition(element.position) : "default"; | |
| } | |
| if (action === "drawing") { | |
| const index = elements.length - 1; | |
| const { x1, y1 } = elements[index]; | |
| if (tool === "capture") { | |
| const element = elements[index]; | |
| const updatedElement = { | |
| ...element, | |
| x2: clientX, | |
| y2: clientY | |
| }; | |
| const elementsCopy = [...elements]; | |
| elementsCopy[index] = updatedElement; | |
| setElements(elementsCopy, true); | |
| setCaptureArea(updatedElement); | |
| } else { | |
| updateElement(index, x1, y1, clientX, clientY, tool); | |
| } | |
| } else if (action === "moving") { | |
| if (selectedElement.type === "pencil") { | |
| const newPoints = selectedElement.points.map((_, index) => ({ | |
| x: clientX - selectedElement.xOffsets[index], | |
| y: clientY - selectedElement.yOffsets[index], | |
| })); | |
| const elementsCopy = [...elements]; | |
| elementsCopy[selectedElement.id] = { | |
| ...elementsCopy[selectedElement.id], | |
| points: newPoints, | |
| }; | |
| setElements(elementsCopy, true); | |
| } else { | |
| const { id, x1, x2, y1, y2, type, offsetX, offsetY } = selectedElement; | |
| const width = x2 - x1; | |
| const height = y2 - y1; | |
| const newX1 = clientX - offsetX; | |
| const newY1 = clientY - offsetY; | |
| const options = type === "text" ? { text: selectedElement.text } : {}; | |
| updateElement(id, newX1, newY1, newX1 + width, newY1 + height, type, options); | |
| } | |
| } else if (action === "resizing") { | |
| const { id, type, position, ...coordinates } = selectedElement; | |
| const { x1, y1, x2, y2 } = resizedCoordinates(clientX, clientY, position, coordinates); | |
| updateElement(id, x1, y1, x2, y2, type); | |
| } | |
| }; | |
| const handleMouseUp = event => { | |
| setIsDrawing(false); | |
| const { clientX, clientY } = getMouseCoordinates(event); | |
| if (selectedElement) { | |
| if ( | |
| selectedElement.type === "text" && | |
| clientX - selectedElement.offsetX === selectedElement.x1 && | |
| clientY - selectedElement.offsetY === selectedElement.y1 | |
| ) { | |
| setAction("writing"); | |
| return; | |
| } | |
| const index = selectedElement.id; | |
| const { id, type } = elements[index]; | |
| if ((action === "drawing" || action === "resizing") && adjustmentRequired(type)) { | |
| const { x1, y1, x2, y2 } = adjustElementCoordinates(elements[index]); | |
| updateElement(id, x1, y1, x2, y2, type); | |
| } | |
| } | |
| if (action === "drawing") { | |
| console.log("Hoàn thành một nét vẽ"); | |
| } | |
| if (action === "writing") return; | |
| setAction("none"); | |
| setSelectedElement(null); | |
| }; | |
| const handleBlur = event => { | |
| const { id, x1, y1, type } = selectedElement; | |
| setAction("none"); | |
| setSelectedElement(null); | |
| updateElement(id, x1, y1, null, null, type, { text: event.target.value }); | |
| }; | |
| return { | |
| handleMouseDown, | |
| handleMouseMove, | |
| handleMouseUp, | |
| handleBlur | |
| }; | |
| }; |