import {mathUtil} from "@/graphic/Util/MathUtil"; import {computed, onUnmounted, ref, Ref, watchEffect} from "vue"; import {getPostionByTarget} from "@/components/base/utils"; // @ts-ignore import matruces from '@/utils/matruces' type Point = { x: number, y: number } type PointStore = [Point] | [Point, Point] enum Mode { move, scale, angle } export const HandMode = { MoveAndScale: [Mode.move, Mode.scale], AngleAndScale: [Mode.angle, Mode.scale], Move: [Mode.move], Scale: [Mode.scale], Angle: [Mode.angle] } export const useHand = ( targetRef: Ref, mode: Mode[], callback = () => {}, oldMatrix = matruces.translateMatrix(0,0,0) ) => { const parent = document.documentElement const matrix = ref(oldMatrix) let translate: Point; let scale: {center: Point, scale: number} let angle: number let startPos: PointStore let currentPos: PointStore const getScrollPos = (dom: HTMLElement) => { let x = 0, y = 0; while (dom && dom !== parent) { x += dom.scrollLeft y += dom.scrollTop dom = dom.offsetParent as HTMLElement } return {x, y} } const getPointByEvent = (ev: TouchEvent | MouseEvent): PointStore => { if (ev instanceof TouchEvent) { const point1 = { x: ev.touches[0].pageX, y: ev.touches[0].pageY } return ev.touches.length > 1 ? [ point1, { x: ev.touches[1].pageX, y: ev.touches[1].pageY } ] : [point1] } else { return [{ x: ev.pageX, y: ev.pageY }] } } let targetPosition, pageCenter, domCenter, pageStart; const start = (ev: TouchEvent | MouseEvent) => { const scrollPos = getScrollPos(targetRef.value) startPos = getPointByEvent(ev) targetPosition = getPostionByTarget(targetRef.value, parent) domCenter = { x: targetRef.value.offsetWidth / 2, y: targetRef.value.offsetHeight / 2 } pageStart = { x: targetPosition.x - scrollPos.x, y: targetPosition.y - scrollPos.y } pageCenter = { x: pageStart.x + domCenter.x, y: pageStart.y + domCenter.y } if (ev instanceof TouchEvent) { parent.addEventListener("touchmove", move) parent.addEventListener("touchend", end) } else { parent.addEventListener("mousemove", move) parent.addEventListener("mouseup", end) } ev.stopPropagation() ev.preventDefault() } const move = (ev: MouseEvent | TouchEvent) => { if ((currentPos = getPointByEvent(ev)).length != startPos.length) { return startPos = currentPos } if (startPos.length !== 2) { if (mode.includes(Mode.move)) { translate = { x: currentPos[0].x - startPos[0].x, y: currentPos[0].y - startPos[0].y } } else if (mode.includes(Mode.angle)) { const angleAngle = mathUtil.Angle(pageCenter, startPos[0], currentPos[0]) * (Math.PI / 180) const isClock = mathUtil.isClockwise([pageCenter, startPos[0], currentPos[0]]) angle = isClock ? -angleAngle : angleAngle } } else if (mode.includes(Mode.scale)) { const center = scale?.center || { x: (startPos[0].x - pageStart.x + startPos[1].x - pageStart.x) / 2, y: (startPos[0].y - pageStart.y + startPos[1].y - pageStart.y) / 2, } scale = { scale: mathUtil.getDistance(...currentPos) / mathUtil.getDistance(...startPos), center } } updateMatrix() startPos = currentPos } const end = (ev: MouseEvent | TouchEvent) => { callback() translate = scale = angle = null; if (ev instanceof TouchEvent) { parent.removeEventListener("touchmove", move) parent.removeEventListener("touchend", end) } else { parent.removeEventListener("mousemove", move) parent.removeEventListener("mouseup", end) } } const updateMatrix = () => { let currentMatrix if (translate) { currentMatrix = matruces.translateMatrix( translate.x / matrix.value[0], translate.y / matrix.value[0], 0 ) translate = null } else if (scale) { const offset = { x: (scale.center.x - domCenter.x) / (matrix.value[0] * scale.scale), y: (scale.center.y - domCenter.y) / (matrix.value[0] * scale.scale) } currentMatrix = matruces.multiplyMatrices( matruces.multiplyMatrices( matruces.translateMatrix(offset.x, offset.y, 0), matruces.scaleMatrix(scale.scale, scale.scale, 1) ), matruces.translateMatrix(-offset.x, -offset.y, 0) ) scale = null } else if (angle) { currentMatrix = matruces.rotateZMatrix(angle) angle = null } if (currentMatrix) { matrix.value = matruces.multiplyMatrices( matrix.value, currentMatrix ) } } const stop = watchEffect((onCleanup) => { if (targetRef.value) { targetRef.value.addEventListener("mousedown", start) targetRef.value.addEventListener("touchstart", start) } onCleanup(() => { if (targetRef.value) { targetRef.value.removeEventListener("mousedown", start) targetRef.value.removeEventListener("touchstart", start) } parent.removeEventListener("mousemove", move) parent.removeEventListener("mouseup", end) parent.removeEventListener("touchmove", move) parent.removeEventListener("touchend", end) }) }) const cssMatrix = computed( () => matruces.matrixArrayToCssMatrix(matrix.value) ) onUnmounted(stop) return { stop, matrix, cssMatrix } }