| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import { useCallback } from 'react';
- import type { PPTElement, PPTShapeElement } from '@/lib/types/slides';
- import { useHistorySnapshot } from '@/lib/hooks/use-history-snapshot';
- import { SHAPE_PATH_FORMULAS } from '@/configs/shapes';
- import { useCanvasOperations } from '@/lib/hooks/use-canvas-operations';
- interface ShapePathData {
- baseSize: number;
- originPos: number;
- min: number;
- max: number;
- relative: string;
- }
- /**
- * Move shape keypoint Hook
- *
- * @param elementListRef - Element list ref (used to read the latest value on mouseup)
- * @param setElementList - Element list setter (used to trigger re-render)
- * @param canvasScale - Canvas scale ratio
- */
- export function useMoveShapeKeypoint(
- elementListRef: React.RefObject<PPTElement[]>,
- setElementList: React.Dispatch<React.SetStateAction<PPTElement[]>>,
- canvasScale: number,
- ) {
- const updateSlide = useCanvasOperations().updateSlide;
- const { addHistorySnapshot } = useHistorySnapshot();
- const moveShapeKeypoint = useCallback(
- (e: React.MouseEvent | React.TouchEvent, element: PPTShapeElement, index = 0) => {
- const native = e.nativeEvent;
- const isTouchEvent = native instanceof TouchEvent;
- if (isTouchEvent && !native.changedTouches?.length) return;
- let isMouseDown = true;
- const startPageX = isTouchEvent ? native.changedTouches[0].pageX : native.pageX;
- const startPageY = isTouchEvent ? native.changedTouches[0].pageY : native.pageY;
- const originKeypoints = element.keypoints!;
- const pathFormula = SHAPE_PATH_FORMULAS[element.pathFormula!];
- let shapePathData: ShapePathData | null = null;
- if ('editable' in pathFormula && pathFormula.editable) {
- const getBaseSize = pathFormula.getBaseSize![index];
- const range = pathFormula.range![index];
- const relative = pathFormula.relative![index];
- const keypoint = originKeypoints[index];
- const baseSize = getBaseSize(element.width, element.height);
- const originPos = baseSize * keypoint;
- const [min, max] = range;
- shapePathData = { baseSize, originPos, min, max, relative };
- }
- const handleMouseMove = (e: MouseEvent | TouchEvent) => {
- if (!isMouseDown) return;
- const currentPageX = e instanceof MouseEvent ? e.pageX : e.changedTouches[0].pageX;
- const currentPageY = e instanceof MouseEvent ? e.pageY : e.changedTouches[0].pageY;
- const moveX = (currentPageX - startPageX) / canvasScale;
- const moveY = (currentPageY - startPageY) / canvasScale;
- // Update local element list during mousemove
- const newElements = elementListRef.current.map((el) => {
- if (el.id === element.id && shapePathData) {
- const { baseSize, originPos, min, max, relative } = shapePathData;
- const shapeElement = el as PPTShapeElement;
- let keypoint = 0;
- if (relative === 'center') keypoint = (originPos - moveX * 2) / baseSize;
- else if (relative === 'left') keypoint = (originPos + moveX) / baseSize;
- else if (relative === 'right') keypoint = (originPos - moveX) / baseSize;
- else if (relative === 'top') keypoint = (originPos + moveY) / baseSize;
- else if (relative === 'bottom') keypoint = (originPos - moveY) / baseSize;
- else if (relative === 'left_bottom') keypoint = (originPos + moveX) / baseSize;
- else if (relative === 'right_bottom') keypoint = (originPos - moveX) / baseSize;
- else if (relative === 'top_right') keypoint = (originPos + moveY) / baseSize;
- else if (relative === 'bottom_right') keypoint = (originPos - moveY) / baseSize;
- if (keypoint < min) keypoint = min;
- if (keypoint > max) keypoint = max;
- let keypoints: number[] = [];
- if (Array.isArray(originKeypoints)) {
- keypoints = [...originKeypoints];
- keypoints[index] = keypoint;
- } else keypoints = [keypoint];
- return {
- ...el,
- keypoints,
- path: pathFormula.formula(shapeElement.width, shapeElement.height, keypoints),
- };
- }
- return el;
- });
- // Update both ref and state
- elementListRef.current = newElements;
- setElementList(newElements);
- };
- const handleMouseUp = (e: MouseEvent | TouchEvent) => {
- isMouseDown = false;
- document.ontouchmove = null;
- document.ontouchend = null;
- document.onmousemove = null;
- document.onmouseup = null;
- const currentPageX = e instanceof MouseEvent ? e.pageX : e.changedTouches[0].pageX;
- const currentPageY = e instanceof MouseEvent ? e.pageY : e.changedTouches[0].pageY;
- if (startPageX === currentPageX && startPageY === currentPageY) return;
- updateSlide({ elements: elementListRef.current });
- addHistorySnapshot();
- };
- if (isTouchEvent) {
- document.ontouchmove = handleMouseMove;
- document.ontouchend = handleMouseUp;
- } else {
- document.onmousemove = handleMouseMove;
- document.onmouseup = handleMouseUp;
- }
- },
- [elementListRef, setElementList, canvasScale, updateSlide, addHistorySnapshot],
- );
- return {
- moveShapeKeypoint,
- };
- }
|