import type { Ref } from 'vue' import { useSlidesStore } from '@/store' import type { PPTElement, PPTLineElement, PPTVideoElement, PPTAudioElement, PPTChartElement } from '@/types/slides' import useHistorySnapshot from '@/hooks/useHistorySnapshot' /** * 计算给定坐标到原点连线的弧度 * @param x 坐标x * @param y 坐标y */ const getAngleFromCoordinate = (x: number, y: number) => { const radian = Math.atan2(x, y) const angle = 180 / Math.PI * radian return angle } export default ( elementList: Ref, viewportRef: Ref, canvasScale: Ref, ) => { const slidesStore = useSlidesStore() const { addHistorySnapshot } = useHistorySnapshot() // 旋转元素 const rotateElement = (e: MouseEvent | TouchEvent, element: Exclude) => { const isTouchEvent = !(e instanceof MouseEvent) if (isTouchEvent && (!e.changedTouches || !e.changedTouches[0])) return let isMouseDown = true let angle = 0 const elOriginRotate = element.rotate || 0 const elLeft = element.left const elTop = element.top const elWidth = element.width const elHeight = element.height // 元素中心点(旋转中心点) const centerX = elLeft + elWidth / 2 const centerY = elTop + elHeight / 2 if (!viewportRef.value) return const viewportRect = viewportRef.value.getBoundingClientRect() 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 mouseX = (currentPageX - viewportRect.left) / canvasScale.value const mouseY = (currentPageY - viewportRect.top) / canvasScale.value const x = mouseX - centerX const y = centerY - mouseY angle = getAngleFromCoordinate(x, y) // 靠近45倍数的角度时有吸附效果 const sorptionRange = 5 if ( Math.abs(angle) <= sorptionRange ) angle = 0 else if ( angle > 0 && Math.abs(angle - 45) <= sorptionRange ) angle -= (angle - 45) else if ( angle < 0 && Math.abs(angle + 45) <= sorptionRange ) angle -= (angle + 45) else if ( angle > 0 && Math.abs(angle - 90) <= sorptionRange ) angle -= (angle - 90) else if ( angle < 0 && Math.abs(angle + 90) <= sorptionRange ) angle -= (angle + 90) else if ( angle > 0 && Math.abs(angle - 135) <= sorptionRange ) angle -= (angle - 135) else if ( angle < 0 && Math.abs(angle + 135) <= sorptionRange ) angle -= (angle + 135) else if ( angle > 0 && Math.abs(angle - 180) <= sorptionRange ) angle -= (angle - 180) else if ( angle < 0 && Math.abs(angle + 180) <= sorptionRange ) angle -= (angle + 180) elementList.value = elementList.value.map(el => element.id === el.id ? { ...el, rotate: angle } : el) } const handleMouseup = () => { isMouseDown = false document.onmousemove = null document.onmouseup = null if (elOriginRotate === angle) return slidesStore.updateSlide({ elements: elementList.value }) addHistorySnapshot() } if (isTouchEvent) { document.ontouchmove = handleMousemove document.ontouchend = handleMouseup } else { document.onmousemove = handleMousemove document.onmouseup = handleMouseup } } return { rotateElement, } }