feat: 不完美的旋转缩放

Re-1.0
咬轮猫 3 years ago
parent 473b947c73
commit e0d7bf0f6d

@ -105,7 +105,17 @@
IDoneJson
} from '../../../../store/global/types';
import { useSvgEditLayoutStore } from '../../../../store/svgedit-layout';
import { randomString } from '../../../../utils';
import { getCenterPoint, randomString } from '../../../../utils';
import {
calculateBottom,
calculateLeft,
calculateLeftBottom,
calculateLeftTop,
calculateRight,
calculateRightBottom,
calculateRightTop,
calculateTop
} from '../../../../utils/scale-core';
import HandlePanel from '../handle-panel/index.vue';
// import HandlePanel from '../handle-panel/index.vue';
const globalStore = useGlobalStore();
@ -159,6 +169,8 @@
e.preventDefault();
};
const onSvgMouseDown = (select_item: IDoneJson, index: number, e: MouseEvent) => {
console.log(172, e);
e.preventDefault();
e.cancelBubble = true;
//
@ -192,62 +204,144 @@
//
globalStore.handle_svg_info.info.x = globalStore.mouse_info.new_position_x;
globalStore.handle_svg_info.info.y = globalStore.mouse_info.new_position_y;
globalStore.handle_svg_info.info.client = {
x: clientX,
y: clientY
};
globalStore.intention = EGlobalStoreIntention.Move;
} else if (globalStore.intention == EGlobalStoreIntention.MoveCanvas) {
//
svgEditLayoutStore.center_offset.x = globalStore.mouse_info.new_position_x;
svgEditLayoutStore.center_offset.y = globalStore.mouse_info.new_position_y;
} else if (globalStore.intention === EGlobalStoreIntention.Zoom) {
if (!globalStore.handle_svg_info) {
return;
}
//
const curPositon = {
x: e.clientX,
y: e.clientY
};
let new_length = {
width: 0,
height: 0,
is_old_width: false,
is_old_height: false
};
if (globalStore.scale_info.type === EScaleInfoType.TopLeft) {
new_length = calculateLeftTop(
curPositon,
globalStore.scale_info.symmetric_point,
globalStore.handle_svg_info.info.rotate
);
} else if (globalStore.scale_info.type === EScaleInfoType.TopRight) {
new_length = calculateRightTop(
curPositon,
globalStore.scale_info.symmetric_point,
globalStore.handle_svg_info.info.rotate
);
} else if (globalStore.scale_info.type === EScaleInfoType.BottomRight) {
new_length = calculateRightBottom(
curPositon,
globalStore.scale_info.symmetric_point,
globalStore.handle_svg_info.info.rotate
);
} else if (globalStore.scale_info.type === EScaleInfoType.BottomLeft) {
new_length = calculateLeftBottom(
curPositon,
globalStore.scale_info.symmetric_point,
globalStore.handle_svg_info.info.rotate
);
} else if (globalStore.scale_info.type === EScaleInfoType.TopCenter) {
new_length = calculateTop(
curPositon,
globalStore.scale_info.symmetric_point,
globalStore.handle_svg_info.info.rotate,
globalStore.handle_svg_info.info.client
);
} else if (globalStore.scale_info.type === EScaleInfoType.Right) {
new_length = calculateRight(
curPositon,
globalStore.scale_info.symmetric_point,
globalStore.handle_svg_info.info.rotate,
globalStore.handle_svg_info.info.client
);
} else if (globalStore.scale_info.type === EScaleInfoType.BottomCenter) {
new_length = calculateBottom(
curPositon,
globalStore.scale_info.symmetric_point,
globalStore.handle_svg_info.info.rotate,
globalStore.handle_svg_info.info.client
);
} else if (globalStore.scale_info.type === EScaleInfoType.Left) {
new_length = calculateLeft(
curPositon,
globalStore.scale_info.symmetric_point,
globalStore.handle_svg_info.info.rotate,
globalStore.handle_svg_info.info.client
);
}
console.log('坐标', globalStore.scale_info.symmetric_point, curPositon, new_length);
//
const move_length_x =
globalStore.scale_info.type === EScaleInfoType.TopLeft ||
globalStore.scale_info.type === EScaleInfoType.Left ||
globalStore.scale_info.type === EScaleInfoType.BottomLeft
? globalStore.mouse_info.new_position_x - globalStore.mouse_info.now_position_x
: globalStore.scale_info.type === EScaleInfoType.TopRight ||
globalStore.scale_info.type === EScaleInfoType.Right ||
globalStore.scale_info.type === EScaleInfoType.BottomRight
? globalStore.mouse_info.now_position_x - globalStore.mouse_info.new_position_x
: 0;
const move_length_y =
globalStore.scale_info.type === EScaleInfoType.TopLeft ||
globalStore.scale_info.type === EScaleInfoType.TopCenter ||
globalStore.scale_info.type === EScaleInfoType.TopRight
? globalStore.mouse_info.new_position_y - globalStore.mouse_info.now_position_y
: globalStore.scale_info.type === EScaleInfoType.BottomLeft ||
globalStore.scale_info.type === EScaleInfoType.BottomCenter ||
globalStore.scale_info.type === EScaleInfoType.BottomRight
? globalStore.mouse_info.now_position_y - globalStore.mouse_info.new_position_y
: 0;
// const move_length_x =
// globalStore.scale_info.type === EScaleInfoType.TopLeft ||
// globalStore.scale_info.type === EScaleInfoType.Left ||
// globalStore.scale_info.type === EScaleInfoType.BottomLeft
// ? -(newTopLeftPoint.x - globalStore.mouse_info.now_position_x)
// : globalStore.scale_info.type === EScaleInfoType.TopRight ||
// globalStore.scale_info.type === EScaleInfoType.Right ||
// globalStore.scale_info.type === EScaleInfoType.BottomRight
// ? globalStore.mouse_info.now_position_x - newTopLeftPoint.x
// : 0;
// const move_length_y =
// globalStore.scale_info.type === EScaleInfoType.TopLeft ||
// globalStore.scale_info.type === EScaleInfoType.TopCenter ||
// globalStore.scale_info.type === EScaleInfoType.TopRight
// ? newTopLeftPoint.y - globalStore.mouse_info.now_position_y
// : globalStore.scale_info.type === EScaleInfoType.BottomLeft ||
// globalStore.scale_info.type === EScaleInfoType.BottomCenter ||
// globalStore.scale_info.type === EScaleInfoType.BottomRight
// ? globalStore.mouse_info.now_position_y - newTopLeftPoint.y
// : 0;
//
if (globalStore.handle_svg_info) {
const scale_x =
if (globalStore.handle_svg_info && new_length.width > 0 && new_length.height > 0) {
const scale_x = !new_length.is_old_width
? new_length.width /
(globalStore.handle_svg_info.info.actual_bound.width *
globalStore.scale_info.scale_times.x -
move_length_x) /
globalStore.handle_svg_info.info.actual_bound.width;
const scale_y =
globalStore.scale_info.scale_times.x)
: 1;
const scale_y = !new_length.is_old_height
? new_length.height /
(globalStore.handle_svg_info.info.actual_bound.height *
globalStore.scale_info.scale_times.y -
move_length_y) /
globalStore.handle_svg_info.info.actual_bound.height;
globalStore.scale_info.scale_times.y)
: 1;
// const move_length_x =
// new_length.width -
// globalStore.handle_svg_info.info.actual_bound.width *
// globalStore.scale_info.scale_times.x;
// const move_length_y =
// new_length.height -
// globalStore.handle_svg_info.info.actual_bound.height *
// globalStore.scale_info.scale_times.y;
if (scale_x > 0) {
globalStore.handle_svg_info.info.scale_x = scale_x;
globalStore.handle_svg_info.info.x =
globalStore.scale_info.type === EScaleInfoType.TopLeft ||
globalStore.scale_info.type === EScaleInfoType.Left ||
globalStore.scale_info.type === EScaleInfoType.BottomLeft
? globalStore.scale_info.scale_item_info.x + move_length_x / 2
: globalStore.scale_info.scale_item_info.x - move_length_x / 2;
//沿
// globalStore.handle_svg_info.info.x =
// globalStore.scale_info.type === EScaleInfoType.TopLeft ||
// globalStore.scale_info.type === EScaleInfoType.Left ||
// globalStore.scale_info.type === EScaleInfoType.BottomLeft
// ? globalStore.scale_info.scale_item_info.x - move_length_x / 2
// : globalStore.scale_info.scale_item_info.x + move_length_x / 2;
}
if (scale_y > 0) {
globalStore.handle_svg_info.info.scale_y = scale_y;
globalStore.handle_svg_info.info.y =
globalStore.scale_info.type === EScaleInfoType.TopLeft ||
globalStore.scale_info.type === EScaleInfoType.TopCenter ||
globalStore.scale_info.type === EScaleInfoType.TopRight
? globalStore.scale_info.scale_item_info.y + move_length_y / 2
: globalStore.scale_info.scale_item_info.y - move_length_y / 2;
// globalStore.handle_svg_info.info.y =
// globalStore.scale_info.type === EScaleInfoType.TopLeft ||
// globalStore.scale_info.type === EScaleInfoType.TopCenter ||
// globalStore.scale_info.type === EScaleInfoType.TopRight
// ? globalStore.scale_info.scale_item_info.y - move_length_y / 2
// : globalStore.scale_info.scale_item_info.y + move_length_y / 2;
}
}
} else if (globalStore.intention === EGlobalStoreIntention.Rotate) {
@ -270,7 +364,7 @@
globalStore.rotate_info.angle + rotateDegreeAfter - rotateDegreeBefore;
}
};
const onCanvasMouseUp = () => {
const onCanvasMouseUp = (e: MouseEvent) => {
//
if (globalStore.mouse_info.state != EMouseInfoState.Down) {
return;
@ -283,6 +377,19 @@
globalStore.setDoneJson(globalStore.done_json);
globalStore.intention = EGlobalStoreIntention.Select;
// globalStore.setHandleSvgInfo(undefined, 0);
} else if (
globalStore.handle_svg_info?.info &&
globalStore.intention == EGlobalStoreIntention.Zoom
) {
//
const newCenterPoint = getCenterPoint(
{ x: e.clientX, y: e.clientY },
globalStore.scale_info.symmetric_point
);
//bug
console.log(newCenterPoint);
globalStore.handle_svg_info.info.client = newCenterPoint;
globalStore.intention = EGlobalStoreIntention.None;
} else if (globalStore.intention != EGlobalStoreIntention.Select) {
globalStore.intention = EGlobalStoreIntention.None;
}

@ -163,6 +163,15 @@
scale_item_info: {
x: props.itemInfo.x,
y: props.itemInfo.y
},
symmetric_point: {
x:
props.itemInfo.client.x +
Math.abs(clientX - props.itemInfo.client.x) *
(clientX < props.itemInfo.client.x ? 1 : -1),
y:
props.itemInfo.client.y +
Math.abs(clientY - props.itemInfo.client.y) * (clientY < props.itemInfo.client.y ? 1 : -1)
}
});
};

