同步框架代码

This commit is contained in:
fan 2023-09-18 11:21:20 +08:00
parent b5716e181b
commit dad0a34339
8 changed files with 158 additions and 27 deletions

@ -1 +1 @@
Subproject commit cc737578baa0dadef5c2b82b6ada66c117ba3d6b
Subproject commit 3e1bbc92bf3e1604ec54db0e9188a1677ac20388

View File

@ -22,7 +22,15 @@ import {
} from '../plugins';
import { CommonMouseTool } from '../plugins/CommonMousePlugin';
import { MenuItemOptions } from '../ui/Menu';
import { DOWN, LEFT, RIGHT, UP, recursiveChildren } from '../utils';
import {
DOWN,
DebouncedFunction,
LEFT,
RIGHT,
UP,
debounce,
recursiveChildren,
} from '../utils';
import {
GraphicDataUpdateOperation,
UpdateCanvasOperation,
@ -233,6 +241,16 @@ export interface IDrawApp extends IGraphicApp {
* @param data
*/
updateGraphicAndRecord(g: JlGraphic, data: GraphicData): void;
/**
* form表单对象
* @param form
*/
bindFormData(form: GraphicData): void;
/**
* form表单对象
* @param form
*/
unbindFormData(form: GraphicData): void;
}
/**
@ -258,6 +276,8 @@ export class JlDrawApp extends GraphicApp implements IDrawApp {
drawAssistants: DrawAssistant[] = [];
_drawing = false;
private debouncedFormDataUpdator: DebouncedFunction<(g: JlGraphic) => void>;
get drawing(): boolean {
return this._drawing;
}
@ -274,6 +294,9 @@ export class JlDrawApp extends GraphicApp implements IDrawApp {
this.appOperationRecord();
// 绑定通用键盘操作
this.bindKeyboardOperation();
this.formDataSyncListen();
this.debouncedFormDataUpdator = debounce(this.doFormDataUpdate, 60);
}
setOptions(options: DrawAppOptions): void {
@ -484,6 +507,57 @@ export class JlDrawApp extends GraphicApp implements IDrawApp {
graphic.eventMode = 'static';
graphic.selectable = true;
graphic.draggable = true;
graphic.on('repaint', () => {
this.handleFormDataUpdate(graphic);
});
graphic.on('transformend', () => {
this.handleFormDataUpdate(graphic);
});
}
formData: GraphicData | undefined = undefined;
/**
* form表单对象
* @param form
*/
bindFormData(form: GraphicData): void {
this.formData = form;
if (this.selectedGraphics.length == 1) {
this.formData.copyFrom(this.selectedGraphics[0].saveData());
}
}
/**
* form绑定
* @param form
*/
unbindFormData(form: GraphicData): void {
if (this.formData == form) {
this.formData = undefined;
}
}
private formDataSyncListen(): void {
this.on('graphicselected', () => {
if (this.selectedGraphics.length == 1) {
this.handleFormDataUpdate(this.selectedGraphics[0]);
}
});
}
/**
* 使debounce限流
*/
private handleFormDataUpdate(g: JlGraphic): void {
this.debouncedFormDataUpdator(this, g);
}
private doFormDataUpdate(g: JlGraphic): void {
if (this.selectedGraphics.length > 1) return;
if (this.formData && g.type === this.formData.graphicType) {
this.formData.copyFrom(g.saveData());
}
}
updateCanvasAndRecord(data: ICanvasProperties) {

View File

@ -46,6 +46,7 @@ import {
} from '../plugins/KeyboardPlugin';
import { ContextMenu, ContextMenuPlugin } from '../ui/ContextMenu';
import { MenuItemOptions } from '../ui/Menu';
import { DebouncedFunction, debounce } from '../utils';
import { getRectangleCenter, recursiveChildren } from '../utils/GraphicUtils';
import {
GraphicCreateOperation,
@ -317,6 +318,7 @@ export interface GraphicAppEvents extends GlobalMixins.GraphicAppEvents {
'options-update': [options: GraphicAppOptions]; // 配置更新
graphicselectedchange: [graphic: JlGraphic, selected: boolean];
graphicchildselectedchange: [child: DisplayObject, selected: boolean];
graphicselected: [graphics: JlGraphic[]];
'viewport-scaled': [vp: Viewport];
drag_op_start: [event: AppDragEvent];
drag_op_move: [event: AppDragEvent];
@ -509,11 +511,6 @@ export interface IGraphicScene extends EventEmitter<GraphicAppEvents> {
* @param graphics
*/
updateSelected(...graphics: JlGraphic[]): void;
/**
*
* @param graphic
*/
fireSelectedChange(graphic: JlGraphic): void;
/**
*
*/
@ -563,6 +560,8 @@ abstract class GraphicSceneBase
menuPlugin: ContextMenuPlugin; // 菜单插件
private debounceEmitFunc: DebouncedFunction<() => void>;
wsMsgBroker: AppWsMsgBroker; // websocket消息代理
constructor(options: GraphicAppOptions) {
super();
@ -628,6 +627,11 @@ abstract class GraphicSceneBase
this.menuPlugin = new ContextMenuPlugin(this);
this.wsMsgBroker = new AppWsMsgBroker(this);
this.debounceEmitFunc = debounce(this.doEmitAppGraphicSelected, 50);
this.on('graphicselectedchange', () => {
this.debounceEmitFunc(this);
});
}
abstract get app(): GraphicApp;
@ -883,12 +887,10 @@ abstract class GraphicSceneBase
// graphic可能是vue的Proxy对象会导致canvas删除时因不是同一个对象而无法从画布移除
const g = this.graphicStore.deleteGraphics(graphic);
if (g) {
// 清除选中
g.updateSelected(false);
// 从画布移除
this.canvas.removeGraphic(g);
// 清除选中
if (g.updateSelected(false)) {
this.fireSelectedChange(g);
}
// 对象删除处理
g.onDelete();
this.emit('graphicdeleted', g);
@ -936,15 +938,6 @@ abstract class GraphicSceneBase
this.updateSelected(...this.queryStore.getAllGraphics());
}
/**
*
* @param graphic
*/
fireSelectedChange(graphic: JlGraphic) {
// console.log('通知选中变化', this.selecteds)
const select = graphic.selected;
this.emit('graphicselectedchange', graphic, select);
}
/**
*
*/
@ -955,16 +948,19 @@ abstract class GraphicSceneBase
}
if (graphic.selected) {
graphic.updateSelected(false);
this.fireSelectedChange(graphic);
}
});
graphics.forEach((graphic) => {
if (graphic.updateSelected(true)) {
this.fireSelectedChange(graphic);
}
graphic.updateSelected(true);
});
}
private doEmitAppGraphicSelected(): void {
// 场景发布图形选中
this.emit('graphicselected', this.selectedGraphics);
// this.app.emit('graphicselected', this.selectedGraphics);
}
/**
*
* @param param

View File

@ -642,6 +642,10 @@ export abstract class JlGraphic extends Container {
this.removeAllChildSelected();
this.emit('unselected', this);
}
const app = this.getGraphicApp();
if (app) {
app.emit('graphicselectedchange', this, this.selected);
}
}
hasSelectedChilds(): boolean {
@ -678,11 +682,16 @@ export abstract class JlGraphic extends Container {
});
}
fireChildSelected(child: DisplayObject) {
if (child.selected) {
const selected = child.selected;
if (selected) {
this.emit('childselected', child);
} else {
this.emit('childunselected', child);
}
const app = this.getGraphicApp();
if (app) {
app.emit('graphicchildselectedchange', child, selected);
}
}
exitChildEdit() {
this.childEdit = false;

View File

@ -271,7 +271,6 @@ export class CommonMouseTool extends AppInteractionPlugin {
graphic.invertChildSelected(target);
} else {
graphic.invertSelected();
app.fireSelectedChange(graphic);
}
}
} else {

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import {
Container,
DisplayObject,
@ -17,9 +18,11 @@ import { JlGraphic } from '../core';
import { AbsorbablePosition, VectorText } from '../graphic';
import { DraggablePoint } from '../graphic/DraggablePoint';
import {
DebouncedFunction,
angleToAxisx,
calculateLineMidpoint,
convertRectangleToPolygonPoints,
debounce,
distance,
recursiveChildren,
} from '../utils';
@ -298,6 +301,8 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
ap.tryAbsorb(...targets);
}
}
// const start = new Date().getTime();
// 事件发布
targets.forEach((target) => {
if (target.shiftStartPoint && target.shiftLastPoint) {
@ -314,6 +319,8 @@ export class GraphicTransformPlugin extends InteractionPluginBase {
);
}
});
// const dt = new Date().getTime() - start;
// console.log('拖拽耗时', `${dt}ms`, targets);
}
}
}
@ -847,11 +854,14 @@ export class BoundsGraphic extends Graphics {
alpha: 1,
};
obj: DisplayObject;
debouncedRedraw: DebouncedFunction<() => void>;
constructor(graphic: DisplayObject) {
super();
this.obj = graphic;
this.name = BoundsGraphic.Name;
this.visible = false;
this.debouncedRedraw = debounce(this.doRedraw, 50);
this.obj.on('transformstart', this.onObjTransformStart, this);
this.obj.on('transformend', this.onObjTransformEnd, this);
if (this.obj.children && this.obj.children.length > 0) {
@ -880,7 +890,6 @@ export class BoundsGraphic extends Graphics {
onGraphicRepaint(): void {
if (this.visible) {
this.redraw();
this.visible = true;
}
}
@ -892,8 +901,13 @@ export class BoundsGraphic extends Graphics {
}
redraw() {
this.debouncedRedraw(this);
}
doRedraw() {
const visible = this.visible;
this.visible = false; // 屏蔽包围框本身
const bounds = new Polygon(this.obj.localBoundsToCanvasPoints());
this.clear().lineStyle(BoundsGraphic.BoundsLineStyle).drawShape(bounds);
this.visible = visible;
}
}

View File

@ -0,0 +1,37 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface DebouncedFunction<F extends (...args: any[]) => any> {
(context: ThisParameterType<F>, ...args: Parameters<F>): void;
cancel: () => void;
}
export function debounce<F extends (...args: Parameters<F>) => any>(
fn: F,
waitMs = 250
): DebouncedFunction<F> {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
const debouncedFunction = function (
context: ThisParameterType<F>,
...args: Parameters<F>
) {
const invokeFunction = function () {
timeoutId = undefined;
fn.apply(context, args);
};
if (timeoutId !== undefined) {
console.debug('debounce clear timeout', fn);
clearTimeout(timeoutId);
}
timeoutId = setTimeout(invokeFunction, waitMs);
};
debouncedFunction.cancel = function () {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
}
};
return debouncedFunction;
}

View File

@ -3,6 +3,8 @@ import { Point, Rectangle } from 'pixi.js';
export * from './GraphicUtils';
export * from './IntersectUtils';
export * from './debounce';
export const UP: Point = new Point(0, -1);
export const DOWN: Point = new Point(0, 1);
export const LEFT: Point = new Point(-1, 0);