/* * ********************************************************************************************************************* * * !! * .F88X * X8888Y * .}888888N; * i888888N; .:! .I$WI: * R888888I .'N88~ i8}+8Y&8"l8i$8>8W~'>W8}8]KW+8IIN"8& * .R888888I .;N8888~ .X8' "8I.!,/8" !%NY8`"8I8~~8>,88I * +888888N; .8888888Y "&&8Y.}8, * ./888888N; .R888888Y .'}~ .>}'.`+> i}! "i' +/' .'i~ !11,.:">, .~]! .i}i * ~888888%: .I888888l .]88~`1/iY88Ii+1'.R$8$8]"888888888> Y8$ W8E X8E W8888'188Il}Y88$* * 18888888 E8888881 .]W%8$`R8X'&8%++N8i,8N%N8+l8%` .}8N:.R$RE%N88N%N$K$R 188,FE$8%~Y88I * .E888888I .i8888888' .:$8I;88+`E8R:/8N,.>881.`$8E/1/]N8X.Y8N`"KF&&FK!'88*."88K./$88%RN888+~ * 8888888I .,N888888~ ~88i"8W,!N8*.I88.}888%F,i$88"F88" 888:E8X.>88!i88>`888*.}Fl1]*}1YKi' * i888888N' I888Y ]88;/EX*IFKFK88X K8R .l8W 88Y ~88}'88E&%8W.X8N``]88!.$8K .:W8I * .i888888N; I8Y .&8$ .X88! i881.:%888>I88 ;88] +88+.';;;;:.Y88X 18N.,88l .+88/ * .:R888888I * .&888888I Copyright (c) 2009-2020. 博锐尚格科技股份有限公司 * ~8888' * .!88~ All rights reserved. * * ********************************************************************************************************************* */ import { SGraphItem } from "./SGraphItem"; 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 SGraphItem { /** 选择对象list */ private _itemList: SGraphItem[] = []; get itemList(): SGraphItem[] { return this._itemList; } // Get itemList /** 外接点的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; /** 最小宽 */ minWidth: number = 0; /** 最大宽 */ maxWidth: number = Number.MAX_SAFE_INTEGER; /** 最小高 */ minHeight: number = 0; /** 最小高 */ maxHeight: number = Number.MAX_SAFE_INTEGER; /** 边框颜色 */ _strokeColor: SColor = new SColor("#82C7FC"); /** 画笔颜色 */ get strokeColor(): SColor { return this._strokeColor; } // Get strokeColor set strokeColor(v: SColor) { this._strokeColor = v; this.update(); } // Set strokeColor /** 填充颜色 */ _fillColor: SColor = new SColor("#ffffff"); get fillColor(): SColor { return this._fillColor; } // Get fillColor set fillColor(v: SColor) { this._fillColor = v; this.update(); } // Set fillColor /** 绘制矩形的圆角半径 */ /** 统计选中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; get curIndex(): number { return this._curIndex; } // get curIndex set curIndex(v: number) { if (v === this._curIndex) { return; } this._curIndex = v; this.update(); } // set curIndex /** 当前点索引 */ private curPoint: SPoint | undefined; /** 鼠标样式 */ static cursorList: string[] = [ "nw-resize", "n-resize", "ne-resize", "e-resize", "se-resize", "s-resize", "sw-resize", "w-resize", "move", "default" ]; /** * 构造体 * */ constructor() { super(); this.zOrder = 9999999; this.visible = false; this.moveable = true; } // Constructor /** * 添加item到list * * @param item 当前选中的item * */ addItem(item: SGraphItem): void { for (let i = 0; i < this.itemList.length; i++) { if (this.itemList[i] == item) { return; } } item.selected = true; this.itemList.push(item); this.throttle(this.selectChange, 80); } // Function addItem() /** * 清空再添加(事件+复制数组) * * @param item 当前选中的item * */ setItem(item: SGraphItem): void { this.itemList.forEach((t: SGraphItem): void => { t.selected = false; }); this.itemList.length = 0; item.selected = true; this.itemList.push(item); this.throttle(this.selectChange, 80); } // Function setItem() /** * 清空选择对象 * * */ clear(): void { this.itemList.forEach((t: SGraphItem): void => { t.selected = false; }); this.itemList.length = 0; this.throttle(this.selectChange, 80); } // Function clear() /** * 切换选择对象 * * @param item 当前选中的item * */ toggleItem(item: SGraphItem): void { for (let i = 0; i < this.itemList.length; i++) { if (this.itemList[i] == item) { this.itemList[i].selected = false; this.itemList.splice(i, 1); this.throttle(this.selectChange, 80); return; } } // 多选时,父级item需要一致 if (this.itemList.length) { if (item.parent != this.itemList[0].parent) { return; } } item.selected = true; this.itemList.push(item); this.throttle(this.selectChange, 80); } // Function toggleItem() /** * 对齐 * * @param type 对齐方式 * */ layout(type: SGraphLayoutType): void { if (this.itemList.length < 2) { return; } switch (type) { case SGraphLayoutType.Left: this.alignLeft(); break; case SGraphLayoutType.Bottom: this.alignBottom(); break; case SGraphLayoutType.Center: this.alignCenter(); break; case SGraphLayoutType.Horizontal: this.alignHorizontal(); break; case SGraphLayoutType.Middle: this.alignMiddle(); break; case SGraphLayoutType.Right: this.alignRight(); break; case SGraphLayoutType.Top: this.alignTop(); break; case SGraphLayoutType.Vertical: this.alignVertical(); break; default: console.log("对齐类型不存在"); break; } } // Function layout() /** * 图层操作 * * @param type 操作类型 * */ setOrder(type: SOrderSetType): void { if (this.itemList.length < 1) { return; } switch (type) { case SOrderSetType.Top: this.setTop(); break; case SOrderSetType.Bottom: this.setBottom(); break; case SOrderSetType.After: this.setAfter(); break; case SOrderSetType.Before: this.setBefore(); break; default: console.log("图层操作类型不存在"); break; } } // Function setOrder() /** * 左对齐 * * */ private alignLeft(): void { let standardItem = this.itemList[0]; // 计算第一个外接矩阵左上角坐标在场景中的坐标 let p = standardItem.mapToScene( standardItem.boundingRect().x, standardItem.boundingRect().y ); for (let i = 1; i < this.itemList.length; i++) { let curItem = this.itemList[i]; if (curItem.moveable) { // 将p转换为当前item中的坐标 let p1 = curItem.mapFromScene(p.x, p.y); // 根据等式差值相等 newboundx - oldboundx = newposx - oldposx => newposx = newboundx - oldboundx + oldposx curItem.x = (p1.x - curItem.boundingRect().x) * curItem.inverseScale + curItem.x; } } } // Function alignLeft() /** * 顶对齐 * * */ private alignTop(): void { let standardItem = this.itemList[0]; let p = standardItem.mapToScene( standardItem.boundingRect().x, standardItem.boundingRect().y ); for (let i = 1; i < this.itemList.length; i++) { let curItem = this.itemList[i]; if (curItem.moveable) { let p1 = curItem.mapFromScene(p.x, p.y); curItem.y = (p1.y - curItem.boundingRect().y) * curItem.inverseScale + curItem.y; } } } // Function alignTop() /** * 右对齐 * * */ private alignRight(): void { let standardItem = this.itemList[0]; let p = standardItem.mapToScene( standardItem.boundingRect().right, standardItem.boundingRect().bottom ); for (let i = 1; i < this.itemList.length; i++) { let curItem = this.itemList[i]; if (curItem.moveable) { let p1 = curItem.mapFromScene(p.x, p.y); curItem.x = (p1.x - curItem.boundingRect().right) * curItem.inverseScale + curItem.x; } } } // Function alignRight() /** * 底对齐 * * */ private alignBottom(): void { let standardItem = this.itemList[0]; let p = standardItem.mapToScene( standardItem.boundingRect().right, standardItem.boundingRect().bottom ); for (let i = 1; i < this.itemList.length; i++) { let curItem = this.itemList[i]; if (curItem.moveable) { let p1 = curItem.mapFromScene(p.x, p.y); curItem.y = (p1.y - curItem.boundingRect().bottom) * curItem.inverseScale + curItem.y; } } } // Function alignBottom() /** * 水平居中对齐 * * */ private alignCenter(): void { // 第一个选中的item即为基准对象 let standardItem = this.itemList[0]; // 基准对象的中心点 let center = standardItem.boundingRect().center(); // 中心点转换为场景坐标 let p = standardItem.mapToScene(center.x, center.y); for (let i = 1; i < this.itemList.length; i++) { let curItem = this.itemList[i]; if (curItem.moveable) { let p1 = curItem.mapFromScene(p.x, p.y); // 根据等式差值相等 newcenterx - oldcenterx = newposx - oldposx => newposx = newcenterx - oldcenterx + oldposx curItem.x = (p1.x - curItem.boundingRect().center().x) * curItem.inverseScale + curItem.x; } } } // Function alignCenter() /** * 垂直居中对齐 * * */ private alignMiddle(): void { // 第一个选中的item即为基准对象 let standardItem = this.itemList[0]; // 基准对象的中心点 let center = standardItem.boundingRect().center(); // 中心点转换为场景坐标 let p = standardItem.mapToScene(center.x, center.y); for (let i = 1; i < this.itemList.length; i++) { let curItem = this.itemList[i]; if (curItem.moveable) { let p1 = curItem.mapFromScene(p.x, p.y); curItem.y = (p1.y - curItem.boundingRect().center().y) * curItem.inverseScale + curItem.y; } } } // Function alignMiddle() /** * 垂直分布 * * */ private alignVertical(): void { if (this.itemList.length < 3) { return; } for (let i = 0; i < this.itemList.length - 1; i++) { for (let j = 0; j < this.itemList.length - 1 - i; j++) { let curItemCenter = this.itemList[j].boundingRect().center(); let nextItemCenter = this.itemList[j + 1] .boundingRect() .center(); let curCenterY = this.itemList[j].mapToScene( curItemCenter.x, curItemCenter.y ).y; let nextCenterY = this.itemList[j + 1].mapToScene( nextItemCenter.x, nextItemCenter.y ).y; if (curCenterY > nextCenterY) { let temp = this.itemList[j]; this.itemList[j] = this.itemList[j + 1]; this.itemList[j + 1] = temp; } } } let top = this.itemList[0]; let bottom = this.itemList[this.itemList.length - 1]; let topCenter = top.boundingRect().center(); let bottomCenter = bottom.boundingRect().center(); let leftToRight = bottom.mapToScene(bottomCenter.x, bottomCenter.y).y - top.mapToScene(topCenter.x, topCenter.y).y; let average = leftToRight / (this.itemList.length - 1); for (let k = 1; k < this.itemList.length - 1; k++) { let curItem = this.itemList[k]; if (curItem.moveable) { let p1 = top.mapToScene( top.boundingRect().center().x, top.boundingRect().center().y ); let p2 = curItem.mapFromScene(p1.x, p1.y + average * k); curItem.y = (p2.y - curItem.boundingRect().center().y) * curItem.inverseScale + curItem.y; } } } // Function alignVertical() /** * 水平分布 * * */ private alignHorizontal(): void { if (this.itemList.length < 3) { return; } for (let i = 0; i < this.itemList.length - 1; i++) { for (let j = 0; j < this.itemList.length - 1 - i; j++) { let curItemCenter = this.itemList[j].boundingRect().center(); let nextItemCenter = this.itemList[j + 1] .boundingRect() .center(); let curCenterX = this.itemList[j].mapToScene( curItemCenter.x, curItemCenter.y ).x; let nextCenterX = this.itemList[j + 1].mapToScene( nextItemCenter.x, nextItemCenter.y ).x; if (curCenterX > nextCenterX) { let temp = this.itemList[j]; this.itemList[j] = this.itemList[j + 1]; this.itemList[j + 1] = temp; } } } let left = this.itemList[0]; let right = this.itemList[this.itemList.length - 1]; let leftCenter = left.boundingRect().center(); let rightCenter = right.boundingRect().center(); let leftToRight = right.mapToScene(rightCenter.x, rightCenter.y).x - left.mapToScene(leftCenter.x, leftCenter.y).x; let average = leftToRight / (this.itemList.length - 1); for (let k = 1; k < this.itemList.length - 1; k++) { let curItem = this.itemList[k]; if (curItem.moveable) { let p1 = left.mapToScene( left.boundingRect().center().x, left.boundingRect().center().y ); let p2 = curItem.mapFromScene(p1.x + average * k, p1.y); // 根据等式 newposx - oldposx = newcenterx - oldcenterx curItem.x = (p2.x - curItem.boundingRect().center().x) * curItem.inverseScale + curItem.x; } } } // Function alignHorizontal() // 修改层级操作步骤: // 排序(保持相对层级不变) // children里边要取 整数部分相同的 点:children是已经排好序的 // 然后再取最大值 /** * 置顶 * * */ private setTop(): void { const arr: SGraphItem[] = this.sortItemList(this.itemList, true); // 按顺序zOrder增加 arr.forEach(it => { let max = it.zOrder; if (it.parent) { it.parent.children.forEach(item => { if ( Number(max.toFixed()) == Number(item.zOrder.toFixed()) ) { if (item.zOrder > max) { max = item.zOrder; } } }); it.zOrder = max + this.radix; } }); } // Function setTop() /** * 置底 * * */ private setBottom(): void { const arr: SGraphItem[] = this.sortItemList(this.itemList, false); // 按顺序zOrder增加 arr.forEach(it => { let min = it.zOrder; if (it.parent) { it.parent.children.forEach(item => { if ( Number(min.toFixed()) == Number(item.zOrder.toFixed()) ) { if (item.zOrder < min) { min = item.zOrder; } } }); it.zOrder = min - this.radix; } }); } // Function setBottom() /** * 下移一层zorder减小 * * */ private setBefore(): void { const arr: SGraphItem[] = this.sortItemList(this.itemList, false); arr.forEach(it => { if (it.parent) { const min = it.zOrder; let temp = it.parent.children .map(child => { if ( Number(child.zOrder.toFixed()) == Number(min.toFixed()) ) { return child.zOrder; } }) .filter(c => c); temp = [...new Set(temp)]; // 当前层有多个 const index = temp.indexOf(it.zOrder); if (index <= 1) { // 当前item 索引为1时 将当前item放至第一个前边 // @ts-ignore it.zOrder = temp[0] - this.radix; } else if (index > 1) { // 当前item 索引大于等于2,将当前item放至前两个中间 // @ts-ignore it.zOrder = (temp[index - 1] + temp[index - 2]) / 2; } } }); } // Function setAfter() /** * 上移一层zorder增加 * * */ private setAfter(): void { const arr: SGraphItem[] = this.sortItemList(this.itemList, false); arr.forEach(it => { if (it.parent) { const max = it.zOrder; let temp = it.parent.children .map(child => { if ( Number(child.zOrder.toFixed()) == Number(max.toFixed()) ) { return child.zOrder; } }) .filter(c => c); temp = [...new Set(temp)].reverse(); // 当前层有多个 const index = temp.indexOf(it.zOrder); if (index <= 1) { // 当前item 索引为1时 将当前item放至第一个前边 // @ts-ignore it.zOrder = temp[0] + this.radix; } else if (index > 1) { // 当前item 索引大于等于2,将当前item放至前两个中间 // @ts-ignore it.zOrder = (temp[index - 1] + temp[index - 2]) / 2; } } }); } // Function setBefore() /** * 数组排序 * * @param arr 排序前的数组 * @param flag 正序or逆序 * @return list 排序后的数组 * */ sortItemList(arr: SGraphItem[], flag: boolean = true): SGraphItem[] { const list: SGraphItem[] = arr.map(t => t); list.sort(this.compare("zOrder", flag)); return list; } // Function sortItemList() /** * 按照某个属性排序 * * @param prop 属性 * @param flag 默认正序 * */ private compare(prop: string, flag: boolean): any { const f = flag ? 1 : -1; // eslint-disable-next-line @typescript-eslint/explicit-function-return-type return (a: any, b: any) => { a = a[prop]; b = b[prop]; if (a < b) { return f * -1; } if (a > b) { return f * 1; } 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(); if ( this.count > 1 || (this.count == 1 && this.itemList[0].showSelect) ) { 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; this.curIndex = -1; 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 { if (this.visible) { return new SRect( -this.sceneDis, -this.sceneDis, this.width + this.sceneDis + this.sceneDis, this.height + this.sceneDis + this.sceneDis ); } else { return new SRect(); } } // 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); const p = v.mapToScene(this.pos.x, this.pos.y); const p2 = v.mapFromScene(p.x, p.y); v.moveTo(p2.x - v.boundingRect().x, p2.y - v.boundingRect().y); } } // Function resize() /** * 鼠标按下事件 * * @param event 保存事件参数 * @return boolean */ onMouseDown(event: SMouseEvent): boolean { 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 { this.setCursor(9); if ( this.curIndex > -1 && this.count == 1 && this.itemList[0]._resizeable ) { this.setCursor(this.curIndex); // @ts-ignore const difX = event.x - this.curPoint.x; // @ts-ignore const difY = event.y - this.curPoint.y; const mp = this.toParentChange(difX, difY); const oldSize = new SSize(this.width, this.height); let resultX, resultY; switch (this.curIndex) { case 0: resultX = this.width - difX; resultY = this.height - difY; if (resultX > this.minWidth && resultX < this.maxWidth) { this.width = resultX; this.x = this.x + mp.x; } if (resultY > this.minHeight && resultY < this.maxHeight) { this.height = resultY; this.y = this.y + mp.y; } break; case 1: resultY = this.height - difY; if (resultY > this.minHeight && resultY < this.maxHeight) { this.height = resultY; this.y = this.y + mp.y; } break; case 2: resultX = this.oldWidth + difX; resultY = this.height - difY; if (resultX > this.minWidth && resultX < this.maxWidth) { this.width = resultX; } if (resultY > this.minHeight && resultY < this.maxHeight) { this.height = resultY; this.y = this.y + mp.y; } break; case 3: resultX = this.oldWidth + difX; if (resultX > this.minWidth && resultX < this.maxWidth) { this.width = resultX; } break; case 4: resultX = this.oldWidth + difX; resultY = this.oldHeight + difY; if (resultX > this.minWidth && resultX < this.maxWidth) { this.width = resultX; } if (resultY > this.minHeight && resultY < this.maxHeight) { this.height = resultY; } break; case 5: resultY = this.oldHeight + difY; if (resultY > this.minHeight && resultY < this.maxHeight) { this.height = resultY; } break; case 6: resultX = this.width - difX; resultY = this.oldHeight + difY; if (resultX > this.minWidth && resultX < this.maxWidth) { this.width = resultX; this.x = this.x + mp.x; } if (resultY > this.minHeight && resultY < this.maxHeight) { this.height = resultY; } break; case 7: resultX = this.width - difX; if (resultX > this.minWidth && resultX < this.maxWidth) { this.width = resultX; this.x = this.x + mp.x; } break; default: break; } const newSize = new SSize(this.width, this.height); this.resize(oldSize, newSize); this.calExtreme(); } else { let flag = true; for (let v of this.itemList) { if (!v.moveable) { flag = false; } } if (flag) { super.onMouseMove(event); this.setCursor(8); 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 n 样式的索引 * */ private setCursor(n: number): void { this.cursor = SGraphSelectContainer.cursorList[n] || "default"; if (this.scene && this.scene.view) { this.scene.view.cursor = this.cursor; } } // Function setCursor() /** * 鼠标按下事件 * * @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); // 多选时 或者 只选择一个并且是需要显示选择器时 绘制 if ( this.count > 1 || (this.count == 1 && this.itemList[0].showSelect) ) { 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 = this.strokeColor; painter.pen.lineDash = [painter.toPx(4), painter.toPx(4)]; 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 = this.fillColor; } else { painter.brush.color = SColor.White; } painter.drawCircle(t.x, t.y, r); }); } } // Function onDraw() } // Class SGraphSelectContainer