菜单功能完善
直线/曲线编辑右键菜单功能完善 添加_rightclick和_leftclick事件(原始事件无法区别拖拽)
This commit is contained in:
parent
df3522e205
commit
3454739356
@ -14,7 +14,7 @@ viewport 使用的 github 开源的 pixi-viewport[pixi-viewport](https://github.
|
|||||||
- 绘制增加吸附功能(移动到特定位置附近吸附)(完成)
|
- 绘制增加吸附功能(移动到特定位置附近吸附)(完成)
|
||||||
- 图形动画抽象
|
- 图形动画抽象
|
||||||
- 添加公用动画逻辑(如按指定路径位移,按角度旋转、按比例缩放、透明度控制等)
|
- 添加公用动画逻辑(如按指定路径位移,按角度旋转、按比例缩放、透明度控制等)
|
||||||
- 菜单事件及处理
|
- 菜单事件及处理,功能:菜单更新、菜单项显隐控制、菜单执行前后事件回调
|
||||||
- 打包
|
- 打包
|
||||||
- 添加拖拽轨迹限制功能
|
- 添加拖拽轨迹限制功能
|
||||||
- 添加图形对象 可编辑属性 定义功能
|
- 添加图形对象 可编辑属性 定义功能
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { fromUint8Array, toUint8Array } from 'js-base64';
|
import { fromUint8Array, toUint8Array } from 'js-base64';
|
||||||
import { IPointData, Point } from 'pixi.js';
|
import { IPointData, Point } from 'pixi.js';
|
||||||
|
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
||||||
|
import { IscsFanDraw } from 'src/graphics/iscs-fan/IscsFanDrawAssistant';
|
||||||
import { Link } from 'src/graphics/link/Link';
|
import { Link } from 'src/graphics/link/Link';
|
||||||
import { LinkDraw } from 'src/graphics/link/LinkDrawAssistant';
|
import { LinkDraw } from 'src/graphics/link/LinkDrawAssistant';
|
||||||
import {
|
import {
|
||||||
@ -10,11 +12,11 @@ import {
|
|||||||
JlDrawApp,
|
JlDrawApp,
|
||||||
KeyListener,
|
KeyListener,
|
||||||
} from 'src/jlgraphic';
|
} from 'src/jlgraphic';
|
||||||
|
import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu';
|
||||||
|
import { MenuItemOptions } from 'src/jlgraphic/ui/Menu';
|
||||||
|
import { IscsFanData } from './graphics/IscsFanInteraction';
|
||||||
import { LinkData } from './graphics/LinkInteraction';
|
import { LinkData } from './graphics/LinkInteraction';
|
||||||
import { graphicData } from './protos/draw_data_storage';
|
import { graphicData } from './protos/draw_data_storage';
|
||||||
import { IscsFanDraw } from 'src/graphics/iscs-fan/IscsFanDrawAssistant';
|
|
||||||
import { IscsFanData } from './graphics/IscsFanInteraction';
|
|
||||||
import { IscsFan } from 'src/graphics/iscs-fan/IscsFan';
|
|
||||||
|
|
||||||
export function fromStoragePoint(p: graphicData.Point): Point {
|
export function fromStoragePoint(p: graphicData.Point): Point {
|
||||||
return new Point(p.x, p.y);
|
return new Point(p.x, p.y);
|
||||||
@ -46,6 +48,28 @@ export function toStorageTransform(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const UndoOptions: MenuItemOptions = {
|
||||||
|
name: '撤销',
|
||||||
|
};
|
||||||
|
const RedoOptions: MenuItemOptions = {
|
||||||
|
name: '重做',
|
||||||
|
};
|
||||||
|
const SelectAllOptions: MenuItemOptions = {
|
||||||
|
name: '全选',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DefaultCanvasMenu = new ContextMenu({
|
||||||
|
name: '绘制-画布菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [UndoOptions, RedoOptions],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
items: [SelectAllOptions],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
export function initDrawApp(app: JlDrawApp) {
|
export function initDrawApp(app: JlDrawApp) {
|
||||||
app.setOptions({
|
app.setOptions({
|
||||||
drawAssistants: [
|
drawAssistants: [
|
||||||
@ -58,6 +82,23 @@ export function initDrawApp(app: JlDrawApp) {
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 画布右键菜单
|
||||||
|
app.registerMenu(DefaultCanvasMenu);
|
||||||
|
app.canvas.on('_rightclick', (e) => {
|
||||||
|
UndoOptions.disabled = !app.opRecord.hasUndo;
|
||||||
|
RedoOptions.disabled = !app.opRecord.hasRedo;
|
||||||
|
UndoOptions.handler = () => {
|
||||||
|
app.opRecord.undo();
|
||||||
|
};
|
||||||
|
RedoOptions.handler = () => {
|
||||||
|
app.opRecord.redo();
|
||||||
|
};
|
||||||
|
SelectAllOptions.handler = () => {
|
||||||
|
app.selectAllGraphics();
|
||||||
|
};
|
||||||
|
DefaultCanvasMenu.open(e.global);
|
||||||
|
});
|
||||||
|
|
||||||
app.addKeyboardListener(
|
app.addKeyboardListener(
|
||||||
new KeyListener({
|
new KeyListener({
|
||||||
value: 'KeyL',
|
value: 'KeyL',
|
||||||
|
@ -29,7 +29,13 @@ import {
|
|||||||
BezierCurveEditPlugin,
|
BezierCurveEditPlugin,
|
||||||
ILineGraphic,
|
ILineGraphic,
|
||||||
PolylineEditPlugin,
|
PolylineEditPlugin,
|
||||||
|
addWayPoint,
|
||||||
|
addWaypointConfig,
|
||||||
|
clearWayPoint,
|
||||||
|
clearWaypointsConfig,
|
||||||
|
getWaypointRangeIndex,
|
||||||
} from 'src/jlgraphic/plugins/GraphicEditPlugin';
|
} from 'src/jlgraphic/plugins/GraphicEditPlugin';
|
||||||
|
import { ContextMenu } from 'src/jlgraphic/ui/ContextMenu';
|
||||||
import { ILinkData, Link, LinkTemplate } from './Link';
|
import { ILinkData, Link, LinkTemplate } from './Link';
|
||||||
|
|
||||||
export interface ILinkDrawOptions {
|
export interface ILinkDrawOptions {
|
||||||
@ -263,6 +269,15 @@ function onEditPointCreate(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const LinkEditMenu: ContextMenu = ContextMenu.init({
|
||||||
|
name: '轨道编辑菜单',
|
||||||
|
groups: [
|
||||||
|
{
|
||||||
|
items: [addWaypointConfig, clearWaypointsConfig],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* link路径编辑
|
* link路径编辑
|
||||||
*/
|
*/
|
||||||
@ -270,6 +285,7 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
|
|||||||
static Name = 'LinkPointsDrag';
|
static Name = 'LinkPointsDrag';
|
||||||
constructor(app: GraphicApp) {
|
constructor(app: GraphicApp) {
|
||||||
super(LinkPointsEditPlugin.Name, app);
|
super(LinkPointsEditPlugin.Name, app);
|
||||||
|
app.registerMenu(LinkEditMenu);
|
||||||
}
|
}
|
||||||
static init(app: GraphicApp): LinkPointsEditPlugin {
|
static init(app: GraphicApp): LinkPointsEditPlugin {
|
||||||
return new LinkPointsEditPlugin(app);
|
return new LinkPointsEditPlugin(app);
|
||||||
@ -281,14 +297,37 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
|
|||||||
g.lineGraphic.eventMode = 'static';
|
g.lineGraphic.eventMode = 'static';
|
||||||
g.lineGraphic.cursor = 'pointer';
|
g.lineGraphic.cursor = 'pointer';
|
||||||
g.lineGraphic.hitArea = new LinkGraphicHitArea(g);
|
g.lineGraphic.hitArea = new LinkGraphicHitArea(g);
|
||||||
|
g.on('_rightclick', this.onContextMenu, this);
|
||||||
g.on('selected', this.onSelected, this);
|
g.on('selected', this.onSelected, this);
|
||||||
g.on('unselected', this.onUnselected, this);
|
g.on('unselected', this.onUnselected, this);
|
||||||
}
|
}
|
||||||
unbind(g: Link): void {
|
unbind(g: Link): void {
|
||||||
|
g.off('_rightclick', this.onContextMenu, this);
|
||||||
g.off('selected', this.onSelected, this);
|
g.off('selected', this.onSelected, this);
|
||||||
g.off('unselected', this.onUnselected, this);
|
g.off('unselected', this.onUnselected, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onContextMenu(e: FederatedMouseEvent) {
|
||||||
|
const target = e.target as DisplayObject;
|
||||||
|
const link = target.getGraphic() as Link;
|
||||||
|
this.app.updateSelected(link);
|
||||||
|
|
||||||
|
addWaypointConfig.handler = () => {
|
||||||
|
const linePoints = link.linePoints;
|
||||||
|
const p = link.screenToLocalPoint(e.global);
|
||||||
|
const { start, end } = getWaypointRangeIndex(
|
||||||
|
linePoints,
|
||||||
|
link.datas.curve,
|
||||||
|
p
|
||||||
|
);
|
||||||
|
addWayPoint(link, link.datas.curve, start, end, p);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(link, link.datas.curve);
|
||||||
|
};
|
||||||
|
LinkEditMenu.open(e.global);
|
||||||
|
}
|
||||||
|
|
||||||
onSelected(g: DisplayObject): void {
|
onSelected(g: DisplayObject): void {
|
||||||
const link = g as Link;
|
const link = g as Link;
|
||||||
let lep;
|
let lep;
|
||||||
|
@ -35,6 +35,8 @@ import {
|
|||||||
ICanvasProperties,
|
ICanvasProperties,
|
||||||
JlCanvas,
|
JlCanvas,
|
||||||
} from './JlGraphicApp';
|
} from './JlGraphicApp';
|
||||||
|
import { ContextMenu } from '../ui/ContextMenu';
|
||||||
|
import { MenuItemOptions, MenuOptions } from '../ui/Menu';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 图形绘制助手
|
* 图形绘制助手
|
||||||
@ -240,7 +242,7 @@ export class JlDrawApp extends GraphicApp {
|
|||||||
this.selectedData = null;
|
this.selectedData = null;
|
||||||
this.initSelectedDataUpdateListen();
|
this.initSelectedDataUpdateListen();
|
||||||
// 拖拽操作记录
|
// 拖拽操作记录
|
||||||
this.appDragRecord();
|
this.appOperationRecord();
|
||||||
// 绑定通用键盘操作
|
// 绑定通用键盘操作
|
||||||
this.bindKeyboardOperation();
|
this.bindKeyboardOperation();
|
||||||
}
|
}
|
||||||
@ -269,7 +271,7 @@ export class JlDrawApp extends GraphicApp {
|
|||||||
return sda as DA;
|
return sda as DA;
|
||||||
}
|
}
|
||||||
|
|
||||||
private appDragRecord() {
|
private appOperationRecord() {
|
||||||
let dragStartDatas: GraphicData[] = [];
|
let dragStartDatas: GraphicData[] = [];
|
||||||
this.on('drag_op_start', (e: AppDragEvent) => {
|
this.on('drag_op_start', (e: AppDragEvent) => {
|
||||||
// 图形拖拽,记录初始数据
|
// 图形拖拽,记录初始数据
|
||||||
@ -283,7 +285,7 @@ export class JlDrawApp extends GraphicApp {
|
|||||||
if (dragStartDatas.length > 0) {
|
if (dragStartDatas.length > 0) {
|
||||||
const newData = this.selectedGraphics.map((g) => g.saveData());
|
const newData = this.selectedGraphics.map((g) => g.saveData());
|
||||||
this.opRecord.record(
|
this.opRecord.record(
|
||||||
new DragOperation(
|
new GraphicDataUpdateOperation(
|
||||||
this,
|
this,
|
||||||
this.selectedGraphics,
|
this.selectedGraphics,
|
||||||
dragStartDatas,
|
dragStartDatas,
|
||||||
@ -293,6 +295,28 @@ export class JlDrawApp extends GraphicApp {
|
|||||||
dragStartDatas = [];
|
dragStartDatas = [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// 菜单操作
|
||||||
|
let preMenuHandleDatas: GraphicData[] = [];
|
||||||
|
this.on('pre-menu-handle', (menu: MenuItemOptions) => {
|
||||||
|
if (menu.name === '撤销' || menu.name === '重做') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
preMenuHandleDatas = this.selectedGraphics.map((g) => g.saveData());
|
||||||
|
});
|
||||||
|
this.on('post-menu-handle', () => {
|
||||||
|
if (preMenuHandleDatas.length > 0) {
|
||||||
|
const newData = this.selectedGraphics.map((g) => g.saveData());
|
||||||
|
this.opRecord.record(
|
||||||
|
new GraphicDataUpdateOperation(
|
||||||
|
this,
|
||||||
|
this.selectedGraphics,
|
||||||
|
preMenuHandleDatas,
|
||||||
|
newData
|
||||||
|
)
|
||||||
|
);
|
||||||
|
preMenuHandleDatas = [];
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -346,7 +370,6 @@ export class JlDrawApp extends GraphicApp {
|
|||||||
this.app.stage.addChild(this.scaleText);
|
this.app.stage.addChild(this.scaleText);
|
||||||
const bound = this.coordinates.getLocalBounds();
|
const bound = this.coordinates.getLocalBounds();
|
||||||
this.scaleText.position.set(bound.width + 10, 0);
|
this.scaleText.position.set(bound.width + 10, 0);
|
||||||
this.canvas.interactive = true;
|
|
||||||
this.canvas.on('mousemove', (e) => {
|
this.canvas.on('mousemove', (e) => {
|
||||||
if (e.target) {
|
if (e.target) {
|
||||||
const { x, y } = this.toCanvasCoordinates(e.global);
|
const { x, y } = this.toCanvasCoordinates(e.global);
|
||||||
@ -517,11 +540,11 @@ export class JlDrawApp extends GraphicApp {
|
|||||||
const data = g.saveData();
|
const data = g.saveData();
|
||||||
g.updateData(this.selectedData as GraphicData);
|
g.updateData(this.selectedData as GraphicData);
|
||||||
this.opRecord.record(
|
this.opRecord.record(
|
||||||
new UpdateDataOperation(
|
new GraphicDataUpdateOperation(
|
||||||
this,
|
this,
|
||||||
g,
|
[g],
|
||||||
data,
|
[data],
|
||||||
this.selectedData as GraphicData
|
[this.selectedData as GraphicData]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -604,7 +627,12 @@ function recordGraphicMoveOperation(app: GraphicApp) {
|
|||||||
) {
|
) {
|
||||||
const newData = app.selectedGraphics.map((g) => g.saveData());
|
const newData = app.selectedGraphics.map((g) => g.saveData());
|
||||||
app.opRecord.record(
|
app.opRecord.record(
|
||||||
new DragOperation(app, app.selectedGraphics, dragStartDatas, newData)
|
new GraphicDataUpdateOperation(
|
||||||
|
app,
|
||||||
|
app.selectedGraphics,
|
||||||
|
dragStartDatas,
|
||||||
|
newData
|
||||||
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
handleArrowKeyMoveGraphics(app, (g) => {
|
handleArrowKeyMoveGraphics(app, (g) => {
|
||||||
@ -695,39 +723,7 @@ export class GraphicDeleteOperation extends JlOperation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
export class GraphicDataUpdateOperation extends JlOperation {
|
||||||
* 更新数据操作
|
|
||||||
*/
|
|
||||||
export class UpdateDataOperation extends JlOperation {
|
|
||||||
obj: JlGraphic;
|
|
||||||
data: GraphicData; // 更新为data的protobuf数据
|
|
||||||
old: GraphicData; // 更新前data的protobuf数据
|
|
||||||
description = '';
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
app: GraphicApp,
|
|
||||||
obj: JlGraphic,
|
|
||||||
old: GraphicData,
|
|
||||||
data: GraphicData
|
|
||||||
) {
|
|
||||||
super(app, 'update-payload');
|
|
||||||
this.app = app;
|
|
||||||
this.obj = obj;
|
|
||||||
this.old = old;
|
|
||||||
this.data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
undo(): JlGraphic[] {
|
|
||||||
this.obj.updateData(this.old);
|
|
||||||
return [this.obj];
|
|
||||||
}
|
|
||||||
redo(): JlGraphic[] {
|
|
||||||
this.obj.updateData(this.data);
|
|
||||||
return [this.obj];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DragOperation extends JlOperation {
|
|
||||||
obj: JlGraphic[];
|
obj: JlGraphic[];
|
||||||
oldData: GraphicData[];
|
oldData: GraphicData[];
|
||||||
newData: GraphicData[];
|
newData: GraphicData[];
|
||||||
@ -746,7 +742,7 @@ export class DragOperation extends JlOperation {
|
|||||||
undo(): void | JlGraphic[] {
|
undo(): void | JlGraphic[] {
|
||||||
for (let i = 0; i < this.obj.length; i++) {
|
for (let i = 0; i < this.obj.length; i++) {
|
||||||
const g = this.obj[i];
|
const g = this.obj[i];
|
||||||
g.exitChildEdit();
|
// g.exitChildEdit();
|
||||||
g.updateData(this.oldData[i]);
|
g.updateData(this.oldData[i]);
|
||||||
}
|
}
|
||||||
return this.obj;
|
return this.obj;
|
||||||
@ -754,7 +750,7 @@ export class DragOperation extends JlOperation {
|
|||||||
redo(): void | JlGraphic[] {
|
redo(): void | JlGraphic[] {
|
||||||
for (let i = 0; i < this.obj.length; i++) {
|
for (let i = 0; i < this.obj.length; i++) {
|
||||||
const g = this.obj[i];
|
const g = this.obj[i];
|
||||||
g.exitChildEdit();
|
// g.exitChildEdit();
|
||||||
g.updateData(this.newData[i]);
|
g.updateData(this.newData[i]);
|
||||||
}
|
}
|
||||||
return this.obj;
|
return this.obj;
|
||||||
|
@ -45,6 +45,7 @@ import {
|
|||||||
} from '../plugins/KeyboardPlugin';
|
} from '../plugins/KeyboardPlugin';
|
||||||
import { ContextMenu, ContextMenuPlugin } from '../ui/ContextMenu';
|
import { ContextMenu, ContextMenuPlugin } from '../ui/ContextMenu';
|
||||||
import { getRectangleCenter, recursiveChildren } from '../utils/GraphicUtils';
|
import { getRectangleCenter, recursiveChildren } from '../utils/GraphicUtils';
|
||||||
|
import { MenuItemOptions } from '../ui/Menu';
|
||||||
|
|
||||||
export const AppConsts = {
|
export const AppConsts = {
|
||||||
viewportname: '__viewport',
|
viewportname: '__viewport',
|
||||||
@ -284,6 +285,8 @@ export interface GraphicAppEvents extends GlobalMixins.GraphicAppEvents {
|
|||||||
drag_op_start: [event: AppDragEvent];
|
drag_op_start: [event: AppDragEvent];
|
||||||
drag_op_move: [event: AppDragEvent];
|
drag_op_move: [event: AppDragEvent];
|
||||||
drag_op_end: [event: AppDragEvent];
|
drag_op_end: [event: AppDragEvent];
|
||||||
|
'pre-menu-handle': [menu: MenuItemOptions];
|
||||||
|
'post-menu-handle': [menu: MenuItemOptions];
|
||||||
destroy: [app: GraphicApp];
|
destroy: [app: GraphicApp];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,6 +305,16 @@ DisplayObject.prototype.localToScreenPoints = function localToScreenPoints(
|
|||||||
): Point[] {
|
): Point[] {
|
||||||
return points.map((p) => this.toGlobal(p));
|
return points.map((p) => this.toGlobal(p));
|
||||||
};
|
};
|
||||||
|
DisplayObject.prototype.screenToLocalPoint = function screenToLocalPoint(
|
||||||
|
p: IPointData
|
||||||
|
): Point {
|
||||||
|
return this.toLocal(p);
|
||||||
|
}
|
||||||
|
DisplayObject.prototype.screenToLocalPoints = function screenToLocalPoints(
|
||||||
|
...points: IPointData[]
|
||||||
|
): Point[] {
|
||||||
|
return points.map((p) => this.toLocal(p));
|
||||||
|
}
|
||||||
|
|
||||||
DisplayObject.prototype.localBoundsToCanvasPoints =
|
DisplayObject.prototype.localBoundsToCanvasPoints =
|
||||||
function localBoundsToCanvasPoints() {
|
function localBoundsToCanvasPoints() {
|
||||||
|
3
src/jlgraphic/global.d.ts
vendored
3
src/jlgraphic/global.d.ts
vendored
@ -11,6 +11,7 @@ declare namespace GlobalMixins {
|
|||||||
type BoundsGraphic = import('./plugins').BoundsGraphic;
|
type BoundsGraphic = import('./plugins').BoundsGraphic;
|
||||||
type IPointDataType = import('pixi.js').IPointData;
|
type IPointDataType = import('pixi.js').IPointData;
|
||||||
type PointType = import('pixi.js').Point;
|
type PointType = import('pixi.js').Point;
|
||||||
|
type FederatedMouseEvent = import('pixi.js').FederatedMouseEvent;
|
||||||
type DisplayObjectType = import('pixi.js').DisplayObject;
|
type DisplayObjectType = import('pixi.js').DisplayObject;
|
||||||
type ContainerType = import('pixi.js').Container;
|
type ContainerType = import('pixi.js').Container;
|
||||||
interface DisplayObjectEvents {
|
interface DisplayObjectEvents {
|
||||||
@ -22,6 +23,8 @@ declare namespace GlobalMixins {
|
|||||||
transformstart: [e: GraphicTransformEvent];
|
transformstart: [e: GraphicTransformEvent];
|
||||||
transforming: [e: GraphicTransformEvent];
|
transforming: [e: GraphicTransformEvent];
|
||||||
transformend: [e: GraphicTransformEvent];
|
transformend: [e: GraphicTransformEvent];
|
||||||
|
_rightclick: [e: FederatedMouseEvent];
|
||||||
|
_leftclick: [e: FederatedMouseEvent];
|
||||||
selected: [DisplayObjectType];
|
selected: [DisplayObjectType];
|
||||||
unselected: [DisplayObjectType];
|
unselected: [DisplayObjectType];
|
||||||
childselected: [DisplayObjectType];
|
childselected: [DisplayObjectType];
|
||||||
|
@ -46,6 +46,14 @@ export class OperationRecord {
|
|||||||
this.maxLen = maxLen;
|
this.maxLen = maxLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get hasUndo(): boolean {
|
||||||
|
return this.undoStack.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get hasRedo(): boolean {
|
||||||
|
return this.redoStack.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
setMaxLen(v: number) {
|
setMaxLen(v: number) {
|
||||||
this.maxLen = v;
|
this.maxLen = v;
|
||||||
const len = this.undoStack.length;
|
const len = this.undoStack.length;
|
||||||
|
@ -6,12 +6,20 @@ import {
|
|||||||
Graphics,
|
Graphics,
|
||||||
IDestroyOptions,
|
IDestroyOptions,
|
||||||
IPointData,
|
IPointData,
|
||||||
|
Point,
|
||||||
} from 'pixi.js';
|
} from 'pixi.js';
|
||||||
import { JlGraphic } from '../core';
|
import { JlGraphic } from '../core';
|
||||||
import { DraggablePoint } from '../graphic';
|
import { DraggablePoint } from '../graphic';
|
||||||
import { calculateMirrorPoint, distance2 } from '../utils';
|
|
||||||
import { MenuItemOptions, MenuOptions } from '../ui/Menu';
|
|
||||||
import { ContextMenu } from '../ui/ContextMenu';
|
import { ContextMenu } from '../ui/ContextMenu';
|
||||||
|
import { MenuItemOptions, MenuOptions } from '../ui/Menu';
|
||||||
|
import {
|
||||||
|
calculateFootPointFromPointToLine,
|
||||||
|
calculateMirrorPoint,
|
||||||
|
assertBezierPoints,
|
||||||
|
distance2,
|
||||||
|
linePoint,
|
||||||
|
pointPolygon,
|
||||||
|
} from '../utils';
|
||||||
import { GraphicTransformEvent, ShiftData } from './GraphicTransformPlugin';
|
import { GraphicTransformEvent, ShiftData } from './GraphicTransformPlugin';
|
||||||
|
|
||||||
export abstract class GraphicEditPlugin<
|
export abstract class GraphicEditPlugin<
|
||||||
@ -22,6 +30,7 @@ export abstract class GraphicEditPlugin<
|
|||||||
super();
|
super();
|
||||||
this.graphic = g;
|
this.graphic = g;
|
||||||
this.zIndex = 2;
|
this.zIndex = 2;
|
||||||
|
this.sortableChildren = true;
|
||||||
this.graphic.on('transformstart', this.hideAll, this);
|
this.graphic.on('transformstart', this.hideAll, this);
|
||||||
this.graphic.on('transformend', this.showAll, this);
|
this.graphic.on('transformend', this.showAll, this);
|
||||||
this.graphic.on('repaint', this.showAll, this);
|
this.graphic.on('repaint', this.showAll, this);
|
||||||
@ -45,20 +54,20 @@ export abstract class GraphicEditPlugin<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const addWaypoint: MenuItemOptions = {
|
export const addWaypointConfig: MenuItemOptions = {
|
||||||
name: '添加路径点',
|
name: '添加路径点',
|
||||||
};
|
};
|
||||||
const removeWaypoint: MenuItemOptions = {
|
export const removeWaypointConfig: MenuItemOptions = {
|
||||||
name: '移除路径点',
|
name: '移除路径点',
|
||||||
};
|
};
|
||||||
const clearWaypoints: MenuItemOptions = {
|
export const clearWaypointsConfig: MenuItemOptions = {
|
||||||
name: '清除所有路径点',
|
name: '清除所有路径点',
|
||||||
};
|
};
|
||||||
const menuOptions: MenuOptions = {
|
const menuOptions: MenuOptions = {
|
||||||
name: '图形编辑点菜单',
|
name: '图形编辑点菜单',
|
||||||
groups: [
|
groups: [
|
||||||
{
|
{
|
||||||
items: [addWaypoint, removeWaypoint, clearWaypoints],
|
items: [removeWaypointConfig, clearWaypointsConfig],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
@ -94,6 +103,42 @@ export abstract class LineEditPlugin extends GraphicEditPlugin<ILineGraphic> {
|
|||||||
abstract initEditPoints(): void;
|
abstract initEditPoints(): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getWaypointRangeIndex(
|
||||||
|
points: IPointData[],
|
||||||
|
curve: boolean,
|
||||||
|
p: IPointData
|
||||||
|
): { start: number; end: number } {
|
||||||
|
let start = 0;
|
||||||
|
let end = 0;
|
||||||
|
if (!curve) {
|
||||||
|
// 直线
|
||||||
|
for (let i = 1; i < points.length; i++) {
|
||||||
|
const sp = points[i - 1];
|
||||||
|
const ep = points[i];
|
||||||
|
const fp = calculateFootPointFromPointToLine(sp, ep, p);
|
||||||
|
if (linePoint(sp, ep, fp, 1, true)) {
|
||||||
|
start = i - 1;
|
||||||
|
end = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 贝塞尔曲线
|
||||||
|
assertBezierPoints(points);
|
||||||
|
for (let i = 0; i < points.length - 3; i += 3) {
|
||||||
|
const p1 = points[i];
|
||||||
|
const cp1 = points[i + 1];
|
||||||
|
const cp2 = points[i + 2];
|
||||||
|
const p2 = points[i + 3];
|
||||||
|
if (pointPolygon(p, [p1, cp1, cp2, p2], 1)) {
|
||||||
|
start = i;
|
||||||
|
end = i + 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { start, end };
|
||||||
|
}
|
||||||
|
|
||||||
export type onEditPointCreate = (
|
export type onEditPointCreate = (
|
||||||
g: ILineGraphic,
|
g: ILineGraphic,
|
||||||
dp: DraggablePoint,
|
dp: DraggablePoint,
|
||||||
@ -125,6 +170,19 @@ export class PolylineEditPlugin extends LineEditPlugin {
|
|||||||
for (let i = 0; i < cps.length; i++) {
|
for (let i = 0; i < cps.length; i++) {
|
||||||
const p = cps[i];
|
const p = cps[i];
|
||||||
const dp = new DraggablePoint(p);
|
const dp = new DraggablePoint(p);
|
||||||
|
dp.on('rightclick', (e: FederatedMouseEvent) => {
|
||||||
|
// dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu);
|
||||||
|
// 路径中的点
|
||||||
|
const app = dp.getGraphicApp();
|
||||||
|
app.registerMenu(EditPointContextMenu);
|
||||||
|
removeWaypointConfig.handler = () => {
|
||||||
|
removeLineWayPoint(this.graphic, i);
|
||||||
|
};
|
||||||
|
clearWaypointsConfig.handler = () => {
|
||||||
|
clearWayPoint(this.graphic, false);
|
||||||
|
};
|
||||||
|
EditPointContextMenu.open(e.global);
|
||||||
|
});
|
||||||
dp.on('transforming', () => {
|
dp.on('transforming', () => {
|
||||||
const tlp = this.graphic.canvasToLocalPoint(dp.position);
|
const tlp = this.graphic.canvasToLocalPoint(dp.position);
|
||||||
const cp = this.linePoints[i];
|
const cp = this.linePoints[i];
|
||||||
@ -166,6 +224,133 @@ export interface ICompleteBezierCurveEditPointOptions
|
|||||||
smooth: boolean;
|
smooth: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function addWayPoint(
|
||||||
|
graphic: ILineGraphic,
|
||||||
|
curve: boolean,
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
p: IPointData
|
||||||
|
) {
|
||||||
|
if (!curve) {
|
||||||
|
addLineWayPoint(graphic, start, end, p);
|
||||||
|
} else {
|
||||||
|
addBezierWayPoint(graphic, start, end, p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addLineWayPoint(
|
||||||
|
graphic: ILineGraphic,
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
p: IPointData
|
||||||
|
) {
|
||||||
|
const linePoints = graphic.linePoints;
|
||||||
|
const points = linePoints.slice(0, start + 1);
|
||||||
|
points.push(new Point(p.x, p.y));
|
||||||
|
points.push(...linePoints.slice(end));
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertBezierWayPoint(i: number) {
|
||||||
|
const c = i % 3;
|
||||||
|
if (c !== 0) {
|
||||||
|
throw new Error(`i=${i}的点不是路径点`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function addBezierWayPoint(
|
||||||
|
graphic: ILineGraphic,
|
||||||
|
start: number,
|
||||||
|
end: number,
|
||||||
|
p: IPointData
|
||||||
|
) {
|
||||||
|
if (start === end) {
|
||||||
|
throw new Error('开始结束点不能一致');
|
||||||
|
}
|
||||||
|
assertBezierWayPoint(start);
|
||||||
|
assertBezierWayPoint(end);
|
||||||
|
const linePoints = graphic.linePoints;
|
||||||
|
const points = linePoints.slice(0, start + 2);
|
||||||
|
const ap = new Point(p.x, p.y);
|
||||||
|
points.push(ap.clone(), ap.clone(), ap.clone());
|
||||||
|
points.push(...linePoints.slice(end - 1));
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeWayPoint(
|
||||||
|
graphic: ILineGraphic,
|
||||||
|
curve: boolean,
|
||||||
|
i: number
|
||||||
|
) {
|
||||||
|
if (!curve) {
|
||||||
|
removeLineWayPoint(graphic, i);
|
||||||
|
} else {
|
||||||
|
removeBezierWayPoint(graphic, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeLineWayPoint(graphic: ILineGraphic, i: number) {
|
||||||
|
const linePoints = graphic.linePoints;
|
||||||
|
if (linePoints.length > 2) {
|
||||||
|
const points = linePoints.slice(0, i);
|
||||||
|
points.push(...linePoints.slice(i + 1));
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function removeBezierWayPoint(graphic: ILineGraphic, i: number) {
|
||||||
|
let points;
|
||||||
|
const linePoints = graphic.linePoints;
|
||||||
|
const c = i % 3;
|
||||||
|
if (c !== 0) {
|
||||||
|
throw new Error(`i=${i}的点${linePoints[i]}不是路径点`);
|
||||||
|
}
|
||||||
|
if (i === 0) {
|
||||||
|
// 第一个点
|
||||||
|
if (linePoints.length > 4) {
|
||||||
|
points = linePoints.slice(3);
|
||||||
|
} else {
|
||||||
|
console.error('不能移除:剩余点数不足');
|
||||||
|
}
|
||||||
|
} else if (i === linePoints.length - 1) {
|
||||||
|
// 最后一个点
|
||||||
|
if (linePoints.length > 4) {
|
||||||
|
points = linePoints.slice(0, linePoints.length - 3);
|
||||||
|
} else {
|
||||||
|
console.error('无法移除:剩余点数不足');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 中间点
|
||||||
|
points = linePoints.slice(0, i - 1);
|
||||||
|
points.push(...linePoints.slice(i + 2));
|
||||||
|
}
|
||||||
|
if (points) {
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除路径点(只留端点),适用于直线和贝塞尔曲线
|
||||||
|
* @param graphic
|
||||||
|
* @param curve
|
||||||
|
*/
|
||||||
|
export function clearWayPoint(graphic: ILineGraphic, curve: boolean) {
|
||||||
|
const linePoints = graphic.linePoints;
|
||||||
|
if (!curve) {
|
||||||
|
if (linePoints.length > 2) {
|
||||||
|
const points = linePoints.slice(0, 1);
|
||||||
|
points.push(...linePoints.slice(-1));
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (linePoints.length > 4) {
|
||||||
|
const points = linePoints.slice(0, 2);
|
||||||
|
points.push(...linePoints.slice(-2));
|
||||||
|
graphic.linePoints = points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 贝塞尔曲线编辑
|
* 贝塞尔曲线编辑
|
||||||
*/
|
*/
|
||||||
@ -195,11 +380,15 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
|
|||||||
const startOrEnd = i == 0 || i == cps.length - 1;
|
const startOrEnd = i == 0 || i == cps.length - 1;
|
||||||
const c = i % 3;
|
const c = i % 3;
|
||||||
if (c === 1) {
|
if (c === 1) {
|
||||||
|
// 前一路径点的控制点
|
||||||
|
dp.zIndex = 2;
|
||||||
const fp = cps[i - 1];
|
const fp = cps[i - 1];
|
||||||
const line = new Graphics();
|
const line = new Graphics();
|
||||||
this.drawAuxiliaryLine(line, fp, p);
|
this.drawAuxiliaryLine(line, fp, p);
|
||||||
this.auxiliaryLines.push(line);
|
this.auxiliaryLines.push(line);
|
||||||
} else if (c === 2) {
|
} else if (c === 2) {
|
||||||
|
// 后一路径点的控制点
|
||||||
|
dp.zIndex = 3;
|
||||||
const np = cps[i + 1];
|
const np = cps[i + 1];
|
||||||
const line = new Graphics();
|
const line = new Graphics();
|
||||||
this.drawAuxiliaryLine(line, p, np);
|
this.drawAuxiliaryLine(line, p, np);
|
||||||
@ -208,34 +397,14 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
|
|||||||
dp.on('rightclick', (e: FederatedMouseEvent) => {
|
dp.on('rightclick', (e: FederatedMouseEvent) => {
|
||||||
// dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu);
|
// dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu);
|
||||||
if (c === 0) {
|
if (c === 0) {
|
||||||
|
// 路径中的点
|
||||||
const app = dp.getGraphicApp();
|
const app = dp.getGraphicApp();
|
||||||
app.registerMenu(EditPointContextMenu);
|
app.registerMenu(EditPointContextMenu);
|
||||||
removeWaypoint.onClick = () => {
|
removeWaypointConfig.handler = () => {
|
||||||
let points;
|
removeBezierWayPoint(this.graphic, i);
|
||||||
if (i === 0) {
|
};
|
||||||
// 第一个点
|
clearWaypointsConfig.handler = () => {
|
||||||
if (this.linePoints.length > 4) {
|
clearWayPoint(this.graphic, true);
|
||||||
points = this.linePoints.slice(3);
|
|
||||||
} else {
|
|
||||||
console.error('无法移除');
|
|
||||||
}
|
|
||||||
} else if (i === cps.length - 1) {
|
|
||||||
// 最后一个点
|
|
||||||
if (this.linePoints.length > 4) {
|
|
||||||
points = this.linePoints.slice(0, this.linePoints.length - 3);
|
|
||||||
} else {
|
|
||||||
console.error('无法移除');
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 中间点
|
|
||||||
points = [];
|
|
||||||
points.push(...this.linePoints.slice(0, i - 1));
|
|
||||||
points.push(...this.linePoints.slice(i + 2));
|
|
||||||
}
|
|
||||||
if (points) {
|
|
||||||
console.log('points', points);
|
|
||||||
this.graphic.linePoints = points;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
EditPointContextMenu.open(e.global);
|
EditPointContextMenu.open(e.global);
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ export class GraphicTransformPlugin extends BaseInteractionPlugin {
|
|||||||
return targets;
|
return targets;
|
||||||
}
|
}
|
||||||
onDragStart(e: AppDragEvent) {
|
onDragStart(e: AppDragEvent) {
|
||||||
if (!e.target.isCanvas()) {
|
if (!e.target.isCanvas() && e.isLeftButton) {
|
||||||
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
||||||
if (targets.length > 0) {
|
if (targets.length > 0) {
|
||||||
targets.forEach((target) => {
|
targets.forEach((target) => {
|
||||||
@ -248,7 +248,7 @@ export class GraphicTransformPlugin extends BaseInteractionPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDragMove(e: AppDragEvent) {
|
onDragMove(e: AppDragEvent) {
|
||||||
if (!e.target.isCanvas()) {
|
if (!e.target.isCanvas() && e.isLeftButton) {
|
||||||
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
||||||
if (targets.length > 0) {
|
if (targets.length > 0) {
|
||||||
// 处理位移
|
// 处理位移
|
||||||
@ -291,7 +291,7 @@ export class GraphicTransformPlugin extends BaseInteractionPlugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
onDragEnd(e: AppDragEvent) {
|
onDragEnd(e: AppDragEvent) {
|
||||||
if (!e.target.isCanvas()) {
|
if (!e.target.isCanvas() && e.isLeftButton) {
|
||||||
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
const targets: DisplayObject[] = this.getDraggedTargets(e);
|
||||||
targets.forEach((target) => {
|
targets.forEach((target) => {
|
||||||
if (target.shiftStartPoint) {
|
if (target.shiftStartPoint) {
|
||||||
|
@ -165,9 +165,10 @@ export class AppDragEvent {
|
|||||||
*/
|
*/
|
||||||
export class DragPlugin extends BaseInteractionPlugin {
|
export class DragPlugin extends BaseInteractionPlugin {
|
||||||
static Name = '__drag_operation_plugin';
|
static Name = '__drag_operation_plugin';
|
||||||
threshold = 3;
|
private threshold = 3;
|
||||||
target: DisplayObject | null = null;
|
target: DisplayObject | null = null;
|
||||||
start: Point | null = null;
|
start: Point | null = null;
|
||||||
|
startClientPoint: Point | null = null;
|
||||||
drag = false;
|
drag = false;
|
||||||
constructor(app: GraphicApp) {
|
constructor(app: GraphicApp) {
|
||||||
super(app, DragPlugin.Name, InteractionPluginType.Other);
|
super(app, DragPlugin.Name, InteractionPluginType.Other);
|
||||||
@ -193,17 +194,19 @@ export class DragPlugin extends BaseInteractionPlugin {
|
|||||||
onPointerDown(e: FederatedPointerEvent) {
|
onPointerDown(e: FederatedPointerEvent) {
|
||||||
this.target = e.target as DisplayObject;
|
this.target = e.target as DisplayObject;
|
||||||
this.start = this.app.toCanvasCoordinates(e.global);
|
this.start = this.app.toCanvasCoordinates(e.global);
|
||||||
|
this.startClientPoint = e.global.clone();
|
||||||
const canvas = this.app.canvas;
|
const canvas = this.app.canvas;
|
||||||
canvas.on('pointermove', this.onPointerMove, this);
|
canvas.on('pointermove', this.onPointerMove, this);
|
||||||
canvas.on('pointerup', this.onPointerUp, this);
|
canvas.on('pointerup', this.onPointerUp, this);
|
||||||
canvas.on('pointerupoutside', this.onPointerUp, this);
|
canvas.on('pointerupoutside', this.onPointerUp, this);
|
||||||
}
|
}
|
||||||
onPointerMove(e: FederatedPointerEvent) {
|
onPointerMove(e: FederatedPointerEvent) {
|
||||||
if (this.start) {
|
if (this.start && this.startClientPoint) {
|
||||||
const current = this.app.toCanvasCoordinates(e.global);
|
const current = e.global;
|
||||||
|
const sgp = this.startClientPoint;
|
||||||
const dragStart =
|
const dragStart =
|
||||||
Math.abs(current.x - this.start.x) > this.threshold ||
|
Math.abs(current.x - sgp.x) > this.threshold ||
|
||||||
Math.abs(current.y - this.start.y) > this.threshold;
|
Math.abs(current.y - sgp.y) > this.threshold;
|
||||||
if (this.target && this.start && !this.drag && dragStart) {
|
if (this.target && this.start && !this.drag && dragStart) {
|
||||||
this.app.emit(
|
this.app.emit(
|
||||||
'drag_op_start',
|
'drag_op_start',
|
||||||
@ -230,6 +233,21 @@ export class DragPlugin extends BaseInteractionPlugin {
|
|||||||
'drag_op_end',
|
'drag_op_end',
|
||||||
new AppDragEvent(this.app, 'end', this.target, e, this.start)
|
new AppDragEvent(this.app, 'end', this.target, e, this.start)
|
||||||
);
|
);
|
||||||
|
} else if (this.target && this.start && !this.drag) {
|
||||||
|
// this.target.emit('click', this.target);
|
||||||
|
const ade = new AppDragEvent(this.app, 'end', this.target, e, this.start);
|
||||||
|
const graphic = this.target.getGraphic();
|
||||||
|
if (ade.isRightButton) {
|
||||||
|
this.target.emit('_rightclick', e);
|
||||||
|
if (graphic != null) {
|
||||||
|
graphic.emit('_rightclick', e);
|
||||||
|
}
|
||||||
|
} else if (ade.isLeftButton) {
|
||||||
|
this.target.emit('_leftclick', e);
|
||||||
|
if (graphic != null) {
|
||||||
|
graphic.emit('_leftclick', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const canvas = this.app.canvas;
|
const canvas = this.app.canvas;
|
||||||
canvas.off('mousemove', this.onPointerMove, this);
|
canvas.off('mousemove', this.onPointerMove, this);
|
||||||
@ -243,6 +261,7 @@ export class DragPlugin extends BaseInteractionPlugin {
|
|||||||
clearCache() {
|
clearCache() {
|
||||||
this.drag = false;
|
this.drag = false;
|
||||||
this.start = null;
|
this.start = null;
|
||||||
|
this.startClientPoint = null;
|
||||||
this.target = null;
|
this.target = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,12 @@ export class ContextMenuPlugin {
|
|||||||
|
|
||||||
constructor(app: GraphicApp) {
|
constructor(app: GraphicApp) {
|
||||||
this.app = app;
|
this.app = app;
|
||||||
|
const canvas = this.app.canvas;
|
||||||
|
canvas.on('pointerdown', () => {
|
||||||
|
this.contextMenuMap.forEach((menu) => {
|
||||||
|
menu.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
registerMenu(menu: ContextMenu) {
|
registerMenu(menu: ContextMenu) {
|
||||||
@ -262,7 +268,7 @@ export class ContextMenu extends Container {
|
|||||||
|
|
||||||
this.addChild(...this.groups);
|
this.addChild(...this.groups);
|
||||||
|
|
||||||
this.interactive = true;
|
this.eventMode = 'static';
|
||||||
this.on('pointerover', () => {
|
this.on('pointerover', () => {
|
||||||
this.active = true;
|
this.active = true;
|
||||||
});
|
});
|
||||||
@ -288,6 +294,7 @@ export class ContextMenu extends Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateBg() {
|
updateBg() {
|
||||||
|
this.bg.clear();
|
||||||
const options = this.menuOptions;
|
const options = this.menuOptions;
|
||||||
let maxItemWidth = 0;
|
let maxItemWidth = 0;
|
||||||
let borderHeight = 0;
|
let borderHeight = 0;
|
||||||
@ -422,10 +429,7 @@ class MenuGroup extends Container {
|
|||||||
config.items.forEach((item) => {
|
config.items.forEach((item) => {
|
||||||
this.items.push(new ContextMenuItem(menu, item));
|
this.items.push(new ContextMenuItem(menu, item));
|
||||||
});
|
});
|
||||||
for (let i = 0; i < this.items.length; i++) {
|
|
||||||
const item = this.items[i];
|
|
||||||
item.position.y = i * item.totalHeight;
|
|
||||||
}
|
|
||||||
this.addChild(...this.items);
|
this.addChild(...this.items);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -464,7 +468,21 @@ class MenuGroup extends Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
this.items.forEach((item) => item.update());
|
let i = 0;
|
||||||
|
this.items.forEach((item) => {
|
||||||
|
item.update();
|
||||||
|
if (item.visible) {
|
||||||
|
item.position.y = i * item.totalHeight;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// this.items.forEach()
|
||||||
|
// for (let i = 0; i < this.items.length; i++) {
|
||||||
|
// const item = this.items[i];
|
||||||
|
// if (item.visible) {
|
||||||
|
// item.position.y = i * item.totalHeight;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
updateItemBox(maxItemWidth: number) {
|
updateItemBox(maxItemWidth: number) {
|
||||||
@ -511,6 +529,42 @@ class ContextMenuItem extends Container {
|
|||||||
this.initSubMenu();
|
this.initSubMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
registerEventHandler() {
|
||||||
|
this.eventMode = 'static';
|
||||||
|
this.cursor = 'pointer';
|
||||||
|
this.on('pointerover', () => {
|
||||||
|
this.active = true;
|
||||||
|
if (this.config.disabled) {
|
||||||
|
this.cursor = 'not-allowed';
|
||||||
|
} else {
|
||||||
|
this.cursor = 'pointer';
|
||||||
|
}
|
||||||
|
if (this.subMenu) {
|
||||||
|
const p = this.toGlobal(new Point(this.width));
|
||||||
|
this.subMenu.open(p);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.on('pointerout', () => {
|
||||||
|
this.active = false;
|
||||||
|
});
|
||||||
|
this.on('pointertap', () => {
|
||||||
|
if (this.config.disabled) {
|
||||||
|
// 禁用,不处理
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.config.handler) {
|
||||||
|
this.menu.plugin.app.emit('pre-menu-handle', this.config);
|
||||||
|
this.config.handler();
|
||||||
|
this.menu.plugin.app.emit('post-menu-handle', this.config);
|
||||||
|
}
|
||||||
|
if (!this.config.subMenu || this.config.subMenu.length === 0) {
|
||||||
|
this.active = false;
|
||||||
|
this.menu.active = false;
|
||||||
|
this.menu.rootMenu.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public get active(): boolean {
|
public get active(): boolean {
|
||||||
return this._active || (this.subMenu != undefined && this.subMenu.active);
|
return this._active || (this.subMenu != undefined && this.subMenu.active);
|
||||||
}
|
}
|
||||||
@ -543,7 +597,11 @@ class ContextMenuItem extends Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public get totalHeight(): number {
|
public get totalHeight(): number {
|
||||||
return this.paddingTop + this.paddingBottom + this.nameGraphic.height;
|
if (this.config.visible === false) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return this.paddingTop + this.paddingBottom + this.nameGraphic.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public get nameBounds(): Rectangle {
|
public get nameBounds(): Rectangle {
|
||||||
@ -683,41 +741,16 @@ class ContextMenuItem extends Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 事件监听
|
// 事件监听
|
||||||
this.interactive = true;
|
|
||||||
this.hitArea = new Rectangle(0, 0, width, height);
|
this.hitArea = new Rectangle(0, 0, width, height);
|
||||||
this.cursor = 'pointer';
|
this.registerEventHandler();
|
||||||
this.on('pointerover', () => {
|
|
||||||
this.active = true;
|
|
||||||
if (this.config.disabled) {
|
|
||||||
this.cursor = 'not-allowed';
|
|
||||||
} else {
|
|
||||||
this.cursor = 'pointer';
|
|
||||||
}
|
|
||||||
if (this.subMenu) {
|
|
||||||
const p = this.toGlobal(new Point(this.width));
|
|
||||||
this.subMenu.open(p);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
this.on('pointerout', () => {
|
|
||||||
this.active = false;
|
|
||||||
});
|
|
||||||
this.on('pointertap', () => {
|
|
||||||
if (this.config.disabled) {
|
|
||||||
// 禁用,不处理
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.config.onClick) {
|
|
||||||
this.config.onClick();
|
|
||||||
}
|
|
||||||
if (!this.config.subMenu || this.config.subMenu.length === 0) {
|
|
||||||
this.active = false;
|
|
||||||
this.menu.active = false;
|
|
||||||
this.menu.rootMenu.close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
update() {
|
update() {
|
||||||
|
if (this.config.visible === false) {
|
||||||
|
this.visible = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.visible = true;
|
||||||
this.nameText.text = this.config.name;
|
this.nameText.text = this.config.name;
|
||||||
this.nameText.style.fontSize = this.fontSize;
|
this.nameText.style.fontSize = this.fontSize;
|
||||||
this.nameText.style.fill = this.fontColor;
|
this.nameText.style.fill = this.fontColor;
|
||||||
|
@ -23,6 +23,10 @@ export interface MenuOptions {
|
|||||||
* 菜单分组
|
* 菜单分组
|
||||||
*/
|
*/
|
||||||
export interface MenuGroupOptions {
|
export interface MenuGroupOptions {
|
||||||
|
/**
|
||||||
|
* 分组命名
|
||||||
|
*/
|
||||||
|
name?: string;
|
||||||
/**
|
/**
|
||||||
* 菜单项
|
* 菜单项
|
||||||
*/
|
*/
|
||||||
@ -53,7 +57,7 @@ export interface MenuStyleOptions {
|
|||||||
*/
|
*/
|
||||||
borderColor?: string;
|
borderColor?: string;
|
||||||
/**
|
/**
|
||||||
* 包围框是否圆角,小于等于0为直角,圆角的半径
|
* 包围框是否圆角,圆角的半径,0为直角
|
||||||
*/
|
*/
|
||||||
borderRoundRadius?: number;
|
borderRoundRadius?: number;
|
||||||
/**
|
/**
|
||||||
@ -90,9 +94,9 @@ export interface MenuItemOptions {
|
|||||||
*/
|
*/
|
||||||
shortcutKeys?: string[];
|
shortcutKeys?: string[];
|
||||||
/**
|
/**
|
||||||
* 点击处理
|
* 菜单逻辑处理
|
||||||
*/
|
*/
|
||||||
onClick?: () => void;
|
handler?: () => void;
|
||||||
fontColor?: string;
|
fontColor?: string;
|
||||||
/**
|
/**
|
||||||
* 子菜单
|
* 子菜单
|
||||||
@ -151,7 +155,7 @@ export const DefaultWhiteStyleOptions: MenuCompletionStyleOptions = {
|
|||||||
borderWidth: 1,
|
borderWidth: 1,
|
||||||
borderColor: '#4C4C4C',
|
borderColor: '#4C4C4C',
|
||||||
/**
|
/**
|
||||||
* 默认直角
|
* 默认圆角
|
||||||
*/
|
*/
|
||||||
borderRoundRadius: 5,
|
borderRoundRadius: 5,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
|
@ -2,12 +2,11 @@ import {
|
|||||||
Container,
|
Container,
|
||||||
DisplayObject,
|
DisplayObject,
|
||||||
IPointData,
|
IPointData,
|
||||||
Matrix,
|
|
||||||
Point,
|
Point,
|
||||||
Rectangle,
|
Rectangle,
|
||||||
} from 'pixi.js';
|
} from 'pixi.js';
|
||||||
import Vector2 from '../math/Vector2';
|
|
||||||
import { floatEquals, isZero } from '../math';
|
import { floatEquals, isZero } from '../math';
|
||||||
|
import Vector2 from '../math/Vector2';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归父节点执行逻辑
|
* 递归父节点执行逻辑
|
||||||
@ -86,10 +85,23 @@ export interface BezierParam {
|
|||||||
cp2: IPointData;
|
cp2: IPointData;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function convertToBezierParams(points: IPointData[]): BezierParam[] {
|
/**
|
||||||
|
* 判断贝塞尔曲线数据是否正确
|
||||||
|
* @param points
|
||||||
|
*/
|
||||||
|
export function assertBezierPoints(points: IPointData[]) {
|
||||||
if (points.length < 4 || points.length % 3 !== 1) {
|
if (points.length < 4 || points.length % 3 !== 1) {
|
||||||
throw new Error(`bezierCurve 数据错误: ${points}`);
|
throw new Error(`bezierCurve 数据错误: ${points}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转换为贝塞尔曲线参数
|
||||||
|
* @param points
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export function convertToBezierParams(points: IPointData[]): BezierParam[] {
|
||||||
|
assertBezierPoints(points);
|
||||||
const bps: BezierParam[] = [];
|
const bps: BezierParam[] = [];
|
||||||
for (let i = 0; i < points.length - 3; i += 3) {
|
for (let i = 0; i < points.length - 3; i += 3) {
|
||||||
const p1 = new Point(points[i].x, points[i].y);
|
const p1 = new Point(points[i].x, points[i].y);
|
||||||
@ -240,22 +252,6 @@ export function deserializeTransformInto(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 计算变换后的点坐标
|
|
||||||
* @param point 坐标点
|
|
||||||
* @param transform 变换矩阵
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export function calculateTransformedPoint(
|
|
||||||
point: IPointData,
|
|
||||||
transform: Matrix
|
|
||||||
): Point {
|
|
||||||
const { a, b, c, d, tx, ty } = transform;
|
|
||||||
const x = a * point.x + c * point.y + tx;
|
|
||||||
const y = b * point.x + d * point.y + ty;
|
|
||||||
return new Point(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将直线转换为多边形
|
* 将直线转换为多边形
|
||||||
* @param p1
|
* @param p1
|
||||||
|
Loading…
Reference in New Issue
Block a user