import { Effect, ModuleControl } from "queenjs"; import { reactive } from "vue"; import { EditorModule } from "../../module"; import { ObjsContainer } from "./ObjsContainer"; import { CompObject } from "./compObj"; import Event from "./event"; import { Matrix } from "./matrix"; import { Project, VectorLenth } from "./objects/mathUtils"; import { Point } from "./objects/point"; import { indexOf } from "lodash"; import { AssistCtrl } from "./assistCtrl"; import { AssistRulerCtrl } from "./assistRulerCtrl"; import { AssistMagnetCtrl } from "./assistMagnetCtrl"; /** * 页面画布空间进行选择 */ const MODE_SEL_RECT = 1; const MODE_MOVING = 2; const MODE_ROTATE = 3; const MODE_SCALE_WIDTH = 4; const MODE_SCALE_SCALE = 5; const MODE_RULER_LINE = 6; const MODE_RULER_DRAG = 7; const MODE_NONE = 0; export class SelectCtrl extends ModuleControl { transEvent = { startX: 0, startY: 0, offsetX: 0, offsetY: 0, width: 0, height: 0, }; transferStyle = reactive({ baseCardTop: "0px", showGizmo: false, editingText: false, width: 0, height: 0, relWidth: 0, relHeight: 0, matrix: "matrix(1,0,0,1,0,0)", matrixInvert: "matrix(1,0,0,1,0,0)", showOrthScale: false, mode: MODE_NONE, }); selected: any[] = []; //选中的所有组件ids pageEl?: HTMLElement; selCanvas = {} as HTMLCanvasElement; _downed = false; _selCtx = {} as CanvasRenderingContext2D; _state = MODE_NONE; _selDownX = 0; _selDownY = 0; _selBox = {} as DOMRect; _selCanvaseSize = { w: 0, h: 0 }; _downClientX = 0; _downClientY = 0; //groupCtrl = new GroupActionCtrl(this.module); bus = new Event(); viewport?: HTMLElement; assistCtrl?: AssistCtrl; assistRuler?: AssistRulerCtrl; assistMagnet?: AssistMagnetCtrl; getPageBox() { return (this.pageEl as (HTMLElement)).getBoundingClientRect(); } getCurrCardBox() { return this.store.currStreamCard.$el.getBoundingClientRect(); } getViewPortBox() { //@ts-ignore return this.viewport.getBoundingClientRect(); } getCurrCard() { return this.store.currStreamCard; } getProjectId() { return this.store.designData._id; } getUnSelectChilds() { const childs = this.store.currStreamCard.children.default || []; let n = childs.length; let out = []; while(n--) { if (this.store.selected.indexOf(childs[n]) == -1) { out.push(new CompObject( this.store.compMap[childs[n]])); } } return out; } initEvents( pageEl: HTMLElement, selCanvas: HTMLCanvasElement, viewport: HTMLElement ) { this.viewport = viewport; this.pageEl = pageEl; this.selCanvas = selCanvas; const b = selCanvas.getBoundingClientRect(); selCanvas.width = b.width * 2; selCanvas.height = b.height * 2; this._selCtx = selCanvas.getContext("2d") as CanvasRenderingContext2D; this._selCanvaseSize.w = selCanvas.width * 2; this._selCanvaseSize.h = selCanvas.height * 2; this.assistCtrl = new AssistCtrl(this); this.assistRuler = new AssistRulerCtrl(this); this.assistMagnet = new AssistMagnetCtrl(this); viewport.addEventListener("mousedown", this.onDocMouseDown.bind(this)); window.addEventListener("resize", this.onResize.bind(this)); this.initEffects(); } initEffects() { //相应相应的事件 Effect.array(() => this.store.selected) .list({ remove: () => { console.log("xxx"); }, change: (added: string[], removed: string[]) => { console.log("changeMap=>", added, removed); this._updateSelects(); }, }) .run(); } _mouseDownFlag = ""; _mouseDownTimestamp = 0; _containers: Map = new Map(); _updateSelects() { const selecteds = this.store.selected; const ObjC = this._containers.get(this.store.selectId); let objs = []; for (let item of selecteds) { objs.push(new CompObject(this.compMap[item])); } this.selecteObjs(objs, ObjC); if (this.store.selectId) { this._containers.set( this.store.selectId, this.objContainer as ObjsContainer ); } } _downClickedCompId = ""; onDocMouseDown(e: MouseEvent) { this._mouseDownTimestamp = Date.now(); if (!this.pageEl || !this.selCanvas) return; document.addEventListener("mousemove", this.onDocMouseMove, { capture: true, }); //mouseup和click都会被触发, 监听click事件可以阻止子组件的点击行为 document.addEventListener("click", this.onDocMouseUp.bind(this), { capture: true, once: true, }); let box = this.pageEl.getBoundingClientRect(); const pageX = e.clientX - box?.left; const pageY = e.clientY - box?.top; const draging = this.assistRuler?.dragTest(e) this._state = draging ? MODE_RULER_DRAG : MODE_NONE; const sel = this.selCanvas.getBoundingClientRect(); const selX = e.clientX - sel.left; const sely = e.clientY - sel.top; this._selDownX = selX; this._selDownY = sely; this._selBox = sel; this._downClientX = e.clientX; this._downClientY = e.clientY; this._downClickedCompId = this.compClickTest2(e); this._downed = true; this._mouseDownFlag = this.getDivTransformFlag(e.target as any); if (!draging) { if (!this._mouseDownFlag) { //选框点击判断 let isClickSelRect = false; if (this.selected.length > 0) { const card = this.store.currStreamCard.$el; box = card.getBoundingClientRect(); const cardX = pageX; const cardY = e.clientY - box.top; isClickSelRect = this.objContainer?.testClick(cardX, cardY) as boolean; if (isClickSelRect) { this._state = MODE_MOVING; } } if (!isClickSelRect) { //点击在选框之外 if (!this._downClickedCompId) { //没有点击到组件 const view = this.viewport?.getBoundingClientRect() as any; const isOut = e.clientX < view.left || e.clientX > view.right || e.clientY < view.top || e.clientY > view.bottom; if (!isOut) { this._state = MODE_SEL_RECT; } } //else {//点击到选框之外的组件, 把时间放到mouseup 时相应. // this._state = MODE_MOVING; // const obj = this.compMap[comps[0].id]; // this.selecteObjs([new CompObject(obj)]); // } } } else if (this._mouseDownFlag == "rotate") { this._state = MODE_ROTATE; } else if (this._mouseDownFlag.indexOf("move") > -1) { this._state = MODE_MOVING; } else if (this._mouseDownFlag.indexOf("scale") > -1) { this._state = MODE_SCALE_WIDTH; if ( this.store.selected.length == 1 && (this.selected[0].comp.compKey == "Text" || this.selected[0].comp.compKey == "Group") ) { //拖拽的是文本 const scaleFlags = [ "scaleBottomright", "scaleBottomleft", "scaleTopleft", "scaleTopright", ]; if (scaleFlags.indexOf(this._mouseDownFlag) > -1) { this._state = MODE_SCALE_SCALE; } } } else if (this._mouseDownFlag.indexOf("ruler") > -1) { this._state = MODE_RULER_LINE; } } this._movePreClientX = this._downClientX; this._movePreClientY = this._downClientY; this._initMovePos.x = -1; this._initMovePos.y = -1; } _initMovePos = { x: -1, y: -1 }; getDivFlag(div: HTMLElement, flag = "editable") { let c: any = div; if (!c) return ""; let i = 0; do { if (c[flag]) return c[flag]; c = c.parentElement; i += 1; if (i > 16) { return ""; } } while (c); return ""; } getDivId(div: HTMLElement) { let c: any = div; if (!c) return; let i = 0; do { if (c.id) return c.id; c = c.parentElement; i += 1; if (i > 5) { return; } } while (c); } getDivTransformFlag(div: HTMLElement) { const id = this.getDivId(div); if (!id) return ""; if ( id.indexOf("rotate") > -1 || id.indexOf("move") > -1 || id.indexOf("scale") > -1 || id.indexOf("toolbar") > -1 || id.indexOf("ruler") > -1 ) return id; return ""; } compClickTest2(e: MouseEvent) { const compId = this.getDivFlag(e.target as any, "compId"); console.log("down click=>", compId); return compId; } compClickTest(e: MouseEvent) { const cards = this.store.streamCardIds; let n = cards.length; const compMap = this.store.designData.compMap; //@ts-ignore const pbox = this.pageEl.getBoundingClientRect(); const pageX = e.clientX - pbox?.left; const Out: any[] = []; while (n--) { const cardComp = compMap[cards[n]]; const box = cardComp.$el.getBoundingClientRect(); const cardY = e.clientY - box.top; const cardChilds = cardComp.children.default || []; let maxZ = -1; let topItem = null; for (const key of cardChilds) { const c = compMap[key]; const m = Matrix.createFromDiv(c.$el); const localp = m.applyInverse(new Point(pageX, cardY)); const cw = this.helper.designSizeToPx(c.layout.size?.[0] as number); const ch = this.helper.designSizeToPx(c.layout.size?.[1] as number); const out = localp.x < 0 || localp.x > cw || localp.y < 0 || localp.y > ch; if (!out) { let z = c.layout.zIndex || 0; if (z > maxZ) { maxZ = z; topItem = { id: key, el: c.$el, cardX: pageX, cardY: cardY, cardId: cards[n], startMatrix: m, }; } } } if (topItem) { Out.push(topItem); return Out; } } return Out; } streamCardClickTest(e: MouseEvent) { const cards = this.store.streamCardIds; let n = cards.length; const compMap = this.store.designData.compMap; //@ts-ignore const pbox = this.pageEl.getBoundingClientRect(); const pageX = e.clientX - pbox?.left; if (pageX < 0 || pageX > pbox.width) return ""; while (n--) { const card = compMap[cards[n]]; const box = card.$el.getBoundingClientRect(); if (e.clientY >= box.top && e.clientY <= box.bottom) return { id: cards[n], x: pageX, y: e.clientY - box.top }; } return ""; } _moveSelectUpdated = false; translate(xOffset: number, yOffset: number) { const objContainer = this.objContainer as ObjsContainer; objContainer.translate(xOffset, yOffset); this.upgateGizmoStyle(); this.helper.extendStreamCard(this.store.currStreamCardId); } movingMousemove(e: MouseEvent) { const objContainer = this.objContainer as ObjsContainer; const magnet = this.assistMagnet as AssistMagnetCtrl; if (this._initMovePos.x == -1 && this._initMovePos.y == -1) { const obj = this.objContainer as ObjsContainer; this._initMovePos = { x: obj.parent.x, y: obj.parent.y }; } magnet.test(e); objContainer.setPivot(0); objContainer.translate( magnet.clientX - this._movePreClientX, magnet.clientY - this._movePreClientY ); this.upgateGizmoStyle(); } _movePreClientX = 0; _movePreClientY = 0; onDocMouseMove = (e: MouseEvent) => { if (!this.pageEl) return; if (this._state) { e.preventDefault(); e.stopPropagation(); } switch (this._state) { case MODE_SEL_RECT: //选框模式 this.drawSelRect(e); break; case MODE_MOVING: this.movingMousemove(e); break; case MODE_ROTATE: this.rotateMousemove(e); break; case MODE_SCALE_WIDTH: case MODE_SCALE_SCALE: this.scaleMousemove(e); break case MODE_RULER_LINE: this.assistRuler?.rulerLineMouseMove(e); break; case MODE_RULER_DRAG: this.assistRuler?.onDragMove(e); break; } this._movePreClientY = e.clientY; this._movePreClientX = e.clientX; }; get compMap() { return this.store.designData.compMap; } onDocMouseUp(e: MouseEvent) { let isClick = false; let offsetT = Date.now() - this._mouseDownTimestamp; const dx = Math.abs(e.clientX - this._downClientX); const dy = Math.abs(e.clientY - this._downClientY); if (dx < 2 && dy < 2 && offsetT < 200) { isClick = true; } document.removeEventListener("mousemove", this.onDocMouseMove, { capture: true, }); if (this._mouseDownFlag == "toolbar") { return; } if (isClick) { this._state = MODE_NONE; if ((!e.shiftKey && !e.ctrlKey) ) { this.actions.pickComp(this._downClickedCompId); } else { const paths = this.helper.getCompTrees(this._downClickedCompId) if (paths[2]) { this._selectObjs([paths[2].id], e) } } } if (this._state == MODE_SEL_RECT && !isClick) { //选择空间转 streamCard空间 const card = this.store.currStreamCard; const box = card.$el.getBoundingClientRect(); this.rectSelect( this._lastSelRect[0] - box.left, this._lastSelRect[1] - box.top, this._lastSelRect[2], this._lastSelRect[3], e ); } if (this._state == MODE_ROTATE) { this.rotateMouseUp(e); } else if ( this._state == MODE_SCALE_WIDTH || this._state == MODE_SCALE_SCALE ) { this.scaleMouseUp(e); } else if (this._state == MODE_MOVING) { this.moveMouseUp(e, isClick); } else if (this._state == MODE_RULER_LINE) { this.assistRuler?.rulerLineMouseUp(e, isClick) }else if (this._state == MODE_RULER_DRAG) { this.assistRuler?.onDragUp(e) } this._state = MODE_NONE; this._downed = false; this._moveSelectUpdated = false; this.upgateGizmoStyle(); this.helper.extendStreamCard(this.store.currStreamCardId); this.assistRuler?.draw(); this.assistMagnet?.onMouseUp(); } moveMouseUp(e: MouseEvent, clicked: boolean) { const history = this.controls.historyCtrl.history; const obj = this.objContainer as ObjsContainer; const lastX = obj.parent.x, lastY = obj.parent.y; const initX = this._initMovePos.x, initY = this._initMovePos.y; this._initMovePos.x = -1; this._initMovePos.y = -1; if (initX == -1 && initY == -1) return; obj.setPivot(0); history.record({ undo: () => { console.log("undo "); const currObj = this.objContainer as ObjsContainer; currObj.setPivot(0); currObj.parent.x = initX; currObj.parent.y = initY; currObj.parent.updateTransform(); currObj.updateCompState(); this.upgateGizmoStyle(); this.helper.extendStreamCard(this.store.currStreamCardId); }, redo: () => { const currObj = this.objContainer as ObjsContainer; currObj.setPivot(0); currObj.parent.x = lastX; currObj.parent.y = lastY; currObj.parent.updateTransform(); currObj.updateCompState(); this.upgateGizmoStyle(); this.helper.extendStreamCard(this.store.currStreamCardId); }, } as any); history.submit(); } rectSelect(x: number, y: number, width: number, height: number, e:MouseEvent) { const childs = this.compMap[this.store.currStreamCardId].children.default || []; let n = childs.length; const outs:string[] = []; while (n--) { const o = new CompObject(this.compMap[childs[n]]); if (o.testRect({ x, y, w: width, h: height }, true)) { //相交 outs.push(o.comp.id); } } this._selectObjs(outs, e); } _selectObjs( outs:string[], e:MouseEvent) { let objs = this.store.selected.slice(0); let lastId = outs[outs.length-1]; if (e.ctrlKey) { objs.forEach(o=>{ if (outs.indexOf(o) == -1) { outs.push(o); } }) } let selected = outs; if (e.shiftKey) {//反选 selected = []; objs.forEach(o=>{ if (outs.indexOf(o) == -1) selected.push(o); }) lastId = selected[selected.length-1] || ""; } if (lastId) { this.actions.pickComp(lastId, false); } this.actions.selectObjs(selected, lastId); if (selected.length < 1) { this.actions.pickComp(this.store.currStreamCardId); } } upgateGizmoStyle() { this.transferStyle.mode = this._state; if (this.selected.length < 1) { this.transferStyle.showGizmo = false; return; } this.assistCtrl?.flashDrawCardDists(); this.transferStyle.showGizmo = false; //@ts-ignore const yoff = this.store.currStreamCard.$el.getBoundingClientRect().top - this.pageEl?.getBoundingClientRect().top; this.transferStyle.baseCardTop = yoff + "px"; const selector = this.objContainer as ObjsContainer; if (!selector) { return; } this.transferStyle.showGizmo = true; let obj = selector.parent; let w = selector.rect.width, h = selector.rect.height; let tmp = new Matrix(); tmp.copyFrom(obj.worldTransform); let matrix = `matrix(${tmp.a},${tmp.b},${tmp.c},${tmp.d},${tmp.tx},${tmp.ty})`; tmp.rotate(-tmp.getRotate()); tmp.invert(); let matrixInvert = `matrix(${tmp.a},${tmp.b},${tmp.c},${tmp.d},0,0)`; this.transferStyle.width = w; this.transferStyle.height = h; this.transferStyle.relWidth = w * obj.scale.x; this.transferStyle.relHeight = h * obj.scale.y; this.transferStyle.matrix = matrix; this.transferStyle.matrixInvert = matrixInvert; this.transferStyle.showOrthScale = this.selected.length == 1; if (this.selected.length == 1) { const comp = this.selected[0].comp if (comp.compKey == "Group") { this.transferStyle.showOrthScale = false; } if (comp.layout.locked) { this.transferStyle.showGizmo = false; } if (comp.compKey == "Text") { if (!this.helper.isStreamCardChild(comp.id)) { this.transferStyle.showGizmo = false; } } } } selectId(id: string) { //选中ids之前 id对应组件必须已经渲染 console.log("selectId=>", id); } _lastSelRect = [0, 0, 0, 0]; drawSelRect(e: MouseEvent) { this.assistRuler?.draw(); const ctx = this._selCtx; const dx = this._selDownX; const dy = this._selDownY; const currX = e.clientX - this._selBox.left; const currY = e.clientY - this._selBox.top; const x = Math.min(currX, dx), y = Math.min(dy, currY); ctx.fillStyle = "rgba(232, 139, 0, 0.16)"; const w = Math.abs(currX - dx); const h = Math.abs(currY - dy); ctx.fillRect(x * 2, y * 2, w * 2, h * 2); ctx.lineWidth = 2; ctx.strokeStyle = "#E88B00"; ctx.strokeRect(x * 2, y * 2, w * 2, h * 2); this._lastSelRect[0] = x + this._selBox.left; this._lastSelRect[1] = y + this._selBox.top; this._lastSelRect[2] = w; this._lastSelRect[3] = h; } checkHover() { this.selCanvas; } onResize() { const b = this.selCanvas.getBoundingClientRect(); this.selCanvas.width = b.width * 2; this.selCanvas.height = b.height * 2; this._selCtx = this.selCanvas.getContext("2d") as CanvasRenderingContext2D; this._selCanvaseSize.w = b.width * 2; this._selCanvaseSize.h = b.height * 2; } // checkIntersect(compId: string, e: MouseEvent) { const currCard = this.store.currStreamCard.$el; const comp = this.store.designData.compMap[compId]; //排除坐标没有在streamCard空间内的坐标 //把当前的card坐标转为 组件的自己local坐标判断是否在方框外面 const cardBox = currCard.getBoundingClientRect(); const cardX = e.clientX - cardBox.left; const cardY = e.clientY - cardBox.top; //const m = Matrix.createFromComp(comp.layout.transform) } objContainer?: ObjsContainer; selecteObjs(objs: any[], ContainerBox?: ObjsContainer) { if (this.selected.length == 0 && objs.length == 0) return; if ( this.selected.length == 1 && objs.length == 1 && this.selected[0] == objs[0] ) return; if (this.objContainer) { this.objContainer.destroy(); this.objContainer = undefined; } let newObjContainer = undefined; if (objs.length > 0 && objs[0]) { newObjContainer = ContainerBox ? ContainerBox : new ObjsContainer(objs); if (ContainerBox) { objs.forEach((obj) => { ContainerBox.parent.addChildWorldNoChange(obj); }); ContainerBox.selected = objs; ContainerBox.parent.updateTransform(); } } this.objContainer = newObjContainer; this.selected = objs; this.emitChange(); this.upgateGizmoStyle(); return this.selected; } emitChange() { const selected = this.selected; if (selected.length && selected[0]) { this.bus.emit("showProps", selected[0].from); } else { this.bus.emit("showProps"); } this.bus.emit("selectedChange"); } rotateCenter?: { x: number; y: number }; ratatePre = 0; objinitAngleRad = 0; rotateCmd = false; lastRad = 0; rotateMousemove(e: MouseEvent) { const card = this.store.currStreamCard; const rect = card.$el.getBoundingClientRect(); let StartX = e.clientX - rect.left; let StartY = e.clientY - rect.top; const objContainer = this.objContainer as ObjsContainer; //获取当前屏幕坐标和选框中心点坐标,计算旋转值 if (!this.rotateCenter) { //let rect = this.objContainer.parent.getBounds(false); let center = objContainer.setPivot(4); this.rotateCenter = center; let vec = { x: StartX - center.x, y: StartY - center.y }; let angle = Math.atan2(vec.y, vec.x); if (angle < 0) angle += 2 * Math.PI; this.ratatePre = angle; this.objinitAngleRad = objContainer.parent.rotation; this.rotateCmd = true; return; } let center = this.rotateCenter; let vec = { x: StartX - center.x, y: StartY - center.y }; let angle = Math.atan2(vec.y, vec.x); if (angle < 0) angle += 2 * Math.PI; let dta = this.objinitAngleRad + angle - this.ratatePre; if (e.shiftKey) { //规整到0 90 180 270 if (dta < 0) dta += 2 * Math.PI; let Deg45 = Math.PI / 4.0; let Deg90 = Math.PI / 2.0; let Deg135 = Deg45 * 3; let Deg225 = Deg45 * 5; let Deg270 = Deg45 * 6; let Deg315 = Deg45 * 7; if (dta < Deg45) { dta = 0; } else if (dta < Deg135) { dta = Deg90; } else if (dta < Deg225) { dta = Math.PI; } else if (dta < Deg315) { dta = Deg270; } else { dta = 0; } } this.lastRad = dta; console.log("rotate=>", dta); objContainer.rotate(dta); // this.emit("translateChange", this.objContainer) this.upgateGizmoStyle(); } rotateMouseUp(e: MouseEvent) { this.rotateCenter = undefined; if (!this.rotateCmd) return; let scope = this; let last = this.lastRad; let initrad = scope.objinitAngleRad; const history = this.controls.historyCtrl.history; this.objContainer?.setPivot(0); history.record({ undo: () => { const objContainer = this.objContainer as ObjsContainer; this.objContainer?.setPivot(4); this.objContainer?.rotate(initrad); this.objContainer?.setPivot(0); this.objContainer?.updateCompState(); this.upgateGizmoStyle(); this.helper.extendStreamCard(this.store.currStreamCardId); }, redo: () => { console.log("redo "); const objContainer = this.objContainer as ObjsContainer; this.objContainer?.setPivot(4); this.objContainer?.rotate(last); this.objContainer?.setPivot(0); this.objContainer?.updateCompState(); this.upgateGizmoStyle(); this.helper.extendStreamCard(this.store.currStreamCardId); }, } as any); history.submit(); this.rotateCmd = false; } //缩放选中的对象 scalePivot?: any; scaleIndex = 0; mainAxisVector = { x: 0, y: 0 }; initScale = { x: 1, y: 1 }; mainAxisVectorLenth = 0; xAxisVector = { x: 1, y: 1 }; xAxisVectorLength = 0; yAxisVector = { x: 1, y: 1 }; yAxisVectorLength = 0; scaleCmd = false; lastScale = { x: 1, y: 1 }; initScaleWith = { w: 0, h: 0 }; scaleMousemove(event: MouseEvent) { let dirIndexs = [ "scaleBottomright", "scaleBottomleft", "scaleTopleft", "scaleTopright", ]; let dirOrth = ["scaleright", "scaleleft", "scalebottom", "scaletop"]; const rect = this.store.currStreamCard.$el.getBoundingClientRect(); const maget = this.assistMagnet as AssistMagnetCtrl; maget.test(event); let StartX = maget.clientX - rect.left; let StartY = maget.clientY - rect.top; const objContainer = this.objContainer as ObjsContainer; //获取当前屏幕坐标和选框中心点坐标,计算旋转值 if (!this.scalePivot) { let dir = this._mouseDownFlag; let scaleIndex = dirIndexs.indexOf(dir); if (scaleIndex == -1) { scaleIndex = dirOrth.indexOf(dir); if (scaleIndex == 2) scaleIndex = 0; } let pivot = objContainer.setPivot(scaleIndex); this.scaleIndex = scaleIndex; this.scalePivot = pivot; this.mainAxisVector = { x: StartX - pivot.x, y: StartY - pivot.y }; let scale = objContainer.parent.scale; this.initScale = { x: scale.x, y: scale.y }; this.initScaleWith = { w: objContainer.width, h: objContainer.height }; this.mainAxisVectorLenth = VectorLenth( this.mainAxisVector.x, this.mainAxisVector.y ); let ret = objContainer.getPivotXY(scaleIndex); this.xAxisVector = ret.x; this.xAxisVectorLength = VectorLenth(ret.x.x, ret.x.y); this.yAxisVector = ret.y; this.yAxisVectorLength = VectorLenth(ret.y.x, ret.y.y); return; } this.scaleCmd = true; let center = this.scalePivot; let vec = { x: StartX - center.x, y: StartY - center.y }; if (event.shiftKey) { //按住shift 自由缩放 let dtaX = Project(vec, this.xAxisVector) / this.xAxisVectorLength; let dtaY = Project(vec, this.yAxisVector) / this.yAxisVectorLength; this.lastScale.x = dtaX * this.initScale.x; this.lastScale.y = dtaY * this.initScale.y; // objContainer.scale(this.lastScale.x, this.lastScale.y); const currW = this.initScaleWith.w * this.lastScale.x; objContainer.scaleSize(currW, this.lastScale.y * this.initScaleWith.h); } else { let mainVec = this.mainAxisVector; let dtaScale = Project(vec, mainVec) / this.mainAxisVectorLenth; console.log("dtaScale=>", dtaScale); let i = dirOrth.indexOf(this._mouseDownFlag); if (i == -1) { this.lastScale.x = dtaScale * this.initScale.x; this.lastScale.y = dtaScale * this.initScale.y; if (this._state == MODE_SCALE_SCALE) { objContainer.scale(this.lastScale.x, this.lastScale.y); } else { objContainer.scaleSize(this.lastScale.x, this.lastScale.y); } } else if (i == 0 || i == 1) { this.lastScale.x = dtaScale * this.initScale.x; // objContainer.scaleX(this.lastScale.x); objContainer.scaleWidth(this.lastScale.x); } else if (i == 2 || i == 3) { this.lastScale.y = dtaScale * this.initScale.y; // objContainer.scaleY(this.lastScale.y); objContainer.scaleHeight(this.lastScale.y); } } this.upgateGizmoStyle(); } scaleMouseUp(event: MouseEvent) { this.scalePivot = undefined; if (this.scaleCmd) { let preScale = { x: this.initScale.x, y: this.initScale.y }; let scaleIndex = this.scaleIndex; let lastScale = { x: this.lastScale.x, y: this.lastScale.y }; const history = this.controls.historyCtrl.history; this.objContainer?.setPivot(0); const scaleMode = this._state; history.record({ undo: () => { console.log("undo "); this.objContainer?.setPivot(scaleIndex); if (scaleMode == MODE_SCALE_WIDTH) { this.objContainer?.scaleSize(preScale.x, preScale.y); } else { this.objContainer?.scale(preScale.x, preScale.y); } this.objContainer?.setPivot(0); this.objContainer?.updateCompState(); this.upgateGizmoStyle(); this.helper.extendStreamCard(this.store.currStreamCardId); }, redo: () => { console.log("redo "); this.objContainer?.setPivot(scaleIndex); if (scaleMode == MODE_SCALE_WIDTH) { this.objContainer?.scaleSize(lastScale.x, lastScale.y); } else { this.objContainer?.scale(lastScale.x, lastScale.y); } this.objContainer?.setPivot(0); this.objContainer?.updateCompState(); this.upgateGizmoStyle(); this.helper.extendStreamCard(this.store.currStreamCardId); }, } as any); history.submit(); this.scaleCmd = false; } } }