123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- import { defineComponent, onMounted , ref, effect} from "vue";
- import { string } from "vue-types";
- import { useCompData } from ".";
- import { useEditor } from "../../../..";
- import { View } from "../View";
- import { CompUI } from "../..";
- import { values } from "lodash";
- 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;
- let minv = 100000;
- let minIndex = -1;
- const positions = []
- for(let i=0; i<n; i++) {
- const p = points[i];
- const x = w *p[0];
- const y = h *p[1];
- positions.push([x, y]);
- const ln = (sx-x)*(sx-x) + (sy-y)*(sy-y);
- if ( ln < minv ) {
- minIndex = i;
- minv = ln;
- if(!(sx < (x -10) || sx > (x + 10) || sy < (y -10) || sy > (y+10) ) ) {
- return {clicked: true, index: i};
- }
- }
- }
- let preIndex = minIndex -1;
- if (preIndex < 0 ) preIndex = n-1;
- let afterIndex = minIndex + 1;
- if ( afterIndex >= n) afterIndex = 0;
- const currV = {x:sx-positions[minIndex][0], y:sy-positions[minIndex][1]};
- const preV = {x:positions[preIndex][0]-positions[minIndex][0], y:positions[preIndex][1]-positions[minIndex][1]}
- const afterV = {x:positions[afterIndex][0]-positions[minIndex][0], y:positions[afterIndex][1]-positions[minIndex][1]}
- const a1 = Angle(currV, preV)
- const a2 = Angle(currV, afterV);
- if (a1 < a2) {
- return {index: preIndex};
- }
- 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,
- },
- setup(props) {
- const { helper, controls , store} = useEditor();
- const data = useCompData(props.compId);
- const canvasRef = ref<HTMLCanvasElement>();
- onMounted(()=>{
-
- canvasRef.value?.addEventListener("dblclick", function(e:MouseEvent){
-
- const x = helper.pxToDesignSize(e.offsetX )
- const y = helper.pxToDesignSize(e.offsetY)
-
- const points = data.value.points as number[][];
- const w = data.layout.size[0];
- const h = data.layout.size[1];
- //判断直线相交求解点到直线的距离
- const ret = findNearestPoint(data.value.points, w , h, x, y);
- //判断是否
- console.log("dbclick=>xxxxx", ret);
- if (ret.clicked) {//点击删除
- points.splice(ret.index, 1);
- if (points.length < 2) {
- return;
- }
- } else {
- 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();
- })
- canvasRef.value?.addEventListener("mousedown", function(e:MouseEvent){
- if (store.currCompId != props.compId) return;
- const el = canvasRef.value as HTMLCanvasElement;
- 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;
- const dragingX = ret.x;
- const dragingY = ret.y;
- 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 isDragingIndex = ret.pIndex;
- const box = controls.selectCtrl.getCurrCardViewPortBox();
- const mvingPoint = !ret.clickCurve && !ret.clickLine
- let initControlpLen :number | undefined = undefined;
- const move = function (e:MouseEvent){
- e.preventDefault();
- e.stopPropagation();
- 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;
- }
- 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();
- }
- if ( !ret.clickCurve && !ret.clickLine && !ret.moving) {
- el.addEventListener("mouseleave", function(){
- document.removeEventListener("mousemove", move);
- }, {once: true})
- }
- document.addEventListener("mousemove", move)
- document.addEventListener("mouseup", function(e:MouseEvent){
- document.removeEventListener("mousemove", move);
- }, {once: true});
- }, )
- effect(draw);
- })
-
-
- function draw() {
- const canvas = canvasRef.value as HTMLCanvasElement;
- if (!canvas) return;
- const ctx = canvasRef.value?.getContext("2d") as CanvasRenderingContext2D;
- const width = data.layout.size[0];
- const height = data.layout.size[1];
- canvas.width = Math.ceil(Math.max(1, width));
- canvas.height = Math.ceil(Math.max(1, height));
- ctx.clearRect(0, 0, canvas.width, canvas.height);
- ctx.lineWidth = data.value.lineWidth;
- if (data.value.reverseFill) {
- ctx.fillStyle = data.value.fillColor;
- ctx.fillRect(0, 0, canvas.width, canvas.height);
- }
- ctx.setLineDash([]);
- ctx.strokeStyle = data.value.lineColor;
-
- const padding = 0.2;
- ctx.lineJoin = "round";
- ctx.beginPath();
-
- 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.5, 0.85]];
- points = data.value.points;
- }
- const ps = parsePoints(data.value.points, width, height);
-
- ctx.save();
- 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 = ps[preIndex];
- if (!prep.isLine) {
- ctx.quadraticCurveTo( prep.cx, prep.cy, x, y);
- } else {
- ctx.lineTo(x, y);
- }
- }
- })
- 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();
- }
- if (data.value.lineWidth !== 0) {
- ctx.stroke();
- }
- if (data.value.isFill) {
- let bColor = data.value.fillColor;
- if (!bColor) bColor = data.value.lineColor;
- ctx.fillStyle = bColor;
- if (data.value.reverseFill) {
- ctx.fillStyle = "red";
- // 设置剪切区域
- ctx.clip();
- // 将当前路径指定的区域颜色扣除
- ctx.globalCompositeOperation = "destination-out";
- }
- ctx.fill();
- }
- ctx.restore();
- //绘制辅助gizmo
- if (store.isEditMode && store.currCompId == props.compId) {
- 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 () => (
- <View compId={props.compId}>
- <canvas ref={canvasRef} style={{width:"100%", height: "100%"}}> </canvas>
- </View>
- );
- },
- });
|