重构拖拽操作和图形变换代码结构
调整DisplayObject可发布的事件及事件对象 调整App可发布的事件
This commit is contained in:
parent
cd951906b0
commit
df3522e205
@ -1,5 +1,10 @@
|
||||
import { FederatedMouseEvent, Point } from 'pixi.js';
|
||||
import { GraphicDrawAssistant, JlDrawApp } from 'src/jlgraphic';
|
||||
import {
|
||||
GraphicDrawAssistant,
|
||||
GraphicInteractionPlugin,
|
||||
JlDrawApp,
|
||||
JlGraphic,
|
||||
} from 'src/jlgraphic';
|
||||
import { IIscsFanData, IscsFan, IscsFanTemplate } from './IscsFan';
|
||||
|
||||
export class IscsFanDraw extends GraphicDrawAssistant<
|
||||
@ -11,6 +16,7 @@ export class IscsFanDraw extends GraphicDrawAssistant<
|
||||
constructor(app: JlDrawApp, createData: () => IIscsFanData) {
|
||||
const template = new IscsFanTemplate();
|
||||
super(app, template, createData, IscsFan.Type, '');
|
||||
IscsFanInteraction.init(app);
|
||||
}
|
||||
|
||||
bind(): void {
|
||||
@ -46,3 +52,29 @@ export class IscsFanDraw extends GraphicDrawAssistant<
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
|
||||
export class IscsFanInteraction extends GraphicInteractionPlugin<IscsFan> {
|
||||
static Name = 'iscs_fan_transform';
|
||||
constructor(app: JlDrawApp) {
|
||||
super(IscsFanInteraction.Name, app);
|
||||
}
|
||||
static init(app: JlDrawApp) {
|
||||
return new IscsFanInteraction(app);
|
||||
}
|
||||
filter(...grahpics: JlGraphic[]): IscsFan[] | undefined {
|
||||
return grahpics
|
||||
.filter((g) => g.type === IscsFan.Type)
|
||||
.map((g) => g as IscsFan);
|
||||
}
|
||||
bind(g: IscsFan): void {
|
||||
g.eventMode = 'static';
|
||||
g.cursor = 'pointer';
|
||||
g.scalable = true;
|
||||
g.rotatable = true;
|
||||
}
|
||||
unbind(g: IscsFan): void {
|
||||
g.eventMode = 'none';
|
||||
g.scalable = false;
|
||||
g.rotatable = false;
|
||||
}
|
||||
}
|
||||
|
@ -72,9 +72,9 @@ export class Link extends JlGraphic implements ILineGraphic {
|
||||
return this.datas.points;
|
||||
}
|
||||
set linePoints(points: IPointData[]) {
|
||||
this.datas.points = points;
|
||||
this.repaint();
|
||||
this.emit('pointupdate', this, this.datas.points);
|
||||
const old = this.datas.clone();
|
||||
old.points = points;
|
||||
this.updateData(old);
|
||||
}
|
||||
getStartPoint(): IPointData {
|
||||
return this.datas.points[0];
|
||||
|
@ -13,6 +13,7 @@ import {
|
||||
GraphicApp,
|
||||
GraphicDrawAssistant,
|
||||
GraphicInteractionPlugin,
|
||||
GraphicTransformEvent,
|
||||
JlDrawApp,
|
||||
JlGraphic,
|
||||
KeyListener,
|
||||
@ -252,10 +253,12 @@ function onEditPointCreate(
|
||||
const link = g as Link;
|
||||
if (index === 0 || index == link.datas.points.length - 1) {
|
||||
// 端点
|
||||
dp.on('dragstart', () => {
|
||||
dp.on('transformstart', (e: GraphicTransformEvent) => {
|
||||
if (e.isShift()) {
|
||||
link.getGraphicApp().setOptions({
|
||||
absorbablePositions: buildAbsorbablePositions(link),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,13 @@ import {
|
||||
} from 'pixi.js';
|
||||
import { GraphicIdGenerator } from '../core/IdGenerator';
|
||||
import { GraphicData, GraphicTemplate, JlGraphic } from '../core/JlGraphic';
|
||||
import { BoundsGraphic, TransformPoints } from '../graphic';
|
||||
import {
|
||||
AppDragEvent,
|
||||
BoundsGraphic,
|
||||
GraphicTransformEvent,
|
||||
ShiftData,
|
||||
TransformPoints,
|
||||
} from '../plugins';
|
||||
import { JlOperation } from '../operation/JlOperation';
|
||||
import {
|
||||
AppInteractionPlugin,
|
||||
@ -24,7 +30,6 @@ import { CommonMouseTool } from '../plugins/CommonMousePlugin';
|
||||
import { DOWN, LEFT, RIGHT, UP, recursiveChildren } from '../utils';
|
||||
import {
|
||||
CanvasData,
|
||||
CanvasEvent,
|
||||
GraphicApp,
|
||||
GraphicAppOptions,
|
||||
ICanvasProperties,
|
||||
@ -266,20 +271,27 @@ export class JlDrawApp extends GraphicApp {
|
||||
|
||||
private appDragRecord() {
|
||||
let dragStartDatas: GraphicData[] = [];
|
||||
this.on('dragstart', () => {
|
||||
this.on('drag_op_start', (e: AppDragEvent) => {
|
||||
// 图形拖拽,记录初始数据
|
||||
// console.log('app图形拖拽开始,记录图形数据');
|
||||
if (!e.target.isCanvas()) {
|
||||
dragStartDatas = this.selectedGraphics.map((g) => g.saveData());
|
||||
}
|
||||
});
|
||||
// 图形拖拽操作监听
|
||||
this.on('dragend', () => {
|
||||
this.on('drag_op_end', () => {
|
||||
// 图形拖拽,记录操作
|
||||
// console.log('app图形拖拽结束,记录操作');
|
||||
if (dragStartDatas.length > 0) {
|
||||
const newData = this.selectedGraphics.map((g) => g.saveData());
|
||||
this.opRecord.record(
|
||||
new DragOperation(this, this.selectedGraphics, dragStartDatas, newData)
|
||||
new DragOperation(
|
||||
this,
|
||||
this.selectedGraphics,
|
||||
dragStartDatas,
|
||||
newData
|
||||
)
|
||||
);
|
||||
dragStartDatas = [];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -289,7 +301,7 @@ export class JlDrawApp extends GraphicApp {
|
||||
private initSelectedDataUpdateListen() {
|
||||
// 画布数据更新
|
||||
this.selectedData = new CanvasData(this.canvas.properties);
|
||||
this.canvas.on(CanvasEvent.propertiesupdate, () => {
|
||||
this.canvas.on('dataupdate', () => {
|
||||
if (this.selectedGraphics.length === 0) {
|
||||
this.selectedData = this.canvas.saveData();
|
||||
this.emit('propertiesupdate', this.selectedData);
|
||||
@ -309,36 +321,10 @@ export class JlDrawApp extends GraphicApp {
|
||||
}
|
||||
};
|
||||
this.on('graphicselectedchange', (g: DisplayObject, selected) => {
|
||||
let br = g.getAssistantAppend<BoundsGraphic>(BoundsGraphic.Name);
|
||||
if (!br) {
|
||||
// 绘制辅助包围框
|
||||
br = new BoundsGraphic(g);
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
g.on('repaint', graphicPropertiesUpdate, this);
|
||||
if (br) {
|
||||
br.redraw();
|
||||
br.visible = true;
|
||||
}
|
||||
} else {
|
||||
g.off('repaint', graphicPropertiesUpdate, this);
|
||||
if (br) {
|
||||
br.visible = false;
|
||||
}
|
||||
}
|
||||
if (g.scalable) {
|
||||
// 缩放点
|
||||
let sp = g.getAssistantAppend<TransformPoints>(TransformPoints.Name);
|
||||
if (!sp) {
|
||||
sp = new TransformPoints(g);
|
||||
}
|
||||
if (selected) {
|
||||
sp.update();
|
||||
sp.visible = true;
|
||||
} else {
|
||||
sp.visible = false;
|
||||
}
|
||||
}
|
||||
if (this.selectedGraphics.length === 0) {
|
||||
this.selectedData = this.canvas.properties.clone();
|
||||
@ -350,19 +336,6 @@ export class JlDrawApp extends GraphicApp {
|
||||
}
|
||||
this.emit(DrawAppEvent.propertiesupdate, this.selectedData);
|
||||
});
|
||||
this.on('graphicchildselectedchange', (_g, child, selected) => {
|
||||
let br = child.getAssistantAppend<BoundsGraphic>(BoundsGraphic.Name);
|
||||
if (!br) {
|
||||
// 绘制辅助包围框
|
||||
br = new BoundsGraphic(child);
|
||||
}
|
||||
if (selected) {
|
||||
br.redraw();
|
||||
br.visible = true;
|
||||
} else {
|
||||
br.visible = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -448,7 +421,7 @@ export class JlDrawApp extends GraphicApp {
|
||||
this.addKeyboardListener(
|
||||
new KeyListener({
|
||||
value: 'ArrowUp',
|
||||
pressTriggerEveryTime: true,
|
||||
pressTriggerAsOriginalEvent: true,
|
||||
onPress: (e: KeyboardEvent, app: GraphicApp) => {
|
||||
updateGraphicPositionOnKeyboardEvent(app, UP);
|
||||
},
|
||||
@ -460,7 +433,7 @@ export class JlDrawApp extends GraphicApp {
|
||||
this.addKeyboardListener(
|
||||
new KeyListener({
|
||||
value: 'ArrowDown',
|
||||
pressTriggerEveryTime: true,
|
||||
pressTriggerAsOriginalEvent: true,
|
||||
onPress: (e: KeyboardEvent, app: GraphicApp) => {
|
||||
updateGraphicPositionOnKeyboardEvent(app, DOWN);
|
||||
},
|
||||
@ -472,7 +445,7 @@ export class JlDrawApp extends GraphicApp {
|
||||
this.addKeyboardListener(
|
||||
new KeyListener({
|
||||
value: 'ArrowLeft',
|
||||
pressTriggerEveryTime: true,
|
||||
pressTriggerAsOriginalEvent: true,
|
||||
onPress: (e: KeyboardEvent, app: GraphicApp) => {
|
||||
updateGraphicPositionOnKeyboardEvent(app, LEFT);
|
||||
},
|
||||
@ -484,7 +457,7 @@ export class JlDrawApp extends GraphicApp {
|
||||
this.addKeyboardListener(
|
||||
new KeyListener({
|
||||
value: 'ArrowRight',
|
||||
pressTriggerEveryTime: true,
|
||||
pressTriggerAsOriginalEvent: true,
|
||||
onPress: (e: KeyboardEvent, app: GraphicApp) => {
|
||||
updateGraphicPositionOnKeyboardEvent(app, RIGHT);
|
||||
},
|
||||
@ -557,31 +530,73 @@ export class JlDrawApp extends GraphicApp {
|
||||
}
|
||||
|
||||
let dragStartDatas: GraphicData[] = [];
|
||||
function updateGraphicPositionOnKeyboardEvent(app: GraphicApp, dp: IPointData) {
|
||||
let dragStart = false;
|
||||
if (dragStartDatas.length === 0) {
|
||||
dragStartDatas = app.selectedGraphics.map((g) => g.saveData());
|
||||
dragStart = true;
|
||||
}
|
||||
|
||||
function handleArrowKeyMoveGraphics(
|
||||
app: GraphicApp,
|
||||
handler: (obj: DisplayObject) => void
|
||||
) {
|
||||
if (
|
||||
app.selectedGraphics.length === 1 &&
|
||||
app.selectedGraphics[0].hasSelectedChilds()
|
||||
) {
|
||||
recursiveChildren(app.selectedGraphics[0], (child) => {
|
||||
if (child.selected && child.draggable) {
|
||||
child.emit('transformstart', child);
|
||||
child.position.x += dp.x;
|
||||
child.position.y += dp.y;
|
||||
handler(child);
|
||||
}
|
||||
// if (child.selected && child.draggable) {
|
||||
// child.emit(
|
||||
// 'transformstart',
|
||||
// GraphicTransformEvent.init(child, 'shift')
|
||||
// );
|
||||
// child.position.x += dp.x;
|
||||
// child.position.y += dp.y;
|
||||
// }
|
||||
});
|
||||
} else {
|
||||
app.selectedGraphics.forEach((g) => {
|
||||
g.emit('transformstart', g);
|
||||
g.position.x += dp.x;
|
||||
g.position.y += dp.y;
|
||||
handler(g);
|
||||
// g.emit('transformstart', GraphicTransformEvent.init(g, 'shift'));
|
||||
// g.position.x += dp.x;
|
||||
// g.position.y += dp.y;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function updateGraphicPositionOnKeyboardEvent(app: GraphicApp, dp: IPointData) {
|
||||
let dragStart = false;
|
||||
if (dragStartDatas.length === 0) {
|
||||
dragStartDatas = app.selectedGraphics.map((g) => g.saveData());
|
||||
dragStart = true;
|
||||
}
|
||||
handleArrowKeyMoveGraphics(app, (g) => {
|
||||
if (dragStart) {
|
||||
g.shiftStartPoint = g.position.clone();
|
||||
g.emit(
|
||||
'transformstart',
|
||||
GraphicTransformEvent.shift(g, ShiftData.new(g.shiftStartPoint))
|
||||
);
|
||||
} else {
|
||||
g.shiftLastPoint = g.position.clone();
|
||||
}
|
||||
g.position.x += dp.x;
|
||||
g.position.y += dp.y;
|
||||
if (!dragStart) {
|
||||
if (g.shiftStartPoint && g.shiftLastPoint) {
|
||||
g.emit(
|
||||
'transforming',
|
||||
GraphicTransformEvent.shift(
|
||||
g,
|
||||
ShiftData.new(
|
||||
g.shiftStartPoint,
|
||||
g.position.clone(),
|
||||
g.shiftLastPoint
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
function recordGraphicMoveOperation(app: GraphicApp) {
|
||||
if (
|
||||
dragStartDatas.length > 0 &&
|
||||
@ -591,20 +606,18 @@ function recordGraphicMoveOperation(app: GraphicApp) {
|
||||
app.opRecord.record(
|
||||
new DragOperation(app, app.selectedGraphics, dragStartDatas, newData)
|
||||
);
|
||||
if (
|
||||
app.selectedGraphics.length === 1 &&
|
||||
app.selectedGraphics[0].hasSelectedChilds()
|
||||
) {
|
||||
recursiveChildren(app.selectedGraphics[0], (child) => {
|
||||
if (child.selected && child.draggable) {
|
||||
child.emit('transformend', child);
|
||||
|
||||
handleArrowKeyMoveGraphics(app, (g) => {
|
||||
if (g.shiftStartPoint) {
|
||||
g.emit(
|
||||
'transformend',
|
||||
GraphicTransformEvent.shift(
|
||||
g,
|
||||
ShiftData.new(g.shiftStartPoint, g.position.clone())
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
app.selectedGraphics.forEach((g) => {
|
||||
g.emit('transformend', g);
|
||||
});
|
||||
}
|
||||
}
|
||||
dragStartDatas = [];
|
||||
}
|
||||
|
@ -12,11 +12,11 @@ import {
|
||||
import { GraphicQueryStore, GraphicStore } from '../core/GraphicStore';
|
||||
import { GraphicIdGenerator } from '../core/IdGenerator';
|
||||
import {
|
||||
JlGraphic,
|
||||
GraphicData,
|
||||
GraphicState,
|
||||
GraphicTemplate,
|
||||
GraphicTransform,
|
||||
JlGraphic,
|
||||
} from '../core/JlGraphic';
|
||||
import { AbsorbablePosition } from '../graphic';
|
||||
import {
|
||||
@ -28,11 +28,13 @@ import {
|
||||
import { OperationRecord } from '../operation/JlOperation';
|
||||
import {
|
||||
CommonMouseTool,
|
||||
GraphicDragEvent,
|
||||
GraphicTransformPlugin,
|
||||
IMouseToolOptions,
|
||||
} from '../plugins';
|
||||
import { GraphicCopyPlugin } from '../plugins/CopyPlugin';
|
||||
import {
|
||||
AppDragEvent,
|
||||
DragPlugin,
|
||||
InteractionPlugin,
|
||||
InteractionPluginType,
|
||||
ViewportMovePlugin,
|
||||
@ -79,11 +81,25 @@ export class CanvasData implements ICanvasProperties {
|
||||
this.viewportTransform = properties.viewportTransform;
|
||||
}
|
||||
|
||||
copyFrom(properties: ICanvasProperties): void {
|
||||
this.width = properties.width;
|
||||
this.height = properties.height;
|
||||
copyFrom(properties: ICanvasProperties): boolean {
|
||||
let sizeChange = false;
|
||||
if (properties.width <= 0 || properties.height <= 0) {
|
||||
console.error('画布宽度/高度不能小于等于0');
|
||||
} else {
|
||||
const width = Math.floor(properties.width);
|
||||
const height = Math.floor(properties.height);
|
||||
if (this.width != width) {
|
||||
this.width = width;
|
||||
sizeChange = true;
|
||||
}
|
||||
if (this.height != height) {
|
||||
this.height = height;
|
||||
sizeChange = true;
|
||||
}
|
||||
}
|
||||
this.backgroundColor = properties.backgroundColor;
|
||||
this.viewportTransform = properties.viewportTransform;
|
||||
return sizeChange;
|
||||
}
|
||||
|
||||
clone(): CanvasData {
|
||||
@ -92,13 +108,6 @@ export class CanvasData implements ICanvasProperties {
|
||||
}
|
||||
}
|
||||
|
||||
export enum CanvasEvent {
|
||||
canvassizechange = 'canvassizechange',
|
||||
propertiesupdate = 'propertiesupdate',
|
||||
enterAbsorbableArea = 'enter-absorbable-area',
|
||||
outAbsorbableArea = 'out-absorbable-area',
|
||||
}
|
||||
|
||||
export class JlCanvas extends Container {
|
||||
__JlCanvas = true;
|
||||
type = 'Canvas';
|
||||
@ -169,19 +178,15 @@ export class JlCanvas extends Container {
|
||||
|
||||
update(properties: ICanvasProperties) {
|
||||
// 更新画布
|
||||
if (
|
||||
this.properties.width !== properties.width ||
|
||||
this.properties.height !== properties.height
|
||||
) {
|
||||
this._properties.copyFrom(properties);
|
||||
this.emit(CanvasEvent.canvassizechange, this);
|
||||
} else {
|
||||
this._properties.copyFrom(properties);
|
||||
}
|
||||
const old = this.properties.clone();
|
||||
const sizeChange = this._properties.copyFrom(properties);
|
||||
this.repaint();
|
||||
if (sizeChange) {
|
||||
this.app.updateViewport();
|
||||
}
|
||||
const vp = this.getViewport();
|
||||
vp.loadTransform(properties.viewportTransform);
|
||||
this.emit(CanvasEvent.propertiesupdate, this);
|
||||
this.emit('dataupdate', this.properties, old);
|
||||
}
|
||||
|
||||
addChild<U extends DisplayObject[]>(...children: U): U[0] {
|
||||
@ -274,15 +279,11 @@ export interface GraphicAppEvents extends GlobalMixins.GraphicAppEvents {
|
||||
'interaction-plugin-pause': [activeTool: InteractionPlugin]; // 交互插件停止
|
||||
'options-update': [options: GraphicAppOptions]; // 配置更新
|
||||
graphicselectedchange: [graphic: JlGraphic, selected: boolean];
|
||||
graphicchildselectedchange: [
|
||||
graphic: JlGraphic,
|
||||
child: DisplayObject,
|
||||
selected: boolean
|
||||
];
|
||||
scaleend: [obj: DisplayObject];
|
||||
dragstart: [event: GraphicDragEvent];
|
||||
dragmove: [event: GraphicDragEvent];
|
||||
dragend: [event: GraphicDragEvent];
|
||||
graphicchildselectedchange: [child: DisplayObject, selected: boolean];
|
||||
'viewport-scaled': [vp: Viewport];
|
||||
drag_op_start: [event: AppDragEvent];
|
||||
drag_op_move: [event: AppDragEvent];
|
||||
drag_op_end: [event: AppDragEvent];
|
||||
destroy: [app: GraphicApp];
|
||||
}
|
||||
|
||||
@ -298,6 +299,10 @@ export interface IGraphicAppConfig {
|
||||
* 最大保存的操作记录数,默认100,越大越占用内存资源
|
||||
*/
|
||||
maxOperationRecords?: number;
|
||||
/**
|
||||
* 拖拽触发移动门槛
|
||||
*/
|
||||
threshold?: number;
|
||||
/**
|
||||
* 通用鼠标工具选项
|
||||
*/
|
||||
@ -413,11 +418,7 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
|
||||
|
||||
// 监听并通知缩放变化事件
|
||||
this.viewport.on('zoomed-end', () => {
|
||||
this.emit('scaleend', this.viewport);
|
||||
});
|
||||
|
||||
this.canvas.on(CanvasEvent.canvassizechange, () => {
|
||||
this.updateViewport();
|
||||
this.emit('viewport-scaled', this.viewport);
|
||||
});
|
||||
|
||||
this.opRecord = new OperationRecord();
|
||||
@ -430,9 +431,15 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
|
||||
// 添加通用交互插件
|
||||
const tool = new CommonMouseTool(this);
|
||||
tool.resume();
|
||||
|
||||
// drag插件
|
||||
DragPlugin.new(this).resume();
|
||||
// 视口移动插件
|
||||
ViewportMovePlugin.new(this);
|
||||
|
||||
// 图形变换插件
|
||||
GraphicTransformPlugin.new(this).resume();
|
||||
|
||||
this.app.ticker.add((dt: number) => {
|
||||
this.queryStore.getAllGraphics().forEach((g) => {
|
||||
g.animationMap.forEach((animation) => {
|
||||
@ -686,10 +693,10 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
|
||||
graphic.repaint();
|
||||
this.emit('graphicstored', graphic);
|
||||
graphic.on('childselected', (child) => {
|
||||
this.emit('graphicchildselectedchange', graphic, child, true);
|
||||
this.emit('graphicchildselectedchange', child, true);
|
||||
});
|
||||
graphic.on('childunselected', (child) => {
|
||||
this.emit('graphicchildselectedchange', graphic, child, false);
|
||||
this.emit('graphicchildselectedchange', child, false);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -814,7 +821,7 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
|
||||
this.interactionPluginMap.delete(plugin.name);
|
||||
}
|
||||
|
||||
private updateViewport(domWidth?: number, domHeight?: number): void {
|
||||
updateViewport(domWidth?: number, domHeight?: number): void {
|
||||
let screenWidth = this.viewport.screenWidth;
|
||||
let screenHeight = this.viewport.screenHeight;
|
||||
if (domWidth) {
|
||||
|
@ -29,7 +29,8 @@ DisplayObject.prototype._childEdit = false;
|
||||
DisplayObject.prototype._transformSave = false;
|
||||
DisplayObject.prototype._assistantAppendMap = null;
|
||||
DisplayObject.prototype._draggable = false;
|
||||
DisplayObject.prototype._dragStartPoint = null;
|
||||
DisplayObject.prototype._shiftStartPoint = null;
|
||||
DisplayObject.prototype._shiftLastPoint = null;
|
||||
DisplayObject.prototype._scalable = false;
|
||||
DisplayObject.prototype._keepAspectRatio = true;
|
||||
DisplayObject.prototype._rotatable = false;
|
||||
@ -82,12 +83,20 @@ Object.defineProperties(DisplayObject.prototype, {
|
||||
this._draggable = v;
|
||||
},
|
||||
},
|
||||
dragStartPoint: {
|
||||
shiftStartPoint: {
|
||||
get() {
|
||||
return this._dragStartPoint;
|
||||
return this._shiftStartPoint;
|
||||
},
|
||||
set(v) {
|
||||
this._dragStartPoint = v;
|
||||
this._shiftStartPoint = v;
|
||||
},
|
||||
},
|
||||
shiftLastPoint: {
|
||||
get() {
|
||||
return this._shiftLastPoint;
|
||||
},
|
||||
set(v) {
|
||||
this._shiftLastPoint = v;
|
||||
},
|
||||
},
|
||||
scalable: {
|
||||
@ -255,6 +264,9 @@ DisplayObject.prototype.getCanvas = function getCanvas() {
|
||||
}
|
||||
throw new Error(`图形${this.name}不在画布中`);
|
||||
};
|
||||
DisplayObject.prototype.isCanvas = function isCanvas(): boolean {
|
||||
return Object.hasOwn(this, '__JlCanvas');
|
||||
};
|
||||
DisplayObject.prototype.getViewport = function getViewport() {
|
||||
const canvas = this.getCanvas();
|
||||
return canvas.parent as Viewport;
|
||||
@ -815,7 +827,7 @@ export abstract class JlGraphic extends Container {
|
||||
this.getDatas().copyFrom(data);
|
||||
this.onDataChange(data, old);
|
||||
this.loadTransformFrom(data);
|
||||
this.emit('dataupdate', this, data, old);
|
||||
this.emit('dataupdate', this.getDatas(), old);
|
||||
this.repaint();
|
||||
}
|
||||
return update;
|
||||
@ -850,7 +862,7 @@ export abstract class JlGraphic extends Container {
|
||||
this.getStates().copyFrom(state);
|
||||
this.onStateChange(state, old);
|
||||
stateChange = true;
|
||||
this.emit('stateupdate', this, state, old);
|
||||
this.emit('stateupdate', this.getStates(), old);
|
||||
this.repaint();
|
||||
}
|
||||
return stateChange;
|
||||
|
34
src/jlgraphic/global.d.ts
vendored
34
src/jlgraphic/global.d.ts
vendored
@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
declare namespace GlobalMixins {
|
||||
type JlCanvasType = import('./app').JlCanvas;
|
||||
type CanvasProperties = import('./app').ICanvasProperties;
|
||||
@ -6,33 +7,21 @@ declare namespace GlobalMixins {
|
||||
type GraphicData = import('./core').GraphicData;
|
||||
type GraphicState = import('./core').GraphicState;
|
||||
type GraphicTransform = import('./core').GraphicTransform;
|
||||
type AppDragEventType = import('./plugins').GraphicDragEvent;
|
||||
type BoundsGraphic = import('./graphic').BoundsGraphic;
|
||||
type GraphicTransformEvent = import('./plugins').GraphicTransformEvent;
|
||||
type BoundsGraphic = import('./plugins').BoundsGraphic;
|
||||
type IPointDataType = import('pixi.js').IPointData;
|
||||
type PointType = import('pixi.js').Point;
|
||||
type DisplayObjectType = import('pixi.js').DisplayObject;
|
||||
type ContainerType = import('pixi.js').Container;
|
||||
interface DisplayObjectEvents {
|
||||
canvassizechange: [JlCanvasType];
|
||||
'enter-absorbable-area': [number | undefined, number | undefined];
|
||||
'out-absorbable-area': [number | undefined, number | undefined];
|
||||
transforming: [JlCanvasType];
|
||||
dataupdate: [JlGraphicType, GraphicData, GraphicData | undefined];
|
||||
pointupdate: [obj: DisplayObjectType, points: IPointDataType[]];
|
||||
stateupdate: [JlGraphicType, GraphicState, GraphicState | undefined];
|
||||
dataupdate: [newVal: any, oldVal: any];
|
||||
stateupdate: [newVal: any, oldVal: any];
|
||||
repaint: [DisplayObjectType];
|
||||
propertiesupdate: [JlGraphicType | JlCanvasType];
|
||||
dragstart: [AppDragEventType];
|
||||
dragmove: [AppDragEventType];
|
||||
dragend: [AppDragEventType];
|
||||
scalestart: [DisplayObjectType];
|
||||
scalemove: [DisplayObjectType];
|
||||
scaleend: [DisplayObjectType];
|
||||
rotatestart: [DisplayObjectType];
|
||||
rotatemove: [DisplayObjectType];
|
||||
rotateend: [DisplayObjectType];
|
||||
transformstart: [DisplayObjectType];
|
||||
transformend: [DisplayObjectType];
|
||||
transformstart: [e: GraphicTransformEvent];
|
||||
transforming: [e: GraphicTransformEvent];
|
||||
transformend: [e: GraphicTransformEvent];
|
||||
selected: [DisplayObjectType];
|
||||
unselected: [DisplayObjectType];
|
||||
childselected: [DisplayObjectType];
|
||||
@ -57,8 +46,10 @@ declare namespace GlobalMixins {
|
||||
assistantAppendMap: Map<string, DisplayObjectType>;
|
||||
_draggable: boolean; // 是否可拖拽
|
||||
draggable: boolean;
|
||||
_dragStartPoint: PointType | null; // 拖拽起始坐标
|
||||
dragStartPoint: PointType | null;
|
||||
_shiftStartPoint: PointType | null; // 位移起始坐标
|
||||
shiftStartPoint: PointType | null;
|
||||
_shiftLastPoint: PointType | null; // 位移上一个事件时坐标
|
||||
shiftLastPoint: PointType | null;
|
||||
_scalable: boolean; // 是否可缩放
|
||||
scalable: boolean;
|
||||
_keepAspectRatio: boolean; // 缩放是否保持纵横比,默认保持
|
||||
@ -86,6 +77,7 @@ declare namespace GlobalMixins {
|
||||
onRemoveFromCanvas(): void; //从画布移除处理
|
||||
isInCanvas(): boolean; // 是否添加到画布中
|
||||
getCanvas(): JlCanvasType; // 获取所在画布
|
||||
isCanvas(): boolean; // 是否画布对象
|
||||
getViewport(): Viewport; // 获取视口
|
||||
getGraphicApp(): GraphicApp; // 获取图形app
|
||||
localToCanvasPoint(p: IPointData): PointType; // 图形本地坐标转为画布坐标
|
||||
|
@ -32,7 +32,7 @@ export class DraggablePoint extends Graphics implements VectorGraphic {
|
||||
constructor(point: IPointData) {
|
||||
super(DraggablePointGraphic.geometry);
|
||||
this.position.copyFrom(point);
|
||||
this.interactive = true;
|
||||
this.eventMode = 'static';
|
||||
this.draggable = true;
|
||||
this.cursor = 'crosshair';
|
||||
VectorGraphicUtil.handle(this);
|
||||
|
@ -18,14 +18,14 @@ export class VectorGraphicUtil {
|
||||
): void {
|
||||
if (!obj.scaledListenerOn) {
|
||||
obj.scaledListenerOn = true;
|
||||
obj.getGraphicApp().on('scaleend', onScaleChange);
|
||||
obj.getGraphicApp().on('viewport-scaled', onScaleChange);
|
||||
}
|
||||
};
|
||||
const unregisterScaleChange = function unregisterScaleChange(
|
||||
obj: VectorGraphic
|
||||
): void {
|
||||
obj.scaledListenerOn = false;
|
||||
obj.getGraphicApp().off('scaleend', onScaleChange);
|
||||
obj.getGraphicApp().off('viewport-scaled', onScaleChange);
|
||||
};
|
||||
obj.onAddToCanvas = function onAddToCanvas() {
|
||||
obj.updateOnScaled();
|
||||
|
@ -2,4 +2,3 @@ export * from './VectorGraphic';
|
||||
export * from './VectorText';
|
||||
export * from './DraggablePoint';
|
||||
export * from './AbsorbablePosition';
|
||||
export * from './GraphicTransformGraphics';
|
||||
|
@ -1,77 +1,11 @@
|
||||
import {
|
||||
Container,
|
||||
DisplayObject,
|
||||
FederatedMouseEvent,
|
||||
Graphics,
|
||||
Point,
|
||||
} from 'pixi.js';
|
||||
import { DisplayObject, FederatedMouseEvent, Graphics, Point } from 'pixi.js';
|
||||
import { GraphicApp, JlCanvas } from '../app';
|
||||
import { JlGraphic } from '../core';
|
||||
import { AppInteractionPlugin } from './InteractionPlugin';
|
||||
import { AbsorbablePosition } from '../graphic';
|
||||
import { recursiveChildren } from '../utils';
|
||||
|
||||
export class GraphicDragEvent {
|
||||
/**
|
||||
* 原始事件
|
||||
*/
|
||||
event: FederatedMouseEvent;
|
||||
app: GraphicApp;
|
||||
target: DisplayObject;
|
||||
start: Point;
|
||||
point: Point;
|
||||
dx = 0; // 距上一个move点的x移动距离
|
||||
dy = 0; // 距上一个move点的y移动距离
|
||||
constructor(
|
||||
app: GraphicApp,
|
||||
event: FederatedMouseEvent,
|
||||
target: DisplayObject,
|
||||
start: Point,
|
||||
point: Point,
|
||||
dx?: number,
|
||||
dy?: number
|
||||
) {
|
||||
this.app = app;
|
||||
this.event = event;
|
||||
this.target = target;
|
||||
this.start = start;
|
||||
this.point = point;
|
||||
if (dx) {
|
||||
this.dx = dx;
|
||||
}
|
||||
if (dy) {
|
||||
this.dy = dy;
|
||||
}
|
||||
}
|
||||
// static buildJlGraphicDragEvent(
|
||||
// graphic: JlGraphic,
|
||||
// source: GraphicDragEvent
|
||||
// ): GraphicDragEvent {
|
||||
// const e = new GraphicDragEvent(
|
||||
// source.app,
|
||||
// source.event,
|
||||
// source.target,
|
||||
// source.start,
|
||||
// source.point,
|
||||
// source.dx,
|
||||
// source.dy
|
||||
// );
|
||||
// return e;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 距拖拽起始点的x方向距离
|
||||
*/
|
||||
public get dsx(): number {
|
||||
return this.point.x - this.start.x;
|
||||
}
|
||||
/**
|
||||
* 距拖拽起始点的y方向距离
|
||||
*/
|
||||
public get dsy(): number {
|
||||
return this.point.y - this.start.y;
|
||||
}
|
||||
}
|
||||
import {
|
||||
AppDragEvent,
|
||||
AppInteractionPlugin,
|
||||
ViewportMovePlugin,
|
||||
} from './InteractionPlugin';
|
||||
|
||||
type GraphicSelectFilter = (graphic: JlGraphic) => boolean;
|
||||
|
||||
@ -88,10 +22,6 @@ export interface IMouseToolOptions {
|
||||
* 是否启用鼠标滚轮缩放,默认启用
|
||||
*/
|
||||
wheelZoom?: boolean;
|
||||
/**
|
||||
* boxSelect 移动多少像素触发, 默认3
|
||||
*/
|
||||
threshold?: number;
|
||||
/**
|
||||
* 可选择图形过滤器
|
||||
*/
|
||||
@ -102,13 +32,11 @@ class CompleteMouseToolOptions implements IMouseToolOptions {
|
||||
boxSelect: boolean;
|
||||
viewportDrag: boolean;
|
||||
wheelZoom: boolean;
|
||||
threshold: number;
|
||||
selectFilter?: GraphicSelectFilter | undefined;
|
||||
constructor() {
|
||||
this.boxSelect = true;
|
||||
this.viewportDrag = true;
|
||||
this.wheelZoom = true;
|
||||
this.threshold = 3;
|
||||
}
|
||||
update(options: IMouseToolOptions) {
|
||||
if (options.boxSelect != undefined) {
|
||||
@ -120,9 +48,6 @@ class CompleteMouseToolOptions implements IMouseToolOptions {
|
||||
if (options.wheelZoom != undefined) {
|
||||
this.wheelZoom = options.wheelZoom;
|
||||
}
|
||||
if (options.threshold != undefined) {
|
||||
this.threshold = options.threshold;
|
||||
}
|
||||
this.selectFilter = options.selectFilter;
|
||||
}
|
||||
}
|
||||
@ -135,29 +60,25 @@ const DefaultSelectToolOptions: CompleteMouseToolOptions =
|
||||
*/
|
||||
export class CommonMouseTool extends AppInteractionPlugin {
|
||||
static Name = 'mouse-tool';
|
||||
static SelectBox = '__select_box';
|
||||
options: CompleteMouseToolOptions;
|
||||
box: Graphics = new Graphics();
|
||||
box: Graphics;
|
||||
leftDownTarget: DisplayObject | null = null;
|
||||
start: Point | null = null;
|
||||
last: Point | null = null;
|
||||
|
||||
drag = false;
|
||||
graphicSelect = false;
|
||||
|
||||
rightTarget: DisplayObject | null = null;
|
||||
|
||||
/**
|
||||
* 可吸附位置列表
|
||||
*/
|
||||
absorbablePositions?: AbsorbablePosition[];
|
||||
apContainer: Container;
|
||||
static AbsorbablePosisiontsName = '__AbsorbablePosisionts';
|
||||
|
||||
constructor(graphicApp: GraphicApp) {
|
||||
super(CommonMouseTool.Name, graphicApp);
|
||||
this.options = DefaultSelectToolOptions;
|
||||
this.apContainer = new Container();
|
||||
this.apContainer.name = CommonMouseTool.AbsorbablePosisiontsName;
|
||||
this.app.canvas.addAssistantAppend(this.apContainer);
|
||||
|
||||
this.box = new Graphics();
|
||||
this.box.name = CommonMouseTool.SelectBox;
|
||||
this.box.visible = false;
|
||||
this.app.canvas.addAssistantAppends(this.box);
|
||||
|
||||
graphicApp.on('options-update', (options) => {
|
||||
if (options.mouseToolOptions) {
|
||||
this.options.update(options.mouseToolOptions);
|
||||
@ -166,17 +87,16 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
this.resume();
|
||||
}
|
||||
}
|
||||
if (options.absorbablePositions) {
|
||||
this.absorbablePositions = options.absorbablePositions;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bind(): void {
|
||||
const canvas = this.app.canvas;
|
||||
canvas.interactive = true;
|
||||
canvas.on('mousedown', this.onMouseDown, this);
|
||||
canvas.on('mouseup', this.onMouseUp, this);
|
||||
this.app.on('drag_op_start', this.onDragStart, this);
|
||||
this.app.on('drag_op_move', this.onDragMove, this);
|
||||
this.app.on('drag_op_end', this.onDragEnd, this);
|
||||
if (this.options.viewportDrag) {
|
||||
this.app.viewport.drag({
|
||||
mouseButtons: 'right',
|
||||
@ -196,9 +116,11 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
// 确保所有事件取消监听
|
||||
canvas.off('mousedown', this.onMouseDown, this);
|
||||
canvas.off('mouseup', this.onMouseUp, this);
|
||||
canvas.off('mousemove', this.onDrag, this);
|
||||
canvas.off('mouseup', this.onDragEnd, this);
|
||||
canvas.off('mouseupoutside', this.onDragEnd, this);
|
||||
|
||||
this.app.off('drag_op_start', this.onDragStart, this);
|
||||
this.app.off('drag_op_move', this.onDragMove, this);
|
||||
this.app.off('drag_op_end', this.onDragEnd, this);
|
||||
|
||||
this.app.viewport.plugins.remove('drag');
|
||||
canvas.off('rightdown', this.setCursor, this);
|
||||
canvas.off('rightup', this.resumeCursor, this);
|
||||
@ -208,6 +130,41 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
onDragStart(event: AppDragEvent) {
|
||||
// console.log(
|
||||
// 'start',
|
||||
// `pointerType:${event.original.pointerType},pointerId:${event.original.pointerId},button: ${event.original.button},buttons:${event.original.buttons}`
|
||||
// );
|
||||
if (this.boxSelect && event.target.isCanvas() && event.isLeftButton) {
|
||||
this.box.visible = true;
|
||||
this.app.interactionPlugin(ViewportMovePlugin.Name).resume();
|
||||
}
|
||||
this.drag = true;
|
||||
}
|
||||
|
||||
onDragMove(event: AppDragEvent) {
|
||||
// console.log(
|
||||
// 'moving',
|
||||
// `pointerType:${event.original.pointerType},pointerId:${event.original.pointerId},button: ${event.original.button},buttons:${event.original.buttons}`
|
||||
// );
|
||||
if (this.boxSelect && event.target.isCanvas()) {
|
||||
this.boxSelectDraw(event.start, event.end);
|
||||
}
|
||||
}
|
||||
onDragEnd(event: AppDragEvent) {
|
||||
// console.log(
|
||||
// 'end',
|
||||
// `pointerType:${event.original.pointerType},pointerId:${event.original.pointerId},button: ${event.original.button},buttons:${event.original.buttons}`
|
||||
// );
|
||||
if (this.boxSelect && event.target.isCanvas() && event.isLeftButton) {
|
||||
this.boxSelectDraw(event.start, event.end);
|
||||
this.boxSelectGraphicCheck();
|
||||
this.app.interactionPlugin(ViewportMovePlugin.Name).pause();
|
||||
this.box.clear();
|
||||
this.box.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
setCursor(e: FederatedMouseEvent) {
|
||||
this.rightTarget = e.target as DisplayObject;
|
||||
if (e.target instanceof JlCanvas && this.app.app.view.style) {
|
||||
@ -228,19 +185,9 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
|
||||
onMouseDown(e: FederatedMouseEvent) {
|
||||
this.leftDownTarget = e.target as DisplayObject;
|
||||
const canvas = this.app.canvas;
|
||||
let enableDrag = false;
|
||||
|
||||
this.graphicSelect = false;
|
||||
if (e.target instanceof JlCanvas) {
|
||||
// 画布
|
||||
if (this.options.boxSelect) {
|
||||
// 框选
|
||||
enableDrag = true;
|
||||
}
|
||||
} else {
|
||||
// 图形
|
||||
const graphic = (e.target as DisplayObject).getGraphic();
|
||||
const graphic = this.leftDownTarget.getGraphic();
|
||||
if (graphic) {
|
||||
const app = this.app;
|
||||
// 图形选中
|
||||
@ -260,29 +207,6 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
graphic.exitChildEdit();
|
||||
}
|
||||
}
|
||||
// 判断是否开启拖拽
|
||||
if (
|
||||
!this.leftDownTarget.isAssistantAppend() &&
|
||||
!graphic.draggable &&
|
||||
!this.leftDownTarget.draggable
|
||||
) {
|
||||
console.debug(
|
||||
'图形未开启拖拽,若需开启,设置draggable = true',
|
||||
graphic,
|
||||
this.leftDownTarget
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
enableDrag = true;
|
||||
}
|
||||
|
||||
if (enableDrag) {
|
||||
// 初始化拖拽监听
|
||||
// console.log('drag 开始');
|
||||
this.start = this.app.toCanvasCoordinates(e.global);
|
||||
canvas.on('mousemove', this.onDrag, this);
|
||||
canvas.on('mouseupoutside', this.onDragEnd, this);
|
||||
}
|
||||
}
|
||||
|
||||
@ -292,9 +216,7 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
*/
|
||||
onMouseUp(e: FederatedMouseEvent) {
|
||||
const app = this.app;
|
||||
if (this.drag) {
|
||||
this.onDragEnd(e);
|
||||
} else {
|
||||
if (!this.drag) {
|
||||
const target = e.target as DisplayObject;
|
||||
const graphic = (e.target as DisplayObject).getGraphic();
|
||||
if (
|
||||
@ -346,12 +268,8 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
* 清理缓存
|
||||
*/
|
||||
clearCache() {
|
||||
this.start = null;
|
||||
this.last = null;
|
||||
this.drag = false;
|
||||
this.leftDownTarget = null;
|
||||
this.box.clear();
|
||||
this.app.canvas.removeChild(this.box);
|
||||
}
|
||||
|
||||
public get boxSelect(): boolean | undefined {
|
||||
@ -362,10 +280,6 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
return this.options.selectFilter;
|
||||
}
|
||||
|
||||
public get threshold(): number {
|
||||
return this.options.threshold;
|
||||
}
|
||||
|
||||
/**
|
||||
* 框选图形绘制并检查
|
||||
*/
|
||||
@ -408,175 +322,4 @@ export class CommonMouseTool extends AppInteractionPlugin {
|
||||
});
|
||||
app.updateSelected(...selects);
|
||||
}
|
||||
|
||||
onDrag(e: FederatedMouseEvent) {
|
||||
if (this.start) {
|
||||
const current = this.app.toCanvasCoordinates(e.global);
|
||||
const dragStart =
|
||||
Math.abs(current.x - this.start.x) > this.threshold ||
|
||||
Math.abs(current.y - this.start.y) > this.threshold;
|
||||
if (this.leftDownTarget instanceof JlCanvas) {
|
||||
// 画布drag
|
||||
if (!this.drag && dragStart) {
|
||||
this.drag = true;
|
||||
this.app.canvas.addChild(this.box);
|
||||
}
|
||||
if (this.drag) {
|
||||
this.boxSelectDraw(this.start, current);
|
||||
// this.boxSelectGraphicCheck();
|
||||
}
|
||||
} else {
|
||||
// 图形drag
|
||||
if (this.start && !this.drag && dragStart) {
|
||||
// 启动拖拽
|
||||
if (this.leftDownTarget) {
|
||||
const s = this.start;
|
||||
const dragEvent = new GraphicDragEvent(
|
||||
this.app,
|
||||
e,
|
||||
this.leftDownTarget,
|
||||
s,
|
||||
s
|
||||
);
|
||||
this.handleDragEvent(dragEvent, 'dragstart');
|
||||
}
|
||||
this.last = this.start.clone();
|
||||
this.drag = true;
|
||||
}
|
||||
|
||||
// drag移动处理
|
||||
if (this.drag && this.last && this.start) {
|
||||
const current = this.app.toCanvasCoordinates(e.global);
|
||||
const dx = current.x - this.last.x;
|
||||
const dy = current.y - this.last.y;
|
||||
if (this.leftDownTarget) {
|
||||
const dragEvent = new GraphicDragEvent(
|
||||
this.app,
|
||||
e,
|
||||
this.leftDownTarget,
|
||||
this.start,
|
||||
current,
|
||||
dx,
|
||||
dy
|
||||
);
|
||||
this.handleDragEvent(dragEvent, 'dragmove');
|
||||
}
|
||||
this.last = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* drag操作结束处理
|
||||
* @param e
|
||||
*/
|
||||
onDragEnd(e: FederatedMouseEvent) {
|
||||
if (this.leftDownTarget instanceof JlCanvas) {
|
||||
this.boxSelectGraphicCheck();
|
||||
} else {
|
||||
if (this.drag) {
|
||||
const end = this.app.toCanvasCoordinates(e.global);
|
||||
if (this.start && this.leftDownTarget) {
|
||||
const dragEvent = new GraphicDragEvent(
|
||||
this.app,
|
||||
e,
|
||||
this.leftDownTarget,
|
||||
this.start,
|
||||
end
|
||||
);
|
||||
this.handleDragEvent(dragEvent, 'dragend');
|
||||
}
|
||||
}
|
||||
}
|
||||
const canvas = this.app.canvas;
|
||||
canvas.off('mousemove', this.onDrag, this);
|
||||
canvas.off('mouseup', this.onDragEnd, this);
|
||||
canvas.off('mouseupoutside', this.onDragEnd, this);
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理拖拽事件(执行、发布)
|
||||
* @param dragEvent
|
||||
* @param type
|
||||
*/
|
||||
handleDragEvent(
|
||||
dragEvent: GraphicDragEvent,
|
||||
type: 'dragstart' | 'dragmove' | 'dragend'
|
||||
): void {
|
||||
const graphic = dragEvent.target.getGraphic();
|
||||
const targets: DisplayObject[] = [];
|
||||
if (!graphic) {
|
||||
targets.push(dragEvent.target);
|
||||
} else {
|
||||
if (
|
||||
dragEvent.target.isGraphicChild() &&
|
||||
dragEvent.target.selected &&
|
||||
dragEvent.target.draggable
|
||||
) {
|
||||
// 图形子元素
|
||||
recursiveChildren(graphic, (child) => {
|
||||
if (child.selected && child.draggable) {
|
||||
targets.push(child);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 图形对象
|
||||
targets.push(...this.app.selectedGraphics);
|
||||
}
|
||||
}
|
||||
if (type === 'dragstart') {
|
||||
targets.forEach((target) => {
|
||||
target.dragStartPoint = target.position.clone();
|
||||
target.emit(type, dragEvent);
|
||||
if (!target.isAssistantAppend()) {
|
||||
target.emit('transformstart', target);
|
||||
}
|
||||
});
|
||||
// 显示吸附图形
|
||||
if (this.absorbablePositions && this.absorbablePositions.length > 0) {
|
||||
this.apContainer.removeChildren();
|
||||
this.apContainer.addChild(...this.absorbablePositions);
|
||||
}
|
||||
} else {
|
||||
// 处理位移
|
||||
targets.forEach((target) => {
|
||||
if (target.dragStartPoint) {
|
||||
target.position.set(
|
||||
target.dragStartPoint.x + dragEvent.dsx,
|
||||
target.dragStartPoint.y + dragEvent.dsy
|
||||
);
|
||||
}
|
||||
});
|
||||
// 处理吸附
|
||||
if (this.absorbablePositions) {
|
||||
for (let i = 0; i < this.absorbablePositions.length; i++) {
|
||||
const ap = this.absorbablePositions[i];
|
||||
if (ap.tryAbsorb(...targets)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 事件发布
|
||||
targets.forEach((target) => {
|
||||
target.emit(type, dragEvent);
|
||||
if (type === 'dragend' && !target.isAssistantAppend()) {
|
||||
target.emit('transformend', target);
|
||||
}
|
||||
});
|
||||
|
||||
if (type === 'dragend') {
|
||||
if (this.app.selectedGraphics.length == 1) {
|
||||
this.app.selectedGraphics[0].repaint();
|
||||
}
|
||||
targets.forEach((target) => {
|
||||
target.dragStartPoint = null;
|
||||
});
|
||||
// 移除吸附图形
|
||||
this.absorbablePositions = [];
|
||||
this.apContainer.removeChildren();
|
||||
}
|
||||
}
|
||||
this.app.emit(type, dragEvent);
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,9 @@ import {
|
||||
import { JlGraphic } from '../core';
|
||||
import { DraggablePoint } from '../graphic';
|
||||
import { calculateMirrorPoint, distance2 } from '../utils';
|
||||
import { GraphicDragEvent } from './CommonMousePlugin';
|
||||
import { MenuItemOptions, MenuOptions } from '../ui/Menu';
|
||||
import { ContextMenu } from '../ui/ContextMenu';
|
||||
import { GraphicTransformEvent, ShiftData } from './GraphicTransformPlugin';
|
||||
|
||||
export abstract class GraphicEditPlugin<
|
||||
DO extends DisplayObject = DisplayObject
|
||||
@ -76,16 +76,16 @@ export abstract class LineEditPlugin extends GraphicEditPlugin<ILineGraphic> {
|
||||
constructor(g: ILineGraphic) {
|
||||
super(g);
|
||||
this.linePoints = g.linePoints;
|
||||
this.graphic.on('pointupdate', this.reset, this);
|
||||
this.graphic.on('dataupdate', this.reset, this);
|
||||
}
|
||||
|
||||
destroy(options?: boolean | IDestroyOptions | undefined): void {
|
||||
this.graphic.off('pointupdate', this.reset, this);
|
||||
this.graphic.off('dataupdate', this.reset, this);
|
||||
super.destroy(options);
|
||||
}
|
||||
|
||||
reset(_obj: DisplayObject, points: IPointData[]): void {
|
||||
this.linePoints = points;
|
||||
reset(): void {
|
||||
this.linePoints = this.graphic.linePoints;
|
||||
this.removeChildren();
|
||||
this.editedPoints.splice(0, this.editedPoints.length);
|
||||
this.initEditPoints();
|
||||
@ -125,7 +125,7 @@ export class PolylineEditPlugin extends LineEditPlugin {
|
||||
for (let i = 0; i < cps.length; i++) {
|
||||
const p = cps[i];
|
||||
const dp = new DraggablePoint(p);
|
||||
dp.on('dragmove', () => {
|
||||
dp.on('transforming', () => {
|
||||
const tlp = this.graphic.canvasToLocalPoint(dp.position);
|
||||
const cp = this.linePoints[i];
|
||||
cp.x = tlp.x;
|
||||
@ -181,13 +181,13 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
|
||||
this.initEditPoints();
|
||||
}
|
||||
|
||||
reset(_obj: DisplayObject, points: IPointData[]): void {
|
||||
reset(): void {
|
||||
this.auxiliaryLines.splice(0, this.auxiliaryLines.length);
|
||||
super.reset(_obj, points);
|
||||
super.reset();
|
||||
}
|
||||
|
||||
initEditPoints() {
|
||||
console.log('initEditPoints');
|
||||
// console.log('initEditPoints');
|
||||
const cps = this.graphic.localToCanvasPoints(...this.linePoints);
|
||||
for (let i = 0; i < cps.length; i++) {
|
||||
const p = cps[i];
|
||||
@ -240,19 +240,20 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
|
||||
EditPointContextMenu.open(e.global);
|
||||
}
|
||||
});
|
||||
dp.on('dragmove', (de: GraphicDragEvent) => {
|
||||
dp.on('transforming', (e: GraphicTransformEvent) => {
|
||||
const tlp = this.graphic.canvasToLocalPoint(dp.position);
|
||||
const cp = this.linePoints[i];
|
||||
cp.x = tlp.x;
|
||||
cp.y = tlp.y;
|
||||
if (this.options.smooth || this.options.symmetry) {
|
||||
if (c === 0 && !startOrEnd) {
|
||||
const shiftData = e.getData<ShiftData>();
|
||||
const fp = this.linePoints[i - 1];
|
||||
const np = this.linePoints[i + 1];
|
||||
fp.x = fp.x + de.dx;
|
||||
fp.y = fp.y + de.dy;
|
||||
np.x = np.x + de.dx;
|
||||
np.y = np.y + de.dy;
|
||||
fp.x = fp.x + shiftData.dx;
|
||||
fp.y = fp.y + shiftData.dy;
|
||||
np.x = np.x + shiftData.dx;
|
||||
np.y = np.y + shiftData.dy;
|
||||
} else if (c === 1 && i !== 1) {
|
||||
const bp = this.linePoints[i - 1];
|
||||
const fp2 = this.linePoints[i - 2];
|
||||
|
@ -6,20 +6,364 @@ import {
|
||||
Point,
|
||||
Polygon,
|
||||
} from 'pixi.js';
|
||||
import { GraphicDragEvent, KeyListener } from '../plugins';
|
||||
import {
|
||||
AppDragEvent,
|
||||
BaseInteractionPlugin,
|
||||
InteractionPluginType,
|
||||
KeyListener,
|
||||
} from '.';
|
||||
import { GraphicApp } from '../app';
|
||||
import { JlGraphic } from '../core';
|
||||
import { AbsorbablePosition } from '../graphic';
|
||||
import { DraggablePoint } from '../graphic/DraggablePoint';
|
||||
import {
|
||||
angleToAxisx,
|
||||
calculateLineMidpoint,
|
||||
convertRectangleToPolygonPoints,
|
||||
distance,
|
||||
calculateLineMidpoint,
|
||||
recursiveChildren,
|
||||
} from '../utils';
|
||||
import { DraggablePoint } from './DraggablePoint';
|
||||
|
||||
const BoundsLineStyle = {
|
||||
width: 1,
|
||||
color: 0x29b6f2,
|
||||
alpha: 1,
|
||||
};
|
||||
export class ShiftData {
|
||||
/**
|
||||
* 起始位置
|
||||
*/
|
||||
startPosition: Point;
|
||||
/**
|
||||
* 上一次终点位置
|
||||
*/
|
||||
lastPosition?: Point;
|
||||
/**
|
||||
* 当前位置
|
||||
*/
|
||||
currentPosition?: Point;
|
||||
constructor(
|
||||
startPosition: Point,
|
||||
currentPosition?: Point,
|
||||
lastPosition?: Point
|
||||
) {
|
||||
this.startPosition = startPosition;
|
||||
this.lastPosition = lastPosition;
|
||||
this.currentPosition = currentPosition;
|
||||
}
|
||||
static new(
|
||||
startPosition: Point,
|
||||
currentPosition?: Point,
|
||||
lastPosition?: Point
|
||||
) {
|
||||
return new ShiftData(startPosition, currentPosition, lastPosition);
|
||||
}
|
||||
|
||||
public get dx(): number {
|
||||
if (!this.lastPosition || !this.currentPosition) {
|
||||
throw new Error('错误的位移数据或阶段');
|
||||
}
|
||||
return this.currentPosition.x - this.lastPosition.x;
|
||||
}
|
||||
|
||||
public get dy(): number {
|
||||
if (!this.lastPosition || !this.currentPosition) {
|
||||
throw new Error('错误的位移数据或阶段');
|
||||
}
|
||||
return this.currentPosition.y - this.lastPosition.y;
|
||||
}
|
||||
|
||||
public get dsx(): number {
|
||||
if (!this.currentPosition) {
|
||||
throw new Error('错误的位移数据或阶段');
|
||||
}
|
||||
return this.currentPosition.x - this.startPosition.x;
|
||||
}
|
||||
|
||||
public get dsy(): number {
|
||||
if (!this.currentPosition) {
|
||||
throw new Error('错误的位移数据或阶段');
|
||||
}
|
||||
return this.currentPosition.y - this.startPosition.y;
|
||||
}
|
||||
}
|
||||
|
||||
export class ScaleData {
|
||||
start: Point;
|
||||
current?: Point;
|
||||
last?: Point;
|
||||
constructor(start: Point, current?: Point, last?: Point) {
|
||||
this.start = start;
|
||||
this.current = current;
|
||||
this.last = last;
|
||||
}
|
||||
static new(start: Point, current?: Point, last?: Point) {
|
||||
return new ScaleData(start, current, last);
|
||||
}
|
||||
}
|
||||
|
||||
export type TransformData = ShiftData | null;
|
||||
|
||||
/**
|
||||
* 图形平移事件
|
||||
*/
|
||||
export class GraphicTransformEvent {
|
||||
/**
|
||||
* 图形对象
|
||||
*/
|
||||
target: DisplayObject;
|
||||
type: 'shift' | 'rotate' | 'scale' | 'skew';
|
||||
data: TransformData;
|
||||
|
||||
constructor(
|
||||
target: DisplayObject,
|
||||
type: 'shift' | 'rotate' | 'scale' | 'skew',
|
||||
data: TransformData
|
||||
) {
|
||||
this.target = target;
|
||||
this.type = type;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
getData<D extends TransformData>(): D {
|
||||
return this.data as D;
|
||||
}
|
||||
|
||||
static shift(target: DisplayObject, data: ShiftData) {
|
||||
return new GraphicTransformEvent(target, 'shift', data);
|
||||
}
|
||||
|
||||
static scale(target: DisplayObject) {
|
||||
return new GraphicTransformEvent(target, 'scale', null);
|
||||
}
|
||||
|
||||
static rotate(target: DisplayObject) {
|
||||
return new GraphicTransformEvent(target, 'rotate', null);
|
||||
}
|
||||
|
||||
static skew(target: DisplayObject) {
|
||||
return new GraphicTransformEvent(target, 'skew', null);
|
||||
}
|
||||
|
||||
isShift(): boolean {
|
||||
return this.type === 'shift';
|
||||
}
|
||||
isRotate(): boolean {
|
||||
return this.type === 'rotate';
|
||||
}
|
||||
isScale(): boolean {
|
||||
return this.type === 'scale';
|
||||
}
|
||||
isSkew(): boolean {
|
||||
return this.type === 'skew';
|
||||
}
|
||||
}
|
||||
|
||||
export class GraphicTransformPlugin extends BaseInteractionPlugin {
|
||||
static Name = '__graphic_transform_plugin';
|
||||
|
||||
/**
|
||||
* 可吸附位置列表
|
||||
*/
|
||||
absorbablePositions?: AbsorbablePosition[];
|
||||
apContainer: Container;
|
||||
static AbsorbablePosisiontsName = '__AbsorbablePosisionts';
|
||||
|
||||
constructor(app: GraphicApp) {
|
||||
super(app, GraphicTransformPlugin.Name, InteractionPluginType.Other);
|
||||
this.apContainer = new Container();
|
||||
this.apContainer.name = GraphicTransformPlugin.AbsorbablePosisiontsName;
|
||||
this.app.canvas.addAssistantAppend(this.apContainer);
|
||||
app.on('options-update', (options) => {
|
||||
if (options.absorbablePositions) {
|
||||
this.absorbablePositions = options.absorbablePositions;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static new(app: GraphicApp) {
|
||||
return new GraphicTransformPlugin(app);
|
||||
}
|
||||
|
||||
bind(): void {
|
||||
this.app.on('drag_op_start', this.onDragStart, this);
|
||||
this.app.on('drag_op_move', this.onDragMove, this);
|
||||
this.app.on('drag_op_end', this.onDragEnd, this);
|
||||
this.app.on('graphicselectedchange', this.onGraphicSelectedChange, this);
|
||||
this.app.on(
|
||||
'graphicchildselectedchange',
|
||||
this.onGraphicChildSelectedChange,
|
||||
this
|
||||
);
|
||||
}
|
||||
unbind(): void {
|
||||
this.app.off('drag_op_start', this.onDragStart, this);
|
||||
this.app.off('drag_op_move', this.onDragMove, this);
|
||||
this.app.off('drag_op_end', this.onDragEnd, this);
|
||||
this.app.off('graphicselectedchange', this.onGraphicSelectedChange, this);
|
||||
this.app.off(
|
||||
'graphicchildselectedchange',
|
||||
this.onGraphicChildSelectedChange,
|
||||
this
|
||||
);
|
||||
}
|
||||
|
||||
getDraggedTargets(e: AppDragEvent): DisplayObject[] {
|
||||
const targets: DisplayObject[] = [];
|
||||
if (e.target.isGraphicChild() && e.target.selected && e.target.draggable) {
|
||||
const graphic = e.target.getGraphic() as JlGraphic;
|
||||
// 图形子元素
|
||||
recursiveChildren(graphic, (child) => {
|
||||
if (child.selected && child.draggable) {
|
||||
targets.push(child);
|
||||
}
|
||||
});
|
||||
} else if (
|
||||
(e.target.isGraphic() || e.target.isGraphicChild()) &&
|
||||
e.target.getGraphic()?.draggable
|
||||
) {
|
||||
// 图形对象
|
||||
targets.push(...this.app.selectedGraphics);
|
||||
} else if (e.target.draggable) {
|
||||
targets.push(e.target);
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
onDragStart(e: AppDragEvent) {
|
||||
if (!e.target.isCanvas()) {
|
||||
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
||||
if (targets.length > 0) {
|
||||
targets.forEach((target) => {
|
||||
target.shiftStartPoint = target.position.clone();
|
||||
target.emit(
|
||||
'transformstart',
|
||||
GraphicTransformEvent.shift(
|
||||
target,
|
||||
ShiftData.new(target.shiftStartPoint)
|
||||
)
|
||||
);
|
||||
});
|
||||
// 显示吸附图形
|
||||
if (this.absorbablePositions && this.absorbablePositions.length > 0) {
|
||||
this.apContainer.removeChildren();
|
||||
this.apContainer.addChild(...this.absorbablePositions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDragMove(e: AppDragEvent) {
|
||||
if (!e.target.isCanvas()) {
|
||||
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
||||
if (targets.length > 0) {
|
||||
// 处理位移
|
||||
targets.forEach((target) => {
|
||||
if (target.shiftStartPoint) {
|
||||
target.shiftLastPoint = target.position.clone();
|
||||
target.position.set(
|
||||
target.shiftStartPoint.x + e.dsx,
|
||||
target.shiftStartPoint.y + e.dsy
|
||||
);
|
||||
}
|
||||
});
|
||||
// 处理吸附
|
||||
if (this.absorbablePositions) {
|
||||
for (let i = 0; i < this.absorbablePositions.length; i++) {
|
||||
const ap = this.absorbablePositions[i];
|
||||
if (ap.tryAbsorb(...targets)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 事件发布
|
||||
targets.forEach((target) => {
|
||||
if (target.shiftStartPoint && target.shiftLastPoint) {
|
||||
target.emit(
|
||||
'transforming',
|
||||
GraphicTransformEvent.shift(
|
||||
target,
|
||||
ShiftData.new(
|
||||
target.shiftStartPoint,
|
||||
target.position.clone(),
|
||||
target.shiftLastPoint
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDragEnd(e: AppDragEvent) {
|
||||
if (!e.target.isCanvas()) {
|
||||
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
||||
targets.forEach((target) => {
|
||||
if (target.shiftStartPoint) {
|
||||
target.emit(
|
||||
'transformend',
|
||||
GraphicTransformEvent.shift(
|
||||
target,
|
||||
ShiftData.new(target.shiftStartPoint, target.position.clone())
|
||||
)
|
||||
);
|
||||
}
|
||||
target.shiftStartPoint = null;
|
||||
});
|
||||
}
|
||||
this.clearCache();
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存
|
||||
*/
|
||||
clearCache() {
|
||||
// 移除吸附图形
|
||||
this.absorbablePositions = [];
|
||||
this.apContainer.removeChildren();
|
||||
}
|
||||
|
||||
onGraphicSelectedChange(g: DisplayObject, selected: boolean) {
|
||||
let br = g.getAssistantAppend<BoundsGraphic>(BoundsGraphic.Name);
|
||||
if (!br) {
|
||||
// 绘制辅助包围框
|
||||
br = new BoundsGraphic(g);
|
||||
}
|
||||
|
||||
if (selected) {
|
||||
if (br) {
|
||||
br.redraw();
|
||||
br.visible = true;
|
||||
}
|
||||
} else {
|
||||
if (br) {
|
||||
br.visible = false;
|
||||
}
|
||||
}
|
||||
if (g.scalable) {
|
||||
// 缩放点
|
||||
let sp = g.getAssistantAppend<TransformPoints>(TransformPoints.Name);
|
||||
if (!sp) {
|
||||
sp = new TransformPoints(g);
|
||||
}
|
||||
if (selected) {
|
||||
sp.update();
|
||||
sp.visible = true;
|
||||
} else {
|
||||
sp.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onGraphicChildSelectedChange(child: DisplayObject, selected: boolean) {
|
||||
let br = child.getAssistantAppend<BoundsGraphic>(BoundsGraphic.Name);
|
||||
if (!br) {
|
||||
// 绘制辅助包围框
|
||||
br = new BoundsGraphic(child);
|
||||
}
|
||||
if (selected) {
|
||||
br.redraw();
|
||||
br.visible = true;
|
||||
} else {
|
||||
br.visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放、旋转辅助
|
||||
@ -118,17 +462,17 @@ export class TransformPoints extends Container {
|
||||
this.obj.on('transformend', this.onObjTransformEnd, this);
|
||||
this.obj.on('repaint', this.onGraphicRepaint, this);
|
||||
this.children.forEach((dp) => {
|
||||
dp.on('dragstart', this.onScaleDragStart, this);
|
||||
dp.on('dragmove', this.onScaleDragMove, this);
|
||||
dp.on('dragend', this.onScaleDragEnd, this);
|
||||
dp.on('transformstart', this.onScaleDragStart, this);
|
||||
dp.on('transforming', this.onScaleDragMove, this);
|
||||
dp.on('transformend', this.onScaleDragEnd, this);
|
||||
});
|
||||
|
||||
// 创建旋转拖拽点
|
||||
this.rotatePoint = new DraggablePoint(new Point());
|
||||
this.addChild(this.rotatePoint);
|
||||
this.rotatePoint.on('dragstart', this.onRotateStart, this);
|
||||
this.rotatePoint.on('dragmove', this.onRotateMove, this);
|
||||
this.rotatePoint.on('dragend', this.onRotateEnd, this);
|
||||
this.rotatePoint.on('transformstart', this.onRotateStart, this);
|
||||
this.rotatePoint.on('transforming', this.onRotateMove, this);
|
||||
this.rotatePoint.on('transformend', this.onRotateEnd, this);
|
||||
this.rotatePivot = new Point();
|
||||
this.rotateLastPoint = new Point();
|
||||
// 初始化旋转角度修改键盘监听器
|
||||
@ -164,23 +508,24 @@ export class TransformPoints extends Container {
|
||||
* 旋转开始
|
||||
* @param de
|
||||
*/
|
||||
onRotateStart(de: GraphicDragEvent) {
|
||||
onRotateStart(de: GraphicTransformEvent) {
|
||||
this.hideAll();
|
||||
const assistantPoint = this.obj.localToCanvasPoint(this.obj.pivot);
|
||||
this.rotatePivot.copyFrom(assistantPoint);
|
||||
this.rotateLastPoint.copyFrom(de.target.position);
|
||||
this.startAngle = this.obj.angle;
|
||||
const app = this.obj.getGraphicApp();
|
||||
this.rotateAngleStepKeyListeners.forEach((listener) =>
|
||||
this.obj.getGraphicApp().addKeyboardListener(listener)
|
||||
app.addKeyboardListener(listener)
|
||||
);
|
||||
this.obj.emit('rotatestart', this.obj);
|
||||
this.obj.emit('transformstart', this.obj);
|
||||
this.obj.emit('transformstart', GraphicTransformEvent.rotate(this.obj));
|
||||
// app.emit('transformstart', app.selectedGraphics);
|
||||
}
|
||||
/**
|
||||
* 旋转移动
|
||||
* @param de
|
||||
*/
|
||||
onRotateMove(de: GraphicDragEvent) {
|
||||
onRotateMove(de: GraphicTransformEvent) {
|
||||
// 旋转角度计算逻辑:取锚点y负方向一点作为旋转点,求旋转点和锚点所形成的直线与x轴角度,此角度+90°即为最终旋转角度,再将旋转角度限制到(-180,180]之间
|
||||
let angle = angleToAxisx(this.rotatePivot, de.target.position);
|
||||
angle = Math.floor(angle / this.angleStep) * this.angleStep;
|
||||
@ -189,7 +534,7 @@ export class TransformPoints extends Container {
|
||||
angle = angle - 360;
|
||||
}
|
||||
this.obj.angle = angle;
|
||||
this.obj.emit('rotatemove', this.obj);
|
||||
// this.obj.emit('rotatemove', this.obj);
|
||||
}
|
||||
/**
|
||||
* 旋转结束
|
||||
@ -200,8 +545,7 @@ export class TransformPoints extends Container {
|
||||
this.rotateAngleStepKeyListeners.forEach((listener) =>
|
||||
this.obj.getGraphicApp().removeKeyboardListener(listener)
|
||||
);
|
||||
this.obj.emit('rotateend', this.obj);
|
||||
this.obj.emit('transformend', this.obj);
|
||||
this.obj.emit('transformend', GraphicTransformEvent.rotate(this.obj));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -227,11 +571,10 @@ export class TransformPoints extends Container {
|
||||
this.lbLocal.copyFrom(p3);
|
||||
this.lLocal.copyFrom(calculateLineMidpoint(p0, p3));
|
||||
this.originScale = this.obj.scale.clone();
|
||||
this.obj.emit('scalestart', this.obj);
|
||||
this.obj.emit('transformstart', this.obj);
|
||||
this.obj.emit('transformstart', GraphicTransformEvent.scale(this.obj));
|
||||
}
|
||||
|
||||
onScaleDragMove(de: GraphicDragEvent) {
|
||||
onScaleDragMove(e: GraphicTransformEvent) {
|
||||
// 缩放计算逻辑:共8个方向缩放点,根据所拖拽的方向:
|
||||
// 1,计算缩放为1时的此点在拖拽开始时的位置到锚点x、y距离,
|
||||
// 2,计算拖拽点的当前位置到锚点的x、y方向距离,
|
||||
@ -243,38 +586,40 @@ export class TransformPoints extends Container {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
this.obj.scale.copyFrom(defaultScale);
|
||||
const point = this.obj.toLocal(de.event.global);
|
||||
if (de.target === this.ltScalePoint) {
|
||||
const point = this.obj.toLocal(
|
||||
e.target.parent.localToScreenPoint(e.target.position)
|
||||
);
|
||||
if (e.target === this.ltScalePoint) {
|
||||
// 左上角
|
||||
originWidth = Math.abs(this.ltLocal.x - this.scalePivot.x);
|
||||
originHeight = Math.abs(this.ltLocal.y - this.scalePivot.y);
|
||||
width = Math.abs(point.x - this.scalePivot.x);
|
||||
height = Math.abs(point.y - this.scalePivot.y);
|
||||
} else if (de.target == this.tScalePoint) {
|
||||
} else if (e.target == this.tScalePoint) {
|
||||
// 上
|
||||
originHeight = Math.abs(this.tLocal.y - this.scalePivot.y);
|
||||
height = Math.abs(point.y - this.scalePivot.y);
|
||||
} else if (de.target == this.rtScalePoint) {
|
||||
} else if (e.target == this.rtScalePoint) {
|
||||
// 右上
|
||||
originWidth = Math.abs(this.rtLocal.x - this.scalePivot.x);
|
||||
originHeight = Math.abs(this.rtLocal.y - this.scalePivot.y);
|
||||
width = Math.abs(point.x - this.scalePivot.x);
|
||||
height = Math.abs(point.y - this.scalePivot.y);
|
||||
} else if (de.target == this.rScalePoint) {
|
||||
} else if (e.target == this.rScalePoint) {
|
||||
// 右
|
||||
originWidth = Math.abs(this.rLocal.x - this.scalePivot.x);
|
||||
width = Math.abs(point.x - this.scalePivot.x);
|
||||
} else if (de.target == this.rbScalePoint) {
|
||||
} else if (e.target == this.rbScalePoint) {
|
||||
// 右下
|
||||
originWidth = Math.abs(this.rbLocal.x - this.scalePivot.x);
|
||||
originHeight = Math.abs(this.rbLocal.y - this.scalePivot.y);
|
||||
width = Math.abs(point.x - this.scalePivot.x);
|
||||
height = Math.abs(point.y - this.scalePivot.y);
|
||||
} else if (de.target == this.bScalePoint) {
|
||||
} else if (e.target == this.bScalePoint) {
|
||||
// 下
|
||||
originHeight = Math.abs(this.bLocal.y - this.scalePivot.y);
|
||||
height = Math.abs(point.y - this.scalePivot.y);
|
||||
} else if (de.target == this.lbScalePoint) {
|
||||
} else if (e.target == this.lbScalePoint) {
|
||||
// 左下
|
||||
originWidth = Math.abs(this.lbLocal.x - this.scalePivot.x);
|
||||
originHeight = Math.abs(this.lbLocal.y - this.scalePivot.y);
|
||||
@ -305,9 +650,7 @@ export class TransformPoints extends Container {
|
||||
|
||||
onScaleDragEnd() {
|
||||
this.showAll();
|
||||
this.obj.emit('scaleend', this.obj);
|
||||
this.obj.emit('transformend', this.obj);
|
||||
this.obj.getGraphicApp().emit('scaleend', this.obj);
|
||||
this.obj.emit('transformend', GraphicTransformEvent.scale(this.obj));
|
||||
}
|
||||
|
||||
hideOthers(dg: DisplayObject) {
|
||||
@ -433,6 +776,11 @@ export class TransformPoints extends Container {
|
||||
*/
|
||||
export class BoundsGraphic extends Graphics {
|
||||
static Name = '_BoundsRect';
|
||||
static BoundsLineStyle = {
|
||||
width: 1,
|
||||
color: 0x29b6f2,
|
||||
alpha: 1,
|
||||
};
|
||||
obj: DisplayObject;
|
||||
constructor(graphic: DisplayObject) {
|
||||
super();
|
||||
@ -470,6 +818,6 @@ export class BoundsGraphic extends Graphics {
|
||||
redraw() {
|
||||
this.visible = false; // 屏蔽包围框本身
|
||||
const bounds = new Polygon(this.obj.localBoundsToCanvasPoints());
|
||||
this.clear().lineStyle(BoundsLineStyle).drawShape(bounds);
|
||||
this.clear().lineStyle(BoundsGraphic.BoundsLineStyle).drawShape(bounds);
|
||||
}
|
||||
}
|
@ -1,5 +1,10 @@
|
||||
import { FederatedMouseEvent } from 'pixi.js';
|
||||
import { GraphicApp } from '../app/JlGraphicApp';
|
||||
import {
|
||||
DisplayObject,
|
||||
FederatedMouseEvent,
|
||||
FederatedPointerEvent,
|
||||
Point,
|
||||
} from 'pixi.js';
|
||||
import { GraphicApp, IGraphicAppConfig } from '../app/JlGraphicApp';
|
||||
import { JlGraphic } from '../core/JlGraphic';
|
||||
|
||||
export enum InteractionPluginType {
|
||||
@ -16,14 +21,14 @@ export interface InteractionPlugin {
|
||||
name: string; // 唯一标识
|
||||
app: GraphicApp;
|
||||
|
||||
/**
|
||||
* 停止
|
||||
*/
|
||||
pause(): void;
|
||||
/**
|
||||
* 恢复
|
||||
*/
|
||||
resume(): void;
|
||||
/**
|
||||
* 停止
|
||||
*/
|
||||
pause(): void;
|
||||
/**
|
||||
* 是否生效
|
||||
*/
|
||||
@ -34,44 +39,243 @@ export interface InteractionPlugin {
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export class ViewportMovePlugin implements InteractionPlugin {
|
||||
static Name = '__viewport-move';
|
||||
readonly _type = InteractionPluginType.Other;
|
||||
name = ViewportMovePlugin.Name; // 唯一标识
|
||||
export abstract class BaseInteractionPlugin implements InteractionPlugin {
|
||||
readonly _type: string;
|
||||
name: string; // 唯一标识
|
||||
app: GraphicApp;
|
||||
_pause: boolean;
|
||||
|
||||
constructor(app: GraphicApp, name: string, type: string) {
|
||||
this._type = type;
|
||||
this.app = app;
|
||||
this.name = name;
|
||||
this._pause = true;
|
||||
app.registerInteractionPlugin(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复
|
||||
*/
|
||||
resume(): void {
|
||||
this.bind();
|
||||
this._pause = false;
|
||||
}
|
||||
/**
|
||||
* 停止
|
||||
*/
|
||||
pause(): void {
|
||||
this.unbind();
|
||||
this._pause = true;
|
||||
}
|
||||
|
||||
abstract bind(): void;
|
||||
abstract unbind(): void;
|
||||
/**
|
||||
* 是否生效
|
||||
*/
|
||||
isActive(): boolean {
|
||||
return !this._pause;
|
||||
}
|
||||
/**
|
||||
* 关闭绑定的交互事件,清理插件相关数据(如需要),从app中移除
|
||||
*/
|
||||
destroy(): void {
|
||||
this.pause();
|
||||
this.app.removeInteractionPlugin(this);
|
||||
}
|
||||
}
|
||||
|
||||
export class AppDragEvent {
|
||||
app: GraphicApp;
|
||||
type: 'start' | 'move' | 'end';
|
||||
target: DisplayObject;
|
||||
original: FederatedPointerEvent;
|
||||
start: Point;
|
||||
constructor(
|
||||
app: GraphicApp,
|
||||
type: 'start' | 'move' | 'end',
|
||||
target: DisplayObject,
|
||||
original: FederatedPointerEvent,
|
||||
start: Point
|
||||
) {
|
||||
this.app = app;
|
||||
this.type = type;
|
||||
this.target = target;
|
||||
this.original = original;
|
||||
this.start = start;
|
||||
}
|
||||
|
||||
public get isMouse(): boolean {
|
||||
return this.original.pointerType === 'mouse';
|
||||
}
|
||||
|
||||
public get isLeftButton(): boolean {
|
||||
return (
|
||||
this.isMouse &&
|
||||
((this.original.button === -1 && this.original.buttons === 1) ||
|
||||
(this.original.button === 0 && this.original.buttons === 0))
|
||||
);
|
||||
}
|
||||
|
||||
public get isRightButton(): boolean {
|
||||
return (
|
||||
this.isMouse &&
|
||||
((this.original.button === -1 && this.original.buttons === 2) ||
|
||||
(this.original.button === 2 && this.original.buttons === 0))
|
||||
);
|
||||
}
|
||||
|
||||
public get isMiddleButton(): boolean {
|
||||
return (
|
||||
this.isMouse &&
|
||||
((this.original.button === -1 && this.original.buttons === 4) ||
|
||||
(this.original.button === 1 && this.original.buttons === 0))
|
||||
);
|
||||
}
|
||||
|
||||
public get isTouch(): boolean {
|
||||
return this.original.pointerType === 'touch';
|
||||
}
|
||||
|
||||
public get end(): Point {
|
||||
return this.app.toCanvasCoordinates(this.original.global);
|
||||
}
|
||||
|
||||
public get dx(): number {
|
||||
const move = this.original.movement;
|
||||
return move.x / this.app.viewport.scaled;
|
||||
}
|
||||
|
||||
public get dy(): number {
|
||||
const move = this.original.movement;
|
||||
return move.y / this.app.viewport.scaled;
|
||||
}
|
||||
|
||||
public get dsx(): number {
|
||||
return this.end.x - this.start.x;
|
||||
}
|
||||
|
||||
public get dsy(): number {
|
||||
return this.end.y - this.start.y;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽操作插件
|
||||
*/
|
||||
export class DragPlugin extends BaseInteractionPlugin {
|
||||
static Name = '__drag_operation_plugin';
|
||||
threshold = 3;
|
||||
target: DisplayObject | null = null;
|
||||
start: Point | null = null;
|
||||
drag = false;
|
||||
constructor(app: GraphicApp) {
|
||||
super(app, DragPlugin.Name, InteractionPluginType.Other);
|
||||
app.on('options-update', (options: IGraphicAppConfig) => {
|
||||
if (options.threshold !== undefined) {
|
||||
this.threshold = options.threshold;
|
||||
}
|
||||
});
|
||||
}
|
||||
static new(app: GraphicApp) {
|
||||
return new DragPlugin(app);
|
||||
}
|
||||
bind(): void {
|
||||
const canvas = this.app.canvas;
|
||||
canvas.on('pointerdown', this.onPointerDown, this);
|
||||
}
|
||||
unbind(): void {
|
||||
const canvas = this.app.canvas;
|
||||
canvas.off('pointerdown', this.onPointerDown, this);
|
||||
canvas.off('pointerup', this.onPointerUp, this);
|
||||
canvas.off('pointerupoutside', this.onPointerUp, this);
|
||||
}
|
||||
onPointerDown(e: FederatedPointerEvent) {
|
||||
this.target = e.target as DisplayObject;
|
||||
this.start = this.app.toCanvasCoordinates(e.global);
|
||||
const canvas = this.app.canvas;
|
||||
canvas.on('pointermove', this.onPointerMove, this);
|
||||
canvas.on('pointerup', this.onPointerUp, this);
|
||||
canvas.on('pointerupoutside', this.onPointerUp, this);
|
||||
}
|
||||
onPointerMove(e: FederatedPointerEvent) {
|
||||
if (this.start) {
|
||||
const current = this.app.toCanvasCoordinates(e.global);
|
||||
const dragStart =
|
||||
Math.abs(current.x - this.start.x) > this.threshold ||
|
||||
Math.abs(current.y - this.start.y) > this.threshold;
|
||||
if (this.target && this.start && !this.drag && dragStart) {
|
||||
this.app.emit(
|
||||
'drag_op_start',
|
||||
new AppDragEvent(this.app, 'start', this.target, e, this.start)
|
||||
);
|
||||
this.drag = true;
|
||||
}
|
||||
|
||||
// drag移动处理
|
||||
if (this.target && this.drag && this.start) {
|
||||
// console.log('drag move', e.movement);
|
||||
this.app.emit(
|
||||
'drag_op_move',
|
||||
new AppDragEvent(this.app, 'move', this.target, e, this.start)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPointerUp(e: FederatedPointerEvent) {
|
||||
if (this.target && this.drag && this.start) {
|
||||
// console.log('drag end');
|
||||
this.app.emit(
|
||||
'drag_op_end',
|
||||
new AppDragEvent(this.app, 'end', this.target, e, this.start)
|
||||
);
|
||||
}
|
||||
const canvas = this.app.canvas;
|
||||
canvas.off('mousemove', this.onPointerMove, this);
|
||||
canvas.off('mouseup', this.onPointerUp, this);
|
||||
canvas.off('mouseupoutside', this.onPointerUp, this);
|
||||
this.clearCache();
|
||||
}
|
||||
/**
|
||||
* 清理缓存
|
||||
*/
|
||||
clearCache() {
|
||||
this.drag = false;
|
||||
this.start = null;
|
||||
this.target = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 视口移动插件
|
||||
*/
|
||||
export class ViewportMovePlugin extends BaseInteractionPlugin {
|
||||
static Name = '__viewport_move_plugin';
|
||||
|
||||
moveHandler: NodeJS.Timeout | null = null;
|
||||
moveSpeedx = 0;
|
||||
moveSpeedy = 0;
|
||||
|
||||
_pause: boolean;
|
||||
|
||||
constructor(app: GraphicApp) {
|
||||
this.app = app;
|
||||
this._pause = true;
|
||||
app.registerInteractionPlugin(this);
|
||||
super(app, ViewportMovePlugin.Name, InteractionPluginType.Other);
|
||||
}
|
||||
|
||||
static new(app: GraphicApp): ViewportMovePlugin {
|
||||
return new ViewportMovePlugin(app);
|
||||
}
|
||||
|
||||
resume(): void {
|
||||
pause(): void {
|
||||
super.pause();
|
||||
this.stopMove();
|
||||
}
|
||||
bind(): void {
|
||||
this.app.canvas.on('pointermove', this.viewportMove, this);
|
||||
}
|
||||
|
||||
pause(): void {
|
||||
unbind(): void {
|
||||
this.app.canvas.off('pointermove', this.viewportMove, this);
|
||||
}
|
||||
|
||||
isActive(): boolean {
|
||||
return !this._pause;
|
||||
}
|
||||
destroy(): void {
|
||||
this.pause();
|
||||
this.app.removeInteractionPlugin(this);
|
||||
}
|
||||
|
||||
startMove(moveSpeedx: number, moveSpeedy: number) {
|
||||
this.moveSpeedx = moveSpeedx;
|
||||
this.moveSpeedy = moveSpeedy;
|
||||
@ -90,6 +294,7 @@ export class ViewportMovePlugin implements InteractionPlugin {
|
||||
if (this.moveHandler != null) {
|
||||
clearInterval(this.moveHandler);
|
||||
this.moveHandler = null;
|
||||
this.app.canvas.cursor = 'auto';
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,24 +332,11 @@ export class ViewportMovePlugin implements InteractionPlugin {
|
||||
/**
|
||||
* 应用交互插件,同时只能生效一个
|
||||
*/
|
||||
export abstract class AppInteractionPlugin implements InteractionPlugin {
|
||||
export abstract class AppInteractionPlugin extends BaseInteractionPlugin {
|
||||
readonly _type = InteractionPluginType.App;
|
||||
name: string; // 唯一标识
|
||||
app: GraphicApp;
|
||||
_pause: boolean;
|
||||
constructor(name: string, app: GraphicApp) {
|
||||
this.name = name;
|
||||
this.app = app;
|
||||
this._pause = true;
|
||||
app.registerInteractionPlugin(this);
|
||||
}
|
||||
isActive(): boolean {
|
||||
return !this._pause;
|
||||
}
|
||||
|
||||
pause(): void {
|
||||
this.unbind();
|
||||
this._pause = true;
|
||||
constructor(name: string, app: GraphicApp) {
|
||||
super(app, name, InteractionPluginType.App);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -155,14 +347,6 @@ export abstract class AppInteractionPlugin implements InteractionPlugin {
|
||||
this.bind();
|
||||
this._pause = false;
|
||||
}
|
||||
|
||||
abstract bind(): void;
|
||||
abstract unbind(): void;
|
||||
|
||||
destroy(): void {
|
||||
this.pause();
|
||||
this.app.removeInteractionPlugin(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -198,17 +382,16 @@ export abstract class GraphicInteractionPlugin<G extends JlGraphic>
|
||||
return !this._pause;
|
||||
}
|
||||
|
||||
pause(): void {
|
||||
const list = this.filter(...this.app.queryStore.getAllGraphics());
|
||||
this.unbinds(list);
|
||||
this._pause = true;
|
||||
}
|
||||
|
||||
resume(): void {
|
||||
const list = this.filter(...this.app.queryStore.getAllGraphics());
|
||||
this.binds(list);
|
||||
this._pause = false;
|
||||
}
|
||||
pause(): void {
|
||||
const list = this.filter(...this.app.queryStore.getAllGraphics());
|
||||
this.unbinds(list);
|
||||
this._pause = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤需要的图形对象
|
||||
|
@ -219,7 +219,7 @@ export interface KeyListenerOptions {
|
||||
// 按下操作处理
|
||||
onPress?: KeyboardKeyHandler;
|
||||
// 按下操作是否每次触发,默认一次
|
||||
pressTriggerEveryTime?: boolean;
|
||||
pressTriggerAsOriginalEvent?: boolean;
|
||||
// 释放/抬起操作处理
|
||||
onRelease?: KeyboardKeyHandler;
|
||||
}
|
||||
@ -233,7 +233,7 @@ export interface ICompleteKeyListenerOptions {
|
||||
global: boolean;
|
||||
// 按下操作处理
|
||||
onPress?: KeyboardKeyHandler;
|
||||
pressTriggerEveryTime: boolean;
|
||||
pressTriggerAsOriginalEvent: boolean;
|
||||
// 释放/抬起操作处理
|
||||
onRelease?: KeyboardKeyHandler;
|
||||
}
|
||||
@ -243,7 +243,7 @@ const DefaultKeyListenerOptions: ICompleteKeyListenerOptions = {
|
||||
combinations: [],
|
||||
global: false,
|
||||
onPress: undefined,
|
||||
pressTriggerEveryTime: false,
|
||||
pressTriggerAsOriginalEvent: false,
|
||||
onRelease: undefined,
|
||||
};
|
||||
|
||||
@ -293,11 +293,11 @@ export class KeyListener {
|
||||
}
|
||||
|
||||
public get pressTriggerEveryTime(): boolean {
|
||||
return this.options.pressTriggerEveryTime;
|
||||
return this.options.pressTriggerAsOriginalEvent;
|
||||
}
|
||||
|
||||
public set pressTriggerEveryTime(v: boolean) {
|
||||
this.options.pressTriggerEveryTime = v;
|
||||
this.options.pressTriggerAsOriginalEvent = v;
|
||||
}
|
||||
|
||||
press(e: KeyboardEvent, app: GraphicApp): void {
|
||||
|
@ -2,3 +2,4 @@ export * from './InteractionPlugin';
|
||||
export * from './CommonMousePlugin';
|
||||
export * from './KeyboardPlugin';
|
||||
export * from './CopyPlugin';
|
||||
export * from './GraphicTransformPlugin';
|
||||
|
Loading…
Reference in New Issue
Block a user