|
@@ -25,30 +25,76 @@
|
|
|
*/
|
|
|
|
|
|
import { SGraphItem } from "./SGraphItem";
|
|
|
-import { SObject } from "@persagy-web/base";
|
|
|
import { SGraphLayoutType } from "./enums/SGraphLayoutType";
|
|
|
import { SOrderSetType } from "./enums/SOrderSetType";
|
|
|
+import { SColor, SPainter, SPoint, SRect, SSize } from "@persagy-web/draw";
|
|
|
+import { SMathUtil } from "./utils/SMathUtil";
|
|
|
+import { SMouseEvent } from "@persagy-web/base";
|
|
|
|
|
|
/**
|
|
|
* 基本选择器
|
|
|
*
|
|
|
* @author 郝建龙
|
|
|
*/
|
|
|
-export class SGraphSelectContainer extends SObject {
|
|
|
- /** 选择对象list */
|
|
|
+export class SGraphSelectContainer extends SGraphItem {
|
|
|
+ /** 选择对象list */
|
|
|
private itemList: SGraphItem[] = [];
|
|
|
+ /** 外接点的list */
|
|
|
+ private pointList: SPoint[] = [];
|
|
|
+ /** item宽 */
|
|
|
+ private _width: number = 0;
|
|
|
+ get width(): number {
|
|
|
+ return this._width;
|
|
|
+ } // Get width
|
|
|
+ set width(v: number) {
|
|
|
+ if (v == this._width) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this._width = v;
|
|
|
+ this.update();
|
|
|
+ } // Set width
|
|
|
+ /** item高 */
|
|
|
+ private _height: number = 0;
|
|
|
+ get height(): number {
|
|
|
+ return this._height;
|
|
|
+ } // Get height
|
|
|
+ set height(v: number) {
|
|
|
+ if (v == this._height) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this._height = v;
|
|
|
+ this.update();
|
|
|
+ } // Set height
|
|
|
+ private oldWidth: number = 0;
|
|
|
+ private oldHeight: number = 0;
|
|
|
+ /** 绘制矩形的圆角半径 */
|
|
|
/** 统计选中item的数量 */
|
|
|
get count(): number {
|
|
|
return this.itemList.length;
|
|
|
}
|
|
|
/** 置顶zorder增加基数 */
|
|
|
private radix: number = 0.001;
|
|
|
+ /** 定时器 */
|
|
|
+ private timer: number | undefined;
|
|
|
+ /** 半径 */
|
|
|
+ radius: number = 5;
|
|
|
+ /** 全局灵敏度 */
|
|
|
+ dis: number = 10;
|
|
|
+ /** 灵敏度转换为场景长度 */
|
|
|
+ private sceneDis: number = 10;
|
|
|
+ /** 当前点索引 */
|
|
|
+ private curIndex: number = -1;
|
|
|
+ /** 当前点索引 */
|
|
|
+ private curPoint: SPoint | undefined;
|
|
|
|
|
|
/**
|
|
|
* 构造体
|
|
|
* */
|
|
|
constructor() {
|
|
|
super();
|
|
|
+ this.zOrder = 9999999;
|
|
|
+ this.visible = false;
|
|
|
+ this.moveable = true;
|
|
|
} // Constructor
|
|
|
|
|
|
/**
|
|
@@ -64,7 +110,7 @@ export class SGraphSelectContainer extends SObject {
|
|
|
}
|
|
|
item.selected = true;
|
|
|
this.itemList.push(item);
|
|
|
- this.$emit("listChange", this.itemList);
|
|
|
+ this.throttle(this.selectChange, 200);
|
|
|
} // Function addItem()
|
|
|
|
|
|
/**
|
|
@@ -79,7 +125,7 @@ export class SGraphSelectContainer extends SObject {
|
|
|
this.itemList.length = 0;
|
|
|
item.selected = true;
|
|
|
this.itemList.push(item);
|
|
|
- this.$emit("listChange", this.itemList);
|
|
|
+ this.throttle(this.selectChange, 200);
|
|
|
} // Function setItem()
|
|
|
|
|
|
/**
|
|
@@ -91,7 +137,7 @@ export class SGraphSelectContainer extends SObject {
|
|
|
t.selected = false;
|
|
|
});
|
|
|
this.itemList.length = 0;
|
|
|
- this.$emit("listChange", this.itemList);
|
|
|
+ this.throttle(this.selectChange, 200);
|
|
|
} // Function clear()
|
|
|
|
|
|
/**
|
|
@@ -104,7 +150,7 @@ export class SGraphSelectContainer extends SObject {
|
|
|
if (this.itemList[i] == item) {
|
|
|
this.itemList[i].selected = false;
|
|
|
this.itemList.splice(i, 1);
|
|
|
- this.$emit("listChange", this.itemList, item);
|
|
|
+ this.throttle(this.selectChange, 200);
|
|
|
return;
|
|
|
}
|
|
|
}
|
|
@@ -116,7 +162,7 @@ export class SGraphSelectContainer extends SObject {
|
|
|
}
|
|
|
item.selected = true;
|
|
|
this.itemList.push(item);
|
|
|
- this.$emit("listChange", this.itemList);
|
|
|
+ this.throttle(this.selectChange, 200);
|
|
|
} // Function toggleItem()
|
|
|
|
|
|
/**
|
|
@@ -594,4 +640,269 @@ export class SGraphSelectContainer extends SObject {
|
|
|
return 0;
|
|
|
};
|
|
|
} // Function compare()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 防抖-防止快速多次触发 itemChange;只执行最后一次
|
|
|
+ * */
|
|
|
+ throttle(fn: Function, wait: number): void {
|
|
|
+ if (this.timer) clearTimeout(this.timer);
|
|
|
+ this.timer = setTimeout((): void => {
|
|
|
+ this.selectChange();
|
|
|
+ }, wait);
|
|
|
+ } // Function throttle()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 选中的item更新时更新自身边界
|
|
|
+ * */
|
|
|
+ selectChange(): void {
|
|
|
+ // 抛出事件
|
|
|
+ this.$emit("listChange", this.itemList);
|
|
|
+ if (this.count > 0) {
|
|
|
+ // 重新计算边界
|
|
|
+ this.getSize();
|
|
|
+ // 计算边界8个点
|
|
|
+ this.calExtreme();
|
|
|
+ this.visible = true;
|
|
|
+ } else {
|
|
|
+ this.visible = false;
|
|
|
+ this.curIndex = -1;
|
|
|
+ }
|
|
|
+ } // Function selectChange()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算选中item的外接矩形和
|
|
|
+ *
|
|
|
+ * @return 外接矩形的和
|
|
|
+ * */
|
|
|
+ calItemBounding(): SRect {
|
|
|
+ if (this.itemList.length) {
|
|
|
+ const fir = this.itemList[0];
|
|
|
+ let rect = fir.boundingRect().adjusted(fir.pos.x, fir.pos.y, 0, 0);
|
|
|
+ for (let i = 1; i < this.itemList.length; i++) {
|
|
|
+ const cur = this.itemList[i];
|
|
|
+ rect = rect.unioned(
|
|
|
+ cur.boundingRect().adjusted(cur.pos.x, cur.pos.y, 0, 0)
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return rect;
|
|
|
+ } else {
|
|
|
+ return new SRect();
|
|
|
+ }
|
|
|
+ } // Function calItemBounding()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 根据矩阵得到宽高,并将自己移动至矩阵左上角
|
|
|
+ */
|
|
|
+ getSize(): void {
|
|
|
+ const r = this.calItemBounding();
|
|
|
+ this.width = r.width;
|
|
|
+ this.height = r.height;
|
|
|
+ this.moveTo(r.left, r.top);
|
|
|
+ } // getSize()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算外接矩形 8 个端点
|
|
|
+ * */
|
|
|
+ calExtreme(): void {
|
|
|
+ this.pointList = [];
|
|
|
+ this.pointList.push(new SPoint(0, 0));
|
|
|
+ this.pointList.push(new SPoint(this.width / 2, 0));
|
|
|
+ this.pointList.push(new SPoint(this.width, 0));
|
|
|
+ this.pointList.push(new SPoint(this.width, this.height / 2));
|
|
|
+ this.pointList.push(new SPoint(this.width, this.height));
|
|
|
+ this.pointList.push(new SPoint(this.width / 2, this.height));
|
|
|
+ this.pointList.push(new SPoint(0, this.height));
|
|
|
+ this.pointList.push(new SPoint(0, this.height / 2));
|
|
|
+ } // Function calExtreme()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 计算点到哪个点
|
|
|
+ */
|
|
|
+ getNearestPoint(p: SPoint): void {
|
|
|
+ let len = this.sceneDis;
|
|
|
+ for (let i = 0; i < this.pointList.length; i++) {
|
|
|
+ let dis = SMathUtil.pointDistance(
|
|
|
+ p.x,
|
|
|
+ p.y,
|
|
|
+ this.pointList[i].x,
|
|
|
+ this.pointList[i].y
|
|
|
+ );
|
|
|
+ if (dis < len) {
|
|
|
+ len = dis;
|
|
|
+ this.curIndex = i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } // Function getNearestPoint()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Item对象边界区域
|
|
|
+ *
|
|
|
+ * @return 对象边界区域
|
|
|
+ */
|
|
|
+ boundingRect(): SRect {
|
|
|
+ return new SRect(
|
|
|
+ -this.sceneDis,
|
|
|
+ -this.sceneDis,
|
|
|
+ this.width + this.sceneDis + this.sceneDis,
|
|
|
+ this.height + this.sceneDis + this.sceneDis
|
|
|
+ );
|
|
|
+ } // Function boundingRect()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 宽高发发生变化
|
|
|
+ *
|
|
|
+ * @param oldSize 改之前的大小
|
|
|
+ * @param newSize 改之后大小
|
|
|
+ * */
|
|
|
+ protected resize(oldSize: SSize, newSize: SSize): void {
|
|
|
+ for (let v of this.itemList) {
|
|
|
+ // @ts-ignore
|
|
|
+ v.resize && v.resize(oldSize, newSize);
|
|
|
+ }
|
|
|
+ } // Function resize()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 鼠标按下事件
|
|
|
+ *
|
|
|
+ * @param event 保存事件参数
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ onMouseDown(event: SMouseEvent): boolean {
|
|
|
+ this.curIndex = -1;
|
|
|
+ if (event.buttons == 1) {
|
|
|
+ this.getNearestPoint(new SPoint(event.x, event.y));
|
|
|
+ if (this.curIndex < 0) {
|
|
|
+ this.curPoint = undefined;
|
|
|
+ return super.onMouseDown(event);
|
|
|
+ } else {
|
|
|
+ const cur = this.pointList[this.curIndex];
|
|
|
+ this.curPoint = new SPoint(cur.x, cur.y);
|
|
|
+ this.oldWidth = this.width;
|
|
|
+ this.oldHeight = this.height;
|
|
|
+ this.grabItem(this);
|
|
|
+ }
|
|
|
+ this.update();
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return super.onMouseDown(event);
|
|
|
+ } // Function onMouseDown()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 鼠标按下事件
|
|
|
+ *
|
|
|
+ * @param event 保存事件参数
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ onMouseMove(event: SMouseEvent): boolean {
|
|
|
+ if (this.curIndex > -1 && this.count == 1) {
|
|
|
+ // @ts-ignore
|
|
|
+ const difX = event.x - this.curPoint.x;
|
|
|
+ // @ts-ignore
|
|
|
+ const difY = event.y - this.curPoint.y;
|
|
|
+ const mp = this.toParentChange(difX, difY);
|
|
|
+ switch (this.curIndex) {
|
|
|
+ case 0:
|
|
|
+ this.moveTo(this.pos.x + mp.x, this.pos.y + mp.y);
|
|
|
+ this.width = this.width - difX;
|
|
|
+ this.height = this.height - difY;
|
|
|
+ break;
|
|
|
+ case 1:
|
|
|
+ this.height = this.height - difY;
|
|
|
+ this.moveTo(this.pos.x, this.pos.y + mp.y);
|
|
|
+ break;
|
|
|
+ case 2:
|
|
|
+ this.moveTo(this.pos.x, this.pos.y + mp.y);
|
|
|
+ this.width = this.oldWidth + difX;
|
|
|
+ this.height = this.height - difY;
|
|
|
+ break;
|
|
|
+ case 3:
|
|
|
+ this.width = this.oldWidth + difX;
|
|
|
+ break;
|
|
|
+ case 4:
|
|
|
+ this.width = this.oldWidth + difX;
|
|
|
+ this.height = this.oldHeight + difY;
|
|
|
+ break;
|
|
|
+ case 5:
|
|
|
+ this.height = this.oldHeight + difY;
|
|
|
+ break;
|
|
|
+ case 6:
|
|
|
+ this.moveTo(this.pos.x + mp.x, this.pos.y);
|
|
|
+ this.width = this.width - difX;
|
|
|
+ this.height = this.oldHeight + difY;
|
|
|
+ break;
|
|
|
+ case 7:
|
|
|
+ this.moveTo(this.pos.x + mp.x, this.pos.y);
|
|
|
+ this.width = this.width - difX;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ this.resize(
|
|
|
+ new SSize(this.oldWidth, this.oldHeight),
|
|
|
+ new SSize(this.width, this.height)
|
|
|
+ );
|
|
|
+ this.calExtreme();
|
|
|
+ } else {
|
|
|
+ let flag = true;
|
|
|
+ for (let v of this.itemList) {
|
|
|
+ if (!v.moveable) {
|
|
|
+ flag = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (flag) {
|
|
|
+ super.onMouseMove(event);
|
|
|
+ if (this._isMoving) {
|
|
|
+ const mp = this.toParentChange(
|
|
|
+ event.x - this._mouseDownPos.x,
|
|
|
+ event.y - this._mouseDownPos.y
|
|
|
+ );
|
|
|
+ this.itemList.forEach(t => {
|
|
|
+ t.moveTo(t.pos.x + mp.x, t.pos.y + mp.y);
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ } // Function onMouseMove()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 鼠标按下事件
|
|
|
+ *
|
|
|
+ * @param event 保存事件参数
|
|
|
+ * @return boolean
|
|
|
+ */
|
|
|
+ onMouseUp(event: SMouseEvent): boolean {
|
|
|
+ this.curIndex = -1;
|
|
|
+ super.onMouseUp(event);
|
|
|
+ return true;
|
|
|
+ } // Function onMouseUp()
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Item绘制操作
|
|
|
+ *
|
|
|
+ * @param painter painter对象
|
|
|
+ */
|
|
|
+ onDraw(painter: SPainter): void {
|
|
|
+ // 缓存场景长度
|
|
|
+ this.sceneDis = painter.toPx(this.dis);
|
|
|
+ painter.pen.lineWidth = painter.toPx(1);
|
|
|
+ painter.pen.color = SColor.White;
|
|
|
+ painter.brush.color = SColor.Transparent;
|
|
|
+ painter.drawRect(0, 0, this.width, this.height);
|
|
|
+
|
|
|
+ painter.pen.color = SColor.Black;
|
|
|
+ painter.pen.lineDash = [painter.toPx(3), painter.toPx(3)];
|
|
|
+ painter.drawRect(0, 0, this.width, this.height);
|
|
|
+
|
|
|
+ const r = painter.toPx(this.radius);
|
|
|
+ painter.pen.lineDash = [];
|
|
|
+ this.pointList.forEach((t, i) => {
|
|
|
+ if (i == this.curIndex) {
|
|
|
+ painter.brush.color = new SColor("#409eff");
|
|
|
+ } else {
|
|
|
+ painter.brush.color = SColor.White;
|
|
|
+ }
|
|
|
+ painter.drawCircle(t.x, t.y, r);
|
|
|
+ });
|
|
|
+ } // Function onDraw()
|
|
|
} // Class SGraphSelectContainer
|