@ -41,6 +41,10 @@ export const useGlobalStore = defineStore('global-store', {
scale_item_info: {
x: 0,
y: 0
},
symmetric_point: {
x: 0,
y: 0
}
},
rotate_info: {

@ -68,6 +68,11 @@ export interface IScaleInfo {
x: number;
y: number;
};
symmetric_point: {
//缩放前缩放手柄对应组件中心坐标的对称点坐标
x: number;
y: number;
};
}
/**
*

@ -26,3 +26,49 @@ export const isOfType = <T>(target: unknown, prop: keyof T): target is T => {
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
};
};

@ -0,0 +1,225 @@
import { calculateRotatedPointCoordinate, getCenterPoint } from '.';
import { IScalePoint } from './types';
/**
*
* @param curPositon
* @param symmetricPoint
* @param rotate
* @returns
*/
export const calculateLeftTop = (
curPositon: IScalePoint,
symmetricPoint: IScalePoint,
rotate: number
) => {
//新的中心点坐标
const newCenterPoint = getCenterPoint(curPositon, symmetricPoint);
console.log('left中心坐标', newCenterPoint);
const newTopLeftPoint = calculateRotatedPointCoordinate(curPositon, newCenterPoint, -rotate);
const newBottomRightPoint = calculateRotatedPointCoordinate(
symmetricPoint,
newCenterPoint,
-rotate
);
return {
width: newBottomRightPoint.x - newTopLeftPoint.x,
height: newBottomRightPoint.y - newTopLeftPoint.y,
is_old_width: false,
is_old_height: false
};
};
/**
*
* @param curPositon
* @param symmetricPoint
* @param rotate
* @returns
*/
export const calculateRightTop = (
curPositon: IScalePoint,
symmetricPoint: IScalePoint,
rotate: number
) => {
const newCenterPoint = getCenterPoint(curPositon, symmetricPoint);
const newTopRightPoint = calculateRotatedPointCoordinate(curPositon, newCenterPoint, -rotate);
const newBottomLeftPoint = calculateRotatedPointCoordinate(
symmetricPoint,
newCenterPoint,
-rotate
);
return {
width: newTopRightPoint.x - newBottomLeftPoint.x,
height: newBottomLeftPoint.y - newTopRightPoint.y,
is_old_width: false,
is_old_height: false
};
};
/**
*
* @param curPositon
* @param symmetricPoint
* @param rotate
* @returns
*/
export const calculateRightBottom = (
curPositon: IScalePoint,
symmetricPoint: IScalePoint,
rotate: number
) => {
const newCenterPoint = getCenterPoint(curPositon, symmetricPoint);
const newTopLeftPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -rotate);
const newBottomRightPoint = calculateRotatedPointCoordinate(curPositon, newCenterPoint, -rotate);
return {
width: newBottomRightPoint.x - newTopLeftPoint.x,
height: newBottomRightPoint.y - newTopLeftPoint.y,
is_old_width: false,
is_old_height: false
};
};
/**
*
* @param curPositon
* @param symmetricPoint
* @param rotate
* @returns
*/
export const calculateLeftBottom = (
curPositon: IScalePoint,
symmetricPoint: IScalePoint,
rotate: number
) => {
const newCenterPoint = getCenterPoint(curPositon, symmetricPoint);
const newTopRightPoint = calculateRotatedPointCoordinate(symmetricPoint, newCenterPoint, -rotate);
const newBottomLeftPoint = calculateRotatedPointCoordinate(curPositon, newCenterPoint, -rotate);
return {
width: newTopRightPoint.x - newBottomLeftPoint.x,
height: newBottomLeftPoint.y - newTopRightPoint.y,
is_old_width: false,
is_old_height: false
};
};
export const calculateTop = (
curPositon: IScalePoint,
symmetricPoint: IScalePoint,
rotate: number,
curPoint: IScalePoint
) => {
// 由于用户拉伸时是以任意角度拉伸的,所以在求得旋转前的坐标时,只取 y 坐标(这里的 x 坐标可能是任意值x 坐标用 curPoint 的。
// 这个中心点(第二个参数)用 curPoint, center, symmetricPoint 都可以,只要他们在一条直线上就行
const rotatedcurPositon = calculateRotatedPointCoordinate(curPositon, curPoint, -rotate);
console.log('top中心坐标', rotatedcurPositon);
// 算出旋转前 y 坐标,再用 curPoint 的 x 坐标,重新计算它们旋转后对应的坐标
const rotatedTopMiddlePoint = calculateRotatedPointCoordinate(
{
x: curPoint.x,
y: rotatedcurPositon.y
},
curPoint,
rotate
);
// 用旋转后的坐标和对称点算出新的高度(勾股定理)
const newHeight = Math.sqrt(
(rotatedTopMiddlePoint.x - symmetricPoint.x) ** 2 +
(rotatedTopMiddlePoint.y - symmetricPoint.y) ** 2
);
return {
width: 1,
height: Math.round(newHeight),
is_old_width: true,
is_old_height: false
};
};
export const calculateRight = (
curPositon: IScalePoint,
symmetricPoint: IScalePoint,
rotate: number,
curPoint: IScalePoint
) => {
const rotatedcurPositon = calculateRotatedPointCoordinate(curPositon, curPoint, -rotate);
const rotatedRightMiddlePoint = calculateRotatedPointCoordinate(
{
x: rotatedcurPositon.x,
y: curPoint.y
},
curPoint,
rotate
);
const newWidth = Math.sqrt(
(rotatedRightMiddlePoint.x - symmetricPoint.x) ** 2 +
(rotatedRightMiddlePoint.y - symmetricPoint.y) ** 2
);
return {
width: Math.round(newWidth),
height: 1,
is_old_width: false,
is_old_height: true
};
};
export const calculateBottom = (
curPositon: IScalePoint,
symmetricPoint: IScalePoint,
rotate: number,
curPoint: IScalePoint
) => {
const rotatedcurPositon = calculateRotatedPointCoordinate(curPositon, curPoint, -rotate);
const rotatedBottomMiddlePoint = calculateRotatedPointCoordinate(
{
x: curPoint.x,
y: rotatedcurPositon.y
},
curPoint,
rotate
);
const newHeight = Math.sqrt(
(rotatedBottomMiddlePoint.x - symmetricPoint.x) ** 2 +
(rotatedBottomMiddlePoint.y - symmetricPoint.y) ** 2
);
return {
width: 1,
height: Math.round(newHeight),
is_old_width: true,
is_old_height: false
};
};
export const calculateLeft = (
curPositon: IScalePoint,
symmetricPoint: IScalePoint,
rotate: number,
curPoint: IScalePoint
) => {
const rotatedcurPositon = calculateRotatedPointCoordinate(curPositon, curPoint, -rotate);
const rotatedLeftMiddlePoint = calculateRotatedPointCoordinate(
{
x: rotatedcurPositon.x,
y: curPoint.y
},
curPoint,
rotate
);
const newWidth = Math.sqrt(
(rotatedLeftMiddlePoint.x - symmetricPoint.x) ** 2 +
(rotatedLeftMiddlePoint.y - symmetricPoint.y) ** 2
);
return {
width: Math.round(newWidth),
height: 1,
is_old_width: false,
is_old_height: true
};
};

@ -0,0 +1,4 @@
export interface IScalePoint {
x: number;
y: number;
}
Loading…
Cancel
Save