|
@@ -5,7 +5,9 @@ import { useEditor } from "../../../..";
|
|
|
import { View } from "../View";
|
|
|
import { CompUI } from "../..";
|
|
|
import { values } from "lodash";
|
|
|
-import { Angle } from "@/modules/editor/controllers/SelectCtrl/objects/mathUtils";
|
|
|
+import { Angle, VectorLenth } from "@/modules/editor/controllers/SelectCtrl/objects/mathUtils";
|
|
|
+import { CompObject } from "@/modules/editor/controllers/SelectCtrl/compObj";
|
|
|
+import { ObjsContainer } from "@/modules/editor/controllers/SelectCtrl/ObjsContainer";
|
|
|
|
|
|
function findNearestPoint(points: number[][], w:number, h:number, sx:number, sy:number) {
|
|
|
const n = points.length;
|
|
@@ -48,6 +50,113 @@ function findNearestPoint(points: number[][], w:number, h:number, sx:number, sy:
|
|
|
return {index: minIndex};
|
|
|
}
|
|
|
|
|
|
+function normalize(v1:{x:number,y:number}) {
|
|
|
+ let x = v1.x;
|
|
|
+ let y = v1.y;
|
|
|
+ const s = Math.sqrt(x*x + y*y);
|
|
|
+ v1.x = x / s;
|
|
|
+ v1.y = y / s;
|
|
|
+ return v1;
|
|
|
+}
|
|
|
+
|
|
|
+type PointItem = {
|
|
|
+ x: number;
|
|
|
+ y: number;
|
|
|
+ cx: number;
|
|
|
+ cy: number;
|
|
|
+
|
|
|
+ preLineX: number; //界面显示控制点
|
|
|
+ preLineY: number; //界面显示控制点
|
|
|
+ aftLineX: number; //界面显示控制点
|
|
|
+ aftLineY: number; //界面显示控制点
|
|
|
+
|
|
|
+ preCirleX: number; //界面显示控制点
|
|
|
+ preCirleY: number; //界面显示控制点
|
|
|
+
|
|
|
+ aftCirleX: number; //界面显示控制点
|
|
|
+ aftCirleY: number; //界面显示控制点
|
|
|
+
|
|
|
+ isLine: boolean;
|
|
|
+}
|
|
|
+
|
|
|
+function parsePoints( points:number[][], width:number, height:number) {
|
|
|
+ let out :PointItem[] = [];
|
|
|
+ let n = points.length;
|
|
|
+ for(let i=0; i<n; i++) {
|
|
|
+ const p = points[i]
|
|
|
+
|
|
|
+ const x = p[0]*width, y = p[1]*height;
|
|
|
+
|
|
|
+ //绘制控制点的位置
|
|
|
+ const cx = width * p[2];
|
|
|
+ const cy = height *p[3];
|
|
|
+
|
|
|
+ let v = {x: cx - x, y: cy-y};
|
|
|
+ v = normalize(v);
|
|
|
+
|
|
|
+ let slen = 90;
|
|
|
+ const preCirleX = x -v.x * slen, preCirleY = y-v.y *slen;
|
|
|
+ const aftCirleX = x +v.x *slen, aftCirleY = y+v.y*slen;
|
|
|
+
|
|
|
+ slen = 80;
|
|
|
+
|
|
|
+ const preLineX = x -v.x * slen, preLineY = y-v.y *slen;
|
|
|
+ const aftLineX = x +v.x *slen, aftLineY = y+v.y*slen;
|
|
|
+
|
|
|
+
|
|
|
+ out.push({x, y, cx, cy, isLine: p[4] != undefined,
|
|
|
+ preCirleX,preCirleY, aftCirleX, aftCirleY,
|
|
|
+ preLineX, preLineY, aftLineX, aftLineY});
|
|
|
+
|
|
|
+ }
|
|
|
+ return out;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function clickTest(points:PointItem[], e:MouseEvent, pxToDesignSize:any,) {
|
|
|
+ const x = pxToDesignSize(e.offsetX )
|
|
|
+ const y = pxToDesignSize(e.offsetY)
|
|
|
+ let n = points.length;
|
|
|
+ let pIndex = -1;
|
|
|
+ let clickCurve = false;
|
|
|
+ let clickLine = false;
|
|
|
+ let moving = false;
|
|
|
+
|
|
|
+ while(n--) {
|
|
|
+ const p = points[n];
|
|
|
+ let px = p.x;
|
|
|
+ let py = p.y;
|
|
|
+
|
|
|
+ const rect = 20
|
|
|
+
|
|
|
+ if(!(x < (px -rect) || x > (px + rect) || y < (py -rect) || y > (py+rect) ) ) {
|
|
|
+ pIndex = n;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ if(!(x < (p.aftCirleX -rect) || x > (p.aftCirleX + rect) || y < (p.aftCirleY -rect) || y > (p.aftCirleY+rect) ) ) {
|
|
|
+ pIndex = n;
|
|
|
+ clickCurve = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ if(!(x < (p.preLineX -rect) || x > (p.preLineX + rect) || y < (p.preLineY -rect) || y > (p.preLineY+rect) ) ) {
|
|
|
+ pIndex = n;
|
|
|
+ clickLine = true;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (pIndex == -1) {
|
|
|
+ //ming
|
|
|
+ const dta = 5
|
|
|
+ if(!(x < (20 - dta) || x > (20 + dta) || y < (20 -dta) || y > (20 + dta) ) ) {
|
|
|
+ moving = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {clickLine, clickCurve, pIndex, moving, x, y};
|
|
|
+}
|
|
|
+
|
|
|
export const Component = defineComponent({
|
|
|
props: {
|
|
|
compId: string().isRequired,
|
|
@@ -58,8 +167,6 @@ export const Component = defineComponent({
|
|
|
const canvasRef = ref<HTMLCanvasElement>();
|
|
|
onMounted(()=>{
|
|
|
|
|
|
- // draw();
|
|
|
- let isDragingIndex = -1;
|
|
|
canvasRef.value?.addEventListener("dblclick", function(e:MouseEvent){
|
|
|
|
|
|
const x = helper.pxToDesignSize(e.offsetX )
|
|
@@ -74,12 +181,18 @@ export const Component = defineComponent({
|
|
|
console.log("dbclick=>xxxxx", ret);
|
|
|
if (ret.clicked) {//点击删除
|
|
|
points.splice(ret.index, 1);
|
|
|
- if (points.length < 4) {
|
|
|
+ if (points.length < 2) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
- points.splice(ret.index+1, 0, [x / w, y / h]);
|
|
|
+ let next = ret.index + 1;
|
|
|
+ if (next >= points.length ) {
|
|
|
+ next = 0;
|
|
|
+ }
|
|
|
+ //计算
|
|
|
+ const x1 = x / w, y1= y / h;
|
|
|
+ points.splice(ret.index+1, 0, [x1, y1 , (x1 + points[next][0])/ 2.0 , (y1 + points[next][1]) / 2.0 ]);
|
|
|
}
|
|
|
|
|
|
draw();
|
|
@@ -89,69 +202,101 @@ export const Component = defineComponent({
|
|
|
if (store.currCompId != props.compId) return;
|
|
|
|
|
|
const el = canvasRef.value as HTMLCanvasElement;
|
|
|
- const x = helper.pxToDesignSize(e.offsetX )
|
|
|
- const y = helper.pxToDesignSize(e.offsetY)
|
|
|
-
|
|
|
- console.log(x , y);
|
|
|
-
|
|
|
- const points = data.value.points as number[][];
|
|
|
const width = data.layout.size[0];
|
|
|
const height = data.layout.size[1];
|
|
|
+ const ps = parsePoints(data.value.points as number[][], width, height)
|
|
|
+ const ret = clickTest(ps, e, helper.pxToDesignSize);
|
|
|
+ if (ret.pIndex == -1 && !ret.moving) return;
|
|
|
|
|
|
- let n = points.length;
|
|
|
- isDragingIndex = -1;
|
|
|
- let initX = 0;
|
|
|
- let initY = 0;
|
|
|
-
|
|
|
- while(n--) {
|
|
|
- const p = points[n];
|
|
|
+ const dragingX = ret.x;
|
|
|
+ const dragingY = ret.y;
|
|
|
|
|
|
- const px = width * p[0];
|
|
|
- const py = height * p[1];
|
|
|
-
|
|
|
- if(!(x < (px -10) || x > (px + 10) || y < (py -10) || y > (py+10) ) ) {
|
|
|
- isDragingIndex = n;
|
|
|
- initX = p[0]
|
|
|
- initY = p[1]
|
|
|
- break;
|
|
|
- }
|
|
|
+ const points = data.value.points as number[][];
|
|
|
+ const initValues = ret.pIndex != -1 ? points[ret.pIndex].slice(0) : {} as any;
|
|
|
+ const initPoint = ret.pIndex != -1 ? {...ps[ret.pIndex]} : {} as any;
|
|
|
+ const initPoints = points.map(item=>item.slice(0));
|
|
|
|
|
|
- }
|
|
|
+ e.preventDefault();
|
|
|
+ e.stopPropagation();
|
|
|
|
|
|
- const dragingX = x;
|
|
|
- const dragingY = y;
|
|
|
+ const isDragingIndex = ret.pIndex;
|
|
|
+
|
|
|
+ const box = controls.selectCtrl.getCurrCardBox();
|
|
|
|
|
|
- if (isDragingIndex != -1) {
|
|
|
+ const mvingPoint = !ret.clickCurve && !ret.clickLine
|
|
|
+ let initControlpLen :number | undefined = undefined;
|
|
|
+
|
|
|
+ const move = function (e:MouseEvent){
|
|
|
e.preventDefault();
|
|
|
e.stopPropagation();
|
|
|
|
|
|
- const move = function (e:MouseEvent){
|
|
|
- if ( isDragingIndex == -1) return;
|
|
|
-
|
|
|
- const offx = (helper.pxToDesignSize(e.offsetX ) - dragingX) / width;
|
|
|
- const offy = (helper.pxToDesignSize(e.offsetY ) - dragingY) / height;
|
|
|
-
|
|
|
- points[isDragingIndex][0] = initX + offx;
|
|
|
- points[isDragingIndex][1] = initY + offy;
|
|
|
+ const obj = controls.selectCtrl.objContainer as ObjsContainer;
|
|
|
+ const cardX = e.clientX - box.left, cardY = e.clientY - box.top;
|
|
|
+ const p = {x: cardX, y:cardY} as any;
|
|
|
+ obj.parent.worldTransform.applyInverse(p, p);
|
|
|
+ obj.parent.updateTransform();
|
|
|
|
|
|
+ const x = helper.pxToDesignSize(p.x), y = helper.pxToDesignSize(p.y);
|
|
|
+ const offx = ( x - dragingX) / width;
|
|
|
+ const offy = ( y - dragingY) / height;
|
|
|
+
|
|
|
+ if (ret.moving) {//移动所有
|
|
|
+ points.forEach((p,i)=>{
|
|
|
+ p[0] = initPoints[i][0] + offx;
|
|
|
+ p[1] = initPoints[i][1] + offy;
|
|
|
+ p[2] = initPoints[i][2] + offx;
|
|
|
+ p[3] = initPoints[i][3] + offy;
|
|
|
+ })
|
|
|
draw();
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- el.addEventListener("mousemove", move)
|
|
|
+ if (mvingPoint) {
|
|
|
+ points[isDragingIndex][0] = initValues[0] + offx;
|
|
|
+ points[isDragingIndex][1] = initValues[1] + offy;
|
|
|
+
|
|
|
+ points[isDragingIndex][2] = initValues[2] + offx;
|
|
|
+ points[isDragingIndex][3] = initValues[3] + offy;
|
|
|
+
|
|
|
+ } else if (ret.clickCurve) {//移动曲线
|
|
|
+ if (initControlpLen == undefined) { //初始控制点的长度
|
|
|
+ initControlpLen = VectorLenth( initPoint.cx - initPoint.x, initPoint.cy - initPoint.y);
|
|
|
+ }
|
|
|
+
|
|
|
+ const half = initControlpLen / 2.0;
|
|
|
+
|
|
|
+ //当前鼠标到远点的长度
|
|
|
+ const len = half + VectorLenth( x - initPoint.x, y - initPoint.y) / 90.0 * half;
|
|
|
+
|
|
|
+ let v = normalize({x: x - initPoint.x, y: y - initPoint.y});
|
|
|
+ if (e.shiftKey) {//一条直线
|
|
|
+ let nextIndex = ret.pIndex + 1;
|
|
|
+ if (nextIndex >= ps.length) {
|
|
|
+ nextIndex = 0;
|
|
|
+ }
|
|
|
+ v = normalize({x: ps[nextIndex].x - initPoint.x, y: ps[nextIndex].y - initPoint.y});
|
|
|
+ }
|
|
|
+
|
|
|
+ points[isDragingIndex][2] = (initPoint.x + v.x * len) / width;
|
|
|
+ points[isDragingIndex][3] = (initPoint.y + v.y * len) / height;
|
|
|
+ }
|
|
|
+ draw();
|
|
|
+ }
|
|
|
|
|
|
- el.addEventListener("mouseup", function(e:MouseEvent){
|
|
|
- el.removeEventListener("mousemove", move);
|
|
|
- });
|
|
|
+ if ( !ret.clickCurve && !ret.clickLine && !ret.moving) {
|
|
|
+ el.addEventListener("mouseleave", function(){
|
|
|
+ document.removeEventListener("mousemove", move);
|
|
|
+ }, {once: true})
|
|
|
+ }
|
|
|
|
|
|
- el.addEventListener("mouseleave", function(e:MouseEvent){
|
|
|
- el.removeEventListener("mousemove", move);
|
|
|
- })
|
|
|
+ document.addEventListener("mousemove", move)
|
|
|
|
|
|
+ document.addEventListener("mouseup", function(e:MouseEvent){
|
|
|
+ document.removeEventListener("mousemove", move);
|
|
|
+ }, {once: true});
|
|
|
|
|
|
- }
|
|
|
}, )
|
|
|
|
|
|
-
|
|
|
effect(draw);
|
|
|
})
|
|
|
|
|
@@ -171,7 +316,7 @@ export const Component = defineComponent({
|
|
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
|
ctx.lineWidth = data.value.lineWidth;
|
|
|
|
|
|
-
|
|
|
+ ctx.setLineDash([]);
|
|
|
ctx.strokeStyle = data.value.lineColor;
|
|
|
|
|
|
const padding = 0.2;
|
|
@@ -182,20 +327,22 @@ export const Component = defineComponent({
|
|
|
|
|
|
let points = data.value.points as number[][];
|
|
|
if (data.value.points.length == 0) {
|
|
|
- data.value.points = [[padding, 0.5, 0.4, 0.1], [1 - padding, 0.5, 0.4, 0.1]];
|
|
|
+ data.value.points = [[padding, 0.5, 0.4, 0.1], [1 - padding, 0.5, 0.5, 0.85]];
|
|
|
points = data.value.points;
|
|
|
}
|
|
|
|
|
|
- points.forEach((p, index)=>{
|
|
|
- const x = width * p[0];
|
|
|
- const y = height * p[1];
|
|
|
+ const ps = parsePoints(data.value.points, width, height);
|
|
|
+
|
|
|
+ ps.forEach((p, index)=>{
|
|
|
+ const x = p.x
|
|
|
+ const y = p.y
|
|
|
if (index == 0) {
|
|
|
ctx.moveTo(x, y)
|
|
|
} else {
|
|
|
const preIndex = index -1;
|
|
|
- const prep = points[preIndex];
|
|
|
- if (prep[2] != undefined) {
|
|
|
- ctx.quadraticCurveTo( width *prep[2], height *prep[3], x, y);
|
|
|
+ const prep = ps[preIndex];
|
|
|
+ if (!prep.isLine) {
|
|
|
+ ctx.quadraticCurveTo( prep.cx, prep.cy, x, y);
|
|
|
} else {
|
|
|
ctx.lineTo(x, y);
|
|
|
}
|
|
@@ -203,6 +350,11 @@ export const Component = defineComponent({
|
|
|
})
|
|
|
|
|
|
if (data.value.isClose) {
|
|
|
+ const prep = ps[points.length-1];
|
|
|
+ const lastPoint = ps[0];
|
|
|
+ if (!prep.isLine) {
|
|
|
+ ctx.quadraticCurveTo( prep.cx, prep.cy, lastPoint.x, lastPoint.y);
|
|
|
+ }
|
|
|
ctx.closePath();
|
|
|
}
|
|
|
|
|
@@ -217,18 +369,58 @@ export const Component = defineComponent({
|
|
|
ctx.fill();
|
|
|
}
|
|
|
|
|
|
- points.forEach((p, index)=>{
|
|
|
- const x = width * p[0];
|
|
|
- const y = height * p[1];
|
|
|
+ //绘制辅助gizmo
|
|
|
+ if (store.isEditMode && store.currCompId == props.compId) {
|
|
|
|
|
|
- if (store.isEditMode && store.currCompId == props.compId) {
|
|
|
- ctx.fillStyle = "red"
|
|
|
+ ctx.lineWidth = 2;
|
|
|
+ ctx.strokeStyle = "orange";
|
|
|
+ ctx.setLineDash([10, 5])
|
|
|
|
|
|
+ ps.forEach((p, index)=>{
|
|
|
+ if (index == 0) {
|
|
|
+ ctx.beginPath();
|
|
|
+ ctx.moveTo(p.x, p.y);
|
|
|
+ } else {
|
|
|
+ ctx.lineTo(p.x, p.y);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (data.value.isClose && ps.length > 2) {
|
|
|
+ ctx.lineTo(ps[0].x, ps[0].y);
|
|
|
+ }
|
|
|
+
|
|
|
+ ctx.stroke();
|
|
|
+ ctx.setLineDash([])
|
|
|
+
|
|
|
+ ps.forEach((p)=>{
|
|
|
+ const x = p.x
|
|
|
+ const y = p.y
|
|
|
+
|
|
|
+ ctx.fillStyle = "red"
|
|
|
ctx.beginPath();
|
|
|
ctx.arc(x, y, 5, 0, Math.PI*2);
|
|
|
ctx.fill();
|
|
|
- }
|
|
|
- })
|
|
|
+
|
|
|
+
|
|
|
+ ctx.beginPath()
|
|
|
+ // ctx.setLineDash([5,5])
|
|
|
+ ctx.moveTo(p.preLineX, p.preLineY);
|
|
|
+ ctx.lineTo(p.aftLineX, p.aftLineY);
|
|
|
+ ctx.stroke();
|
|
|
+
|
|
|
+ // ctx.beginPath()
|
|
|
+ // ctx.arc(p.preCirleX, p.preCirleY, r, 0, Math.PI*2)
|
|
|
+ // ctx.stroke();
|
|
|
+
|
|
|
+ const r = 10
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.arc(p.aftCirleX, p.aftCirleY, r, 0, Math.PI*2)
|
|
|
+ ctx.fill();
|
|
|
+ })
|
|
|
+ ctx.beginPath()
|
|
|
+ ctx.arc(20, 20 , 5, 0, Math.PI*2)
|
|
|
+ ctx.fill();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
return () => (
|