|
|
@@ -0,0 +1,358 @@
|
|
|
+import type { IDzrPropsModelValue } from '../types'
|
|
|
+import type { MouseTouchEvent } from './types'
|
|
|
+
|
|
|
+/**
|
|
|
+ * 会自动销毁的鼠标移动事件
|
|
|
+ * @param onMousemove
|
|
|
+ */
|
|
|
+export const autoDestroyMouseMove = (
|
|
|
+ onMousemove: (e: MouseTouchEvent) => void,
|
|
|
+ mouseUpCallBack?: () => void
|
|
|
+) => {
|
|
|
+ const onMouseup = () => {
|
|
|
+ document.removeEventListener('mousemove', onMousemove)
|
|
|
+ document.removeEventListener('touchmove', onMousemove)
|
|
|
+ document.removeEventListener('mouseup', onMouseup)
|
|
|
+ document.removeEventListener('touchend', onMouseup)
|
|
|
+ document.removeEventListener('mouseleave', onMouseup)
|
|
|
+ if (mouseUpCallBack) {
|
|
|
+ mouseUpCallBack()
|
|
|
+ }
|
|
|
+ }
|
|
|
+ document.addEventListener('mousemove', onMousemove)
|
|
|
+ document.addEventListener('touchmove', onMousemove)
|
|
|
+ document.addEventListener('mouseup', onMouseup)
|
|
|
+ document.addEventListener('touchend', onMouseup)
|
|
|
+ document.addEventListener('mouseleave', onMouseup)
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 根据坐标对齐到网格
|
|
|
+ * @param position 当前坐标
|
|
|
+ * @param grid 网格大小
|
|
|
+ * @returns 对应网格的坐标
|
|
|
+ */
|
|
|
+export const alignToGrid = (position: number, grid = 1) => {
|
|
|
+ const integerPart = Math.floor(position / grid)
|
|
|
+ const fractionalPart = position % grid
|
|
|
+
|
|
|
+ if (fractionalPart >= grid / 2) {
|
|
|
+ return (integerPart + 1) * grid
|
|
|
+ } else {
|
|
|
+ return integerPart * grid
|
|
|
+ }
|
|
|
+}
|
|
|
+/** 根据移动的距离对齐到网格
|
|
|
+ * @param diff 移动的距离
|
|
|
+ * @param grid 网格大小
|
|
|
+ */
|
|
|
+export const calcGrid = (diff: number, grid = 1) => {
|
|
|
+ // 得到每次缩放的余数
|
|
|
+ const r = Math.abs(diff) % grid
|
|
|
+
|
|
|
+ // 正负grid
|
|
|
+ const mulGrid = diff > 0 ? grid : -grid
|
|
|
+ let result = 0
|
|
|
+ // 余数大于grid的1/2
|
|
|
+ if (r > grid / 2) {
|
|
|
+ result = mulGrid * Math.ceil(Math.abs(diff) / grid)
|
|
|
+ } else {
|
|
|
+ result = mulGrid * Math.floor(Math.abs(diff) / grid)
|
|
|
+ }
|
|
|
+
|
|
|
+ return result
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 获取当前点击坐标 根据pc端和移动端获取
|
|
|
+ * @param e
|
|
|
+ * @returns
|
|
|
+ */
|
|
|
+export function getXY(e: MouseTouchEvent) {
|
|
|
+ let clientX = 0,
|
|
|
+ clientY = 0
|
|
|
+ if (isTouchEvent(e)) {
|
|
|
+ const touch = e.targetTouches[0]
|
|
|
+ clientX = touch.pageX
|
|
|
+ clientY = touch.pageY
|
|
|
+ } else {
|
|
|
+ clientX = e.clientX
|
|
|
+ clientY = e.clientY
|
|
|
+ }
|
|
|
+
|
|
|
+ return { clientX, clientY }
|
|
|
+}
|
|
|
+
|
|
|
+function isTouchEvent(val: unknown): val is TouchEvent {
|
|
|
+ const typeStr = Object.prototype.toString.call(val)
|
|
|
+ return typeStr.substring(8, typeStr.length - 1) === 'TouchEvent'
|
|
|
+}
|
|
|
+
|
|
|
+export const getLength = (x: number, y: number) => Math.sqrt(x * x + y * y)
|
|
|
+
|
|
|
+export const degToRadian = (deg: number) => (deg * Math.PI) / 180
|
|
|
+const cos = (deg: number) => Math.cos(degToRadian(deg))
|
|
|
+const sin = (deg: number) => Math.sin(degToRadian(deg))
|
|
|
+/**
|
|
|
+ * 计算并返回给定类型变换的新样式。
|
|
|
+ *
|
|
|
+ * @param {string} type - 变换的类型。
|
|
|
+ * @param {any} rect - 矩形对象。
|
|
|
+ * @param {number} deltaW - 宽度变化。
|
|
|
+ * @param {number} deltaH - 高度变化。
|
|
|
+ * @param {number | undefined} ratio - 比例。
|
|
|
+ * @param {number} minWidth - 最小宽度。
|
|
|
+ * @param {number} minHeight - 最小高度。
|
|
|
+ * @returns {Object} 矩形的新位置和大小。
|
|
|
+ */
|
|
|
+export const getNewStyle = (
|
|
|
+ type: string,
|
|
|
+ rect: any,
|
|
|
+ deltaW: number,
|
|
|
+ deltaH: number,
|
|
|
+ ratio: number | undefined,
|
|
|
+ minWidth: number,
|
|
|
+ minHeight: number
|
|
|
+) => {
|
|
|
+ // eslint-disable-next-line prefer-const
|
|
|
+ let { width, height, centerX, centerY, rotateAngle } = rect
|
|
|
+ const widthFlag = width < 0 ? -1 : 1
|
|
|
+ const heightFlag = height < 0 ? -1 : 1
|
|
|
+ width = Math.abs(width)
|
|
|
+ height = Math.abs(height)
|
|
|
+ switch (type) {
|
|
|
+ case 'r': {
|
|
|
+ const widthAndDeltaW = setWidthAndDeltaW(width, deltaW, minWidth)
|
|
|
+ width = widthAndDeltaW.width
|
|
|
+ deltaW = widthAndDeltaW.deltaW
|
|
|
+ if (ratio) {
|
|
|
+ deltaH = deltaW / ratio
|
|
|
+ height = width / ratio
|
|
|
+ // 左上角固定
|
|
|
+ centerX += (deltaW / 2) * cos(rotateAngle) - (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY += (deltaW / 2) * sin(rotateAngle) + (deltaH / 2) * cos(rotateAngle)
|
|
|
+ } else {
|
|
|
+ // 左边固定
|
|
|
+ centerX += (deltaW / 2) * cos(rotateAngle)
|
|
|
+ centerY += (deltaW / 2) * sin(rotateAngle)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 'tr': {
|
|
|
+ deltaH = -deltaH
|
|
|
+ const widthAndDeltaW = setWidthAndDeltaW(width, deltaW, minWidth)
|
|
|
+ width = widthAndDeltaW.width
|
|
|
+ deltaW = widthAndDeltaW.deltaW
|
|
|
+ const heightAndDeltaH = setHeightAndDeltaH(height, deltaH, minHeight)
|
|
|
+ height = heightAndDeltaH.height
|
|
|
+ deltaH = heightAndDeltaH.deltaH
|
|
|
+ if (ratio) {
|
|
|
+ deltaW = deltaH * ratio
|
|
|
+ width = height * ratio
|
|
|
+ }
|
|
|
+ centerX += (deltaW / 2) * cos(rotateAngle) + (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY += (deltaW / 2) * sin(rotateAngle) - (deltaH / 2) * cos(rotateAngle)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 'br': {
|
|
|
+ const widthAndDeltaW = setWidthAndDeltaW(width, deltaW, minWidth)
|
|
|
+ width = widthAndDeltaW.width
|
|
|
+ deltaW = widthAndDeltaW.deltaW
|
|
|
+ const heightAndDeltaH = setHeightAndDeltaH(height, deltaH, minHeight)
|
|
|
+ height = heightAndDeltaH.height
|
|
|
+ deltaH = heightAndDeltaH.deltaH
|
|
|
+ if (ratio) {
|
|
|
+ deltaW = deltaH * ratio
|
|
|
+ width = height * ratio
|
|
|
+ }
|
|
|
+ centerX += (deltaW / 2) * cos(rotateAngle) - (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY += (deltaW / 2) * sin(rotateAngle) + (deltaH / 2) * cos(rotateAngle)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 'bc': {
|
|
|
+ const heightAndDeltaH = setHeightAndDeltaH(height, deltaH, minHeight)
|
|
|
+ height = heightAndDeltaH.height
|
|
|
+ deltaH = heightAndDeltaH.deltaH
|
|
|
+ if (ratio) {
|
|
|
+ deltaW = deltaH * ratio
|
|
|
+ width = height * ratio
|
|
|
+ // 左上角固定
|
|
|
+ centerX += (deltaW / 2) * cos(rotateAngle) - (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY += (deltaW / 2) * sin(rotateAngle) + (deltaH / 2) * cos(rotateAngle)
|
|
|
+ } else {
|
|
|
+ // 上边固定
|
|
|
+ centerX -= (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY += (deltaH / 2) * cos(rotateAngle)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 'bl': {
|
|
|
+ deltaW = -deltaW
|
|
|
+ const widthAndDeltaW = setWidthAndDeltaW(width, deltaW, minWidth)
|
|
|
+ width = widthAndDeltaW.width
|
|
|
+ deltaW = widthAndDeltaW.deltaW
|
|
|
+ const heightAndDeltaH = setHeightAndDeltaH(height, deltaH, minHeight)
|
|
|
+ height = heightAndDeltaH.height
|
|
|
+ deltaH = heightAndDeltaH.deltaH
|
|
|
+ if (ratio) {
|
|
|
+ height = width / ratio
|
|
|
+ deltaH = deltaW / ratio
|
|
|
+ }
|
|
|
+ centerX -= (deltaW / 2) * cos(rotateAngle) + (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY -= (deltaW / 2) * sin(rotateAngle) - (deltaH / 2) * cos(rotateAngle)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 'l': {
|
|
|
+ deltaW = -deltaW
|
|
|
+ const widthAndDeltaW = setWidthAndDeltaW(width, deltaW, minWidth)
|
|
|
+ width = widthAndDeltaW.width
|
|
|
+ deltaW = widthAndDeltaW.deltaW
|
|
|
+ if (ratio) {
|
|
|
+ height = width / ratio
|
|
|
+ deltaH = deltaW / ratio
|
|
|
+ // 右上角固定
|
|
|
+ centerX -= (deltaW / 2) * cos(rotateAngle) + (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY -= (deltaW / 2) * sin(rotateAngle) - (deltaH / 2) * cos(rotateAngle)
|
|
|
+ } else {
|
|
|
+ // 右边固定
|
|
|
+ centerX -= (deltaW / 2) * cos(rotateAngle)
|
|
|
+ centerY -= (deltaW / 2) * sin(rotateAngle)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 'tl': {
|
|
|
+ deltaW = -deltaW
|
|
|
+ deltaH = -deltaH
|
|
|
+ const widthAndDeltaW = setWidthAndDeltaW(width, deltaW, minWidth)
|
|
|
+ width = widthAndDeltaW.width
|
|
|
+ deltaW = widthAndDeltaW.deltaW
|
|
|
+ const heightAndDeltaH = setHeightAndDeltaH(height, deltaH, minHeight)
|
|
|
+ height = heightAndDeltaH.height
|
|
|
+ deltaH = heightAndDeltaH.deltaH
|
|
|
+ if (ratio) {
|
|
|
+ width = height * ratio
|
|
|
+ deltaW = deltaH * ratio
|
|
|
+ }
|
|
|
+ centerX -= (deltaW / 2) * cos(rotateAngle) - (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY -= (deltaW / 2) * sin(rotateAngle) + (deltaH / 2) * cos(rotateAngle)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ case 'tc': {
|
|
|
+ deltaH = -deltaH
|
|
|
+ const heightAndDeltaH = setHeightAndDeltaH(height, deltaH, minHeight)
|
|
|
+ height = heightAndDeltaH.height
|
|
|
+ deltaH = heightAndDeltaH.deltaH
|
|
|
+ if (ratio) {
|
|
|
+ width = height * ratio
|
|
|
+ deltaW = deltaH * ratio
|
|
|
+ // 左下角固定
|
|
|
+ centerX += (deltaW / 2) * cos(rotateAngle) + (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY += (deltaW / 2) * sin(rotateAngle) - (deltaH / 2) * cos(rotateAngle)
|
|
|
+ } else {
|
|
|
+ centerX += (deltaH / 2) * sin(rotateAngle)
|
|
|
+ centerY -= (deltaH / 2) * cos(rotateAngle)
|
|
|
+ }
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ position: {
|
|
|
+ centerX,
|
|
|
+ centerY
|
|
|
+ },
|
|
|
+ size: {
|
|
|
+ width: width * widthFlag,
|
|
|
+ height: height * heightFlag
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 根据给定的参数设置高度和 deltaH 值。
|
|
|
+ *
|
|
|
+ * @param {number} height - 当前的高度值。
|
|
|
+ * @param {number} deltaH - 高度变化值。
|
|
|
+ * @param {number} minHeight - 最小高度值。
|
|
|
+ * @return {object} - 包含更新后的高度和 deltaH 值的对象。
|
|
|
+ */
|
|
|
+const setHeightAndDeltaH = (height: number, deltaH: number, minHeight: number) => {
|
|
|
+ const expectedHeight = height + deltaH
|
|
|
+ if (expectedHeight > minHeight) {
|
|
|
+ height = expectedHeight
|
|
|
+ } else {
|
|
|
+ deltaH = minHeight - height
|
|
|
+ height = minHeight
|
|
|
+ }
|
|
|
+ return { height, deltaH }
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 设置元素的宽度和deltaW值。
|
|
|
+ *
|
|
|
+ * @param {number} width - 元素的当前宽度。
|
|
|
+ * @param {number} deltaW - 元素宽度的变化量。
|
|
|
+ * @param {number} minWidth - 元素的最小宽度。
|
|
|
+ * @return {Object} - 包含更新后的宽度和deltaW值的对象。
|
|
|
+ */
|
|
|
+const setWidthAndDeltaW = (width: number, deltaW: number, minWidth: number) => {
|
|
|
+ const expectedWidth = width + deltaW
|
|
|
+ if (expectedWidth > minWidth) {
|
|
|
+ width = expectedWidth
|
|
|
+ } else {
|
|
|
+ deltaW = minWidth - width
|
|
|
+ width = minWidth
|
|
|
+ }
|
|
|
+ return { width, deltaW }
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 根据矩形的中心坐标、尺寸和角度计算左上角的位置。
|
|
|
+ *
|
|
|
+ * @param {object} params - 计算的参数。
|
|
|
+ * @param {number} params.centerX - 矩形的中心点的 x 坐标。
|
|
|
+ * @param {number} params.centerY - 矩形的中心点的 y 坐标。
|
|
|
+ * @param {number} params.width - 矩形的宽度。
|
|
|
+ * @param {number} params.height - 矩形的高度。
|
|
|
+ * @param {number} params.angle - 矩形的旋转角度。
|
|
|
+ * @return {object} - 矩形的左上角位置。
|
|
|
+ */
|
|
|
+export const centerToTL = ({
|
|
|
+ centerX,
|
|
|
+ centerY,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ angle
|
|
|
+}: any): IDzrPropsModelValue => ({
|
|
|
+ top: centerY - height / 2,
|
|
|
+ left: centerX - width / 2,
|
|
|
+ width,
|
|
|
+ height,
|
|
|
+ angle
|
|
|
+})
|
|
|
+/**
|
|
|
+ * 格式化数据并返回一个包含更新后尺寸和位置的对象。
|
|
|
+ *
|
|
|
+ * @param {IDzrPropsModelValue} data - 包含宽度和高度的数据。
|
|
|
+ * @param {number} centerX - 中心点的x坐标。
|
|
|
+ * @param {number} centerY - 中心点的y坐标。
|
|
|
+ * @return {object} - 一个包含更新后尺寸和位置的对象。
|
|
|
+ */
|
|
|
+export const formatData = (data: IDzrPropsModelValue, centerX: number, centerY: number) => {
|
|
|
+ const { width, height } = data
|
|
|
+ return {
|
|
|
+ width: Math.abs(width),
|
|
|
+ height: Math.abs(height),
|
|
|
+ left: centerX - Math.abs(width) / 2,
|
|
|
+ top: centerY - Math.abs(height) / 2
|
|
|
+ }
|
|
|
+}
|
|
|
+/**
|
|
|
+ * 生成随机字符串
|
|
|
+ * @param len 生成个数
|
|
|
+ */
|
|
|
+export const randomString = (len?: number) => {
|
|
|
+ len = len || 10
|
|
|
+ const str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
|
|
|
+ const maxPos = str.length
|
|
|
+ let random_str = ''
|
|
|
+ for (let i = 0; i < len; i++) {
|
|
|
+ random_str += str.charAt(Math.floor(Math.random() * maxPos))
|
|
|
+ }
|
|
|
+ return random_str
|
|
|
+}
|