liwei 1 year ago
parent
commit
859985d9f6

+ 255 - 63
src/modules/editor/components/CompUI/basicUI/Curve/component.tsx

@@ -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 () => (

+ 1 - 1
src/modules/editor/components/Viewport/Slider/SliderLeft/BaseComp.tsx

@@ -13,7 +13,7 @@ export default defineComponent({
 
     const state = useReactive(() => ({
       basicComps() {
-        return ["Text", "Image", "Video", "Web3D",  "Line", "Arc", "Ellipse", "Triangle", "Rectage", "Polygon"].map(
+        return ["Text", "Image", "Video", "Web3D",  "Line", "Arc", "Ellipse", "Triangle", "Rectage", "Polygon", "Curve"].map(
           (key) => compUICtrl.state.components.get(key) as any
         );
       },