You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
vue-webtopo-svgeditor/src/utils/index.ts

279 lines
8.2 KiB
TypeScript

import { ELineBindAnchors } from '@/config-center/system/types';
import { EDoneJsonType, IConfigItem } from '@/config-center/types';
import type { IDoneJson } from '@/store/global/types';
/**
* 生成随机字符串
* @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;
};
// 通过泛型定义通用类型保护函数
export const isOfType = <T>(target: unknown, prop: keyof T): target is T => {
return (target as T)[prop] !== undefined;
};
/**
* 获取坐标偏移量
* @param length 真实宽/高
* @param scale 缩放倍数
* @returns 坐标偏移量
*/
export const getCoordinateOffset = (length: number, scale: number) => {
return (length / 2) * (scale - 1);
};
// 角度转弧度
// Math.PI = 180 度
export const angleToRadian = (angle: number) => {
return (angle * Math.PI) / 180;
};
/**
* 计算根据圆心旋转后的点的坐标
* @param {Object} point 旋转前的点坐标
* @param {Object} center 旋转中心
* @param {Number} rotate 旋转的角度
* @return {Object} 旋转后的坐标
* https://www.zhihu.com/question/67425734/answer/252724399 旋转矩阵公式
*/
export const calculateRotatedPointCoordinate = (
point: { x: number; y: number },
center: { x: number; y: number },
rotate: number
) => {
/**
* 旋转公式:
* 点a(x, y)
* 旋转中心c(x, y)
* 旋转后点n(x, y)
* 旋转角度θ tan ??
* nx = cosθ * (ax - cx) - sinθ * (ay - cy) + cx
* ny = sinθ * (ax - cx) + cosθ * (ay - cy) + cy
*/
return {
x:
(point.x - center.x) * Math.cos(angleToRadian(rotate)) -
(point.y - center.y) * Math.sin(angleToRadian(rotate)) +
center.x,
y:
(point.x - center.x) * Math.sin(angleToRadian(rotate)) +
(point.y - center.y) * Math.cos(angleToRadian(rotate)) +
center.y
};
};
// 求两点之间的中点坐标
export const getCenterPoint = (p1: { x: number; y: number }, p2: { x: number; y: number }) => {
return {
x: p1.x + (p2.x - p1.x) / 2,
y: p1.y + (p2.y - p1.y) / 2
};
};
/**
* 坐标数组转换成path路径
* @param position_arr
* @returns
*/
export const positionArrarToPath = (position_arr: { x: number; y: number }[]) => {
let path_str = '';
for (let index = 0; index < position_arr.length; index++) {
if (index === 0) {
path_str += `M ${position_arr[index].x} ${position_arr[index].y}`;
} else {
path_str += ` L ${position_arr[index].x} ${position_arr[index].y}`;
}
}
return path_str;
};
/**
* 获取相对于svg最新的坐标
* @param init_pos 相对于页面的初始坐标
* @param finally_pos 相对于页面的最终坐标
* @param svg_init_pos 相对于svg的初始坐标
* @returns svg最新的坐标
*/
export const getSvgNowPosition = (init_pos: number, finally_pos: number, svg_init_pos: number) => {
return svg_init_pos + (finally_pos - init_pos);
};
/**
* 对象深拷贝
* @param object
* @param default_val
* @returns
*/
export const objectDeepClone = <T>(object: object, default_val: any = {}) => {
if (!object) {
return default_val as T;
}
return JSON.parse(JSON.stringify(object)) as T;
};
/**
* 设置实际的属性
* @param done_json
*/
export const setSvgActualInfo = (done_json: IDoneJson) => {
const queryBbox = document.querySelector(`#${done_json.id}`);
// const rectBBox = document.querySelector(`#rect${done_json.id}`);
if (queryBbox) {
let x = 0,
y = 0,
width = 0,
height = 0;
if (done_json.type !== EDoneJsonType.Vue) {
const BBox = (queryBbox as SVGGraphicsElement).getBBox();
x = BBox.x;
y = BBox.y;
(width = BBox.width), (height = BBox.height);
} else {
(width = (queryBbox as HTMLElement).offsetWidth),
(height = (queryBbox as HTMLElement).offsetHeight);
x = 50 - width / 2;
y = 50 - height / 2;
const foreignObjectBox = document.querySelector(`#foreign-object${done_json.id}`);
if (foreignObjectBox) {
foreignObjectBox.setAttribute('x', x.toString());
foreignObjectBox.setAttribute('y', y.toString());
foreignObjectBox.setAttribute('width', width.toString());
foreignObjectBox.setAttribute('height', height.toString());
}
}
// rectBBox.setAttribute('x', x.toString());
// rectBBox.setAttribute('y', y.toString());
// rectBBox.setAttribute('width', width.toString());
// rectBBox.setAttribute('height', height.toString());
done_json.actual_bound = { x, y, width, height };
done_json.point_coordinate.tl = {
x: done_json.x - (width * done_json.scale_x) / 2,
y: done_json.y - (height * done_json.scale_y) / 2
};
done_json.point_coordinate.tc = {
x: done_json.x,
y: done_json.y - (height * done_json.scale_y) / 2
};
done_json.point_coordinate.tr = {
x: done_json.x + (width * done_json.scale_x) / 2,
y: done_json.y - (height * done_json.scale_y) / 2
};
done_json.point_coordinate.l = {
x: done_json.x - (width * done_json.scale_x) / 2,
y: done_json.y
};
done_json.point_coordinate.r = {
x: done_json.x + (width * done_json.scale_x) / 2,
y: done_json.y
};
done_json.point_coordinate.bl = {
x: done_json.x - (width * done_json.scale_x) / 2,
y: done_json.y + (height * done_json.scale_y) / 2
};
done_json.point_coordinate.bc = {
x: done_json.x,
y: done_json.y + (height * done_json.scale_y) / 2
};
done_json.point_coordinate.br = {
x: done_json.x + (width * done_json.scale_x) / 2,
y: done_json.y + (height * done_json.scale_y) / 2
};
if (done_json.rotate !== 0) {
setAfterRotationPointCoordinate(done_json);
}
}
};
/**
* 根据锚点类型获取锚点坐标
* @param anchor_type
* @param done_json
* @returns
*/
export const getAnchorPosByAnchorType = (anchor_type: ELineBindAnchors, done_json: IDoneJson) => {
if (anchor_type === ELineBindAnchors.BottomCenter) {
return done_json.point_coordinate.bc;
} else if (anchor_type === ELineBindAnchors.Left) {
return done_json.point_coordinate.l;
} else if (anchor_type === ELineBindAnchors.Right) {
return done_json.point_coordinate.r;
} else {
return done_json.point_coordinate.tc;
}
};
/**
* 旋转之后重新设置组件八点坐标
* @param item
*/
export const setAfterRotationPointCoordinate = (item: IDoneJson) => {
item.point_coordinate = {
tl: calculateRotatedPointCoordinate(
item.point_coordinate.tl,
{ x: item.x, y: item.y },
item.rotate
),
tc: calculateRotatedPointCoordinate(
item.point_coordinate.tc,
{ x: item.x, y: item.y },
item.rotate
),
tr: calculateRotatedPointCoordinate(
item.point_coordinate.tr,
{ x: item.x, y: item.y },
item.rotate
),
l: calculateRotatedPointCoordinate(
item.point_coordinate.l,
{ x: item.x, y: item.y },
item.rotate
),
r: calculateRotatedPointCoordinate(
item.point_coordinate.r,
{ x: item.x, y: item.y },
item.rotate
),
bl: calculateRotatedPointCoordinate(
item.point_coordinate.bl,
{ x: item.x, y: item.y },
item.rotate
),
bc: calculateRotatedPointCoordinate(
item.point_coordinate.bc,
{ x: item.x, y: item.y },
item.rotate
),
br: calculateRotatedPointCoordinate(
item.point_coordinate.br,
{ x: item.x, y: item.y },
item.rotate
)
};
};
export const prosToVBind = (item: IConfigItem) => {
let temp = {};
if (item.state) {
for (const key in item.state) {
if (key === 'OnOff') {
for (const on_off_key in item.state[key]?.props) {
temp = {
...temp,
...{
[on_off_key]: item.state[key]?.default
? item.state[key]?.props[on_off_key].openVal
: item.state[key]?.props[on_off_key].closeVal
}
};
}
}
}
}
for (const key in item.props) {
temp = { ...temp, ...{ [key]: item.props[key].val } };
}
return temp;
};