菜单使用调整

复制bug修改
编辑点相关调整
link编辑点添加右键菜单,实现移除编辑点逻辑
This commit is contained in:
walker 2023-05-09 18:27:15 +08:00
parent f649cb4a3d
commit cd951906b0
16 changed files with 443 additions and 167 deletions

View File

@ -12,6 +12,8 @@ viewport 使用的 github 开源的 pixi-viewport[pixi-viewport](https://github.
- 图形复制功能(完成)
- 绘制应用图形外包围框及旋转缩放功能(完成)
- 绘制增加吸附功能(移动到特定位置附近吸附)(完成)
- 图形动画抽象
- 添加公用动画逻辑(如按指定路径位移,按角度旋转、按比例缩放、透明度控制等)
- 菜单事件及处理
- 打包
- 添加拖拽轨迹限制功能

View File

@ -27,6 +27,20 @@ export abstract class BaseGraphicData implements GraphicData {
this._data = data;
}
static defaultCommonInfo(): graphicData.CommonInfo {
return new graphicData.CommonInfo({
id: '',
graphicType: '',
transform: new graphicData.Transform({
position: new graphicData.Point({ x: 0, y: 0 }),
scale: new graphicData.Point({ x: 1, y: 1 }),
rotation: 0,
skew: new graphicData.Point({ x: 0, y: 0 }),
}),
childTransforms: [],
});
}
getData<D extends IProtoGraphicData>(): D {
return this._data as D;
}

View File

@ -10,7 +10,7 @@ export class IscsFanData extends BaseGraphicData implements IIscsFanData {
fan = data;
} else {
fan = new graphicData.IscsFan({
common: new graphicData.CommonInfo(),
common: BaseGraphicData.defaultCommonInfo(),
});
}
super(fan);

View File

@ -9,7 +9,7 @@ export class LinkData extends BaseGraphicData implements ILinkData {
let link;
if (!data) {
link = new graphicData.Link({
common: new graphicData.CommonInfo(),
common: BaseGraphicData.defaultCommonInfo(),
});
} else {
link = data;

View File

@ -1,9 +1,5 @@
import { FederatedMouseEvent, Point } from 'pixi.js';
import {
GraphicDrawAssistant,
GraphicTransform,
JlDrawApp,
} from 'src/jlgraphic';
import { GraphicDrawAssistant, JlDrawApp } from 'src/jlgraphic';
import { IIscsFanData, IscsFan, IscsFanTemplate } from './IscsFan';
export class IscsFanDraw extends GraphicDrawAssistant<
@ -43,10 +39,7 @@ export class IscsFanDraw extends GraphicDrawAssistant<
this.finish();
}
prepareData(data: IIscsFanData): boolean {
// 变换设置必须新建变换赋值,不能直接修改变换数据
const transfrom = GraphicTransform.default();
transfrom.position = this.iscsFan.position.clone();
data.transform = transfrom;
data.transform = this.iscsFan.saveTransform();
return true;
}
onEsc(): void {

View File

@ -3,8 +3,9 @@ import {
GraphicData,
JlGraphic,
JlGraphicTemplate,
convertToBezierPoints,
convertToBezierParams,
} from 'src/jlgraphic';
import { ILineGraphic } from 'src/jlgraphic/plugins/GraphicEditPlugin';
export interface ILinkData extends GraphicData {
get code(): string; // 编号
@ -24,7 +25,7 @@ export interface ILinkData extends GraphicData {
eq(other: ILinkData): boolean;
}
export class Link extends JlGraphic {
export class Link extends JlGraphic implements ILineGraphic {
static Type = 'Link';
lineGraphic: Graphics;
constructor() {
@ -32,6 +33,7 @@ export class Link extends JlGraphic {
this.lineGraphic = new Graphics();
this.addChild(this.lineGraphic);
}
get datas(): ILinkData {
return this.getDatas<ILinkData>();
}
@ -46,7 +48,7 @@ export class Link extends JlGraphic {
);
if (this.datas.curve) {
// 曲线
const bps = convertToBezierPoints(this.datas.points);
const bps = convertToBezierParams(this.datas.points);
bps.forEach((bp) => {
this.lineGraphic.drawBezierCurve(
bp.p1,
@ -66,7 +68,14 @@ export class Link extends JlGraphic {
}
}
}
get linePoints(): IPointData[] {
return this.datas.points;
}
set linePoints(points: IPointData[]) {
this.datas.points = points;
this.repaint();
this.emit('pointupdate', this, this.datas.points);
}
getStartPoint(): IPointData {
return this.datas.points[0];
}

View File

@ -17,7 +17,7 @@ import {
JlGraphic,
KeyListener,
calculateMirrorPoint,
convertToBezierPoints,
convertToBezierParams,
linePoint,
pointPolygon,
} from 'src/jlgraphic';
@ -26,6 +26,7 @@ import AbsorbablePoint, {
} from 'src/jlgraphic/graphic/AbsorbablePosition';
import {
BezierCurveEditPlugin,
ILineGraphic,
PolylineEditPlugin,
} from 'src/jlgraphic/plugins/GraphicEditPlugin';
import { ILinkData, Link, LinkTemplate } from './Link';
@ -64,7 +65,7 @@ export class LinkDraw extends GraphicDrawAssistant<LinkTemplate, ILinkData> {
this.container.addChild(this.graphic);
this.graphicTemplate.curve = true;
new LinkPointsEditPlugin(app);
LinkPointsEditPlugin.init(app);
}
bind(): void {
@ -132,7 +133,7 @@ export class LinkDraw extends GraphicDrawAssistant<LinkTemplate, ILinkData> {
cp.copyFrom(mp);
}
const bps = convertToBezierPoints(ps);
const bps = convertToBezierParams(ps);
bps.forEach((bp) =>
this.graphic.drawBezierCurve(
bp.p1,
@ -184,7 +185,7 @@ export class LinkGraphicHitArea implements IHitArea {
const p = new Point(x, y);
if (this.link.datas.curve) {
// 曲线
const bps = convertToBezierPoints(this.link.datas.points);
const bps = convertToBezierParams(this.link.datas.points);
for (let i = 0; i < bps.length; i++) {
const bp = bps[i];
if (
@ -244,7 +245,7 @@ function buildAbsorbablePositions(link: Link): AbsorbablePosition[] {
* @param index
*/
function onEditPointCreate(
g: JlGraphic,
g: ILineGraphic,
dp: DraggablePoint,
index: number
): void {
@ -267,6 +268,9 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
constructor(app: GraphicApp) {
super(LinkPointsEditPlugin.Name, app);
}
static init(app: GraphicApp): LinkPointsEditPlugin {
return new LinkPointsEditPlugin(app);
}
filter(...grahpics: JlGraphic[]): Link[] | undefined {
return grahpics.filter((g) => g.type == Link.Type) as Link[];
}
@ -291,11 +295,9 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
BezierCurveEditPlugin.Name
);
if (!lep) {
lep = new BezierCurveEditPlugin(
link,
link.datas.points,
onEditPointCreate
);
lep = new BezierCurveEditPlugin(link, {
onEditPointCreate,
});
link.addAssistantAppend(lep);
}
} else {
@ -304,11 +306,7 @@ export class LinkPointsEditPlugin extends GraphicInteractionPlugin<Link> {
PolylineEditPlugin.Name
);
if (!lep) {
lep = new PolylineEditPlugin(
link,
link.datas.points,
onEditPointCreate
);
lep = new PolylineEditPlugin(link, { onEditPointCreate });
link.addAssistantAppend(lep);
}
}

View File

@ -41,7 +41,7 @@ import {
JlGraphicAppKeyboardPlugin,
KeyListener,
} from '../plugins/KeyboardPlugin';
import { ContextMenuPlugin } from '../ui/ContextMenu';
import { ContextMenu, ContextMenuPlugin } from '../ui/ContextMenu';
import { getRectangleCenter, recursiveChildren } from '../utils/GraphicUtils';
export const AppConsts = {
@ -479,6 +479,14 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
return template as GT;
}
/**
*
* @param menu
*/
registerMenu(menu: ContextMenu) {
this.menuPlugin.registerMenu(menu);
}
/**
* 使websocket Stomp通信
*/
@ -609,7 +617,7 @@ export class GraphicApp extends EventEmitter<GraphicAppEvents> {
for (const item of this.graphicTemplateMap) {
await item[1].loadAssets();
}
console.log('开始加载proto数据', protos);
// console.log('开始加载proto数据', protos);
// 加载数据到图形存储
protos.forEach((proto) => {
const template = this.getGraphicTemplatesByType(proto.graphicType);

View File

@ -917,14 +917,15 @@ export abstract class JlGraphicTemplate<G extends JlGraphic> {
if (graphic._datas) {
// 数据克隆
const datas = graphic.saveData();
g.updateData(datas);
g.loadData(datas);
}
if (graphic._states) {
// 状态克隆
const state = graphic.getStates().clone();
g.updateStates(state);
g.loadState(state);
}
g.id = GraphicIdGenerator.next();
g.repaint();
return g;
}
}

View File

@ -143,6 +143,8 @@ export class CommonMouseTool extends AppInteractionPlugin {
drag = false;
graphicSelect = false;
rightTarget: DisplayObject | null = null;
/**
*
*/
@ -206,16 +208,22 @@ export class CommonMouseTool extends AppInteractionPlugin {
this.clearCache();
}
setCursor() {
if (this.app.app.view.style) {
setCursor(e: FederatedMouseEvent) {
this.rightTarget = e.target as DisplayObject;
if (e.target instanceof JlCanvas && this.app.app.view.style) {
this.app.app.view.style.cursor = 'grab';
}
}
resumeCursor() {
if (this.app.app.view.style) {
if (
this.rightTarget &&
this.rightTarget instanceof JlCanvas &&
this.app.app.view.style
) {
this.app.app.view.style.cursor = 'inherit';
}
this.rightTarget = null;
}
onMouseDown(e: FederatedMouseEvent) {

View File

@ -69,10 +69,12 @@ export class GraphicCopyPlugin {
const template = app.getGraphicTemplatesByType(g.type);
const clone = template.clone(g);
this.copys.push(clone);
this.container.position.set(0, 0);
this.container.addChild(clone);
});
this.app.canvas.on('mousemove', this.onPointerMove, this);
this.app.canvas.on('pointertap', this.onFinish, this);
this.app.canvas.on('mouseup', this.onFinish, this);
this.app.canvas.on('rightup', this.cancle, this);
this.keyListeners.forEach((kl) => {
this.app.addKeyboardListener(kl);
});
@ -86,7 +88,8 @@ export class GraphicCopyPlugin {
this.container.removeChildren();
this.app.canvas.removeChild(this.container);
this.app.canvas.off('mousemove', this.onPointerMove, this);
this.app.canvas.off('pointertap', this.onFinish, this);
this.app.canvas.off('mouseup', this.onFinish, this);
this.app.canvas.off('rightup', this.cancle, this);
this.keyListeners.forEach((kl) => {
this.app.removeKeyboardListener(kl);
});

View File

@ -2,6 +2,7 @@ import {
Color,
Container,
DisplayObject,
FederatedMouseEvent,
Graphics,
IDestroyOptions,
IPointData,
@ -10,10 +11,14 @@ 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';
export abstract class GraphicEditPlugin extends Container {
graphic: JlGraphic;
constructor(g: JlGraphic) {
export abstract class GraphicEditPlugin<
DO extends DisplayObject = DisplayObject
> extends Container {
graphic: DO;
constructor(g: DO) {
super();
this.graphic = g;
this.zIndex = 2;
@ -40,18 +45,37 @@ export abstract class GraphicEditPlugin extends Container {
}
}
export abstract class LineEditPlugin extends GraphicEditPlugin {
const addWaypoint: MenuItemOptions = {
name: '添加路径点',
};
const removeWaypoint: MenuItemOptions = {
name: '移除路径点',
};
const clearWaypoints: MenuItemOptions = {
name: '清除所有路径点',
};
const menuOptions: MenuOptions = {
name: '图形编辑点菜单',
groups: [
{
items: [addWaypoint, removeWaypoint, clearWaypoints],
},
],
};
const EditPointContextMenu = ContextMenu.init(menuOptions);
export interface ILineGraphic extends JlGraphic {
get linePoints(): IPointData[];
set linePoints(points: IPointData[]);
}
export abstract class LineEditPlugin extends GraphicEditPlugin<ILineGraphic> {
linePoints: IPointData[];
editedPoints: DraggablePoint[] = [];
onEditPointCreate?: onEditPointCreate;
constructor(
g: JlGraphic,
linePoints: IPointData[],
onEditPointCreate?: onEditPointCreate
) {
constructor(g: ILineGraphic) {
super(g);
this.linePoints = linePoints;
this.onEditPointCreate = onEditPointCreate;
this.linePoints = g.linePoints;
this.graphic.on('pointupdate', this.reset, this);
}
@ -71,21 +95,27 @@ export abstract class LineEditPlugin extends GraphicEditPlugin {
}
export type onEditPointCreate = (
g: JlGraphic,
g: ILineGraphic,
dp: DraggablePoint,
index: number
) => void;
export interface IEditPointOptions {
/**
*
*/
onEditPointCreate?: onEditPointCreate;
}
/**
* 线(线)
*/
export class PolylineEditPlugin extends LineEditPlugin {
static Name = 'line_points_edit';
constructor(
g: JlGraphic,
linePoints: IPointData[],
onEditPointCreate?: onEditPointCreate
) {
super(g, linePoints, onEditPointCreate);
options: IEditPointOptions;
constructor(g: ILineGraphic, options?: IEditPointOptions) {
super(g);
this.options = Object.assign({}, options);
this.name = PolylineEditPlugin.Name;
this.initEditPoints();
}
@ -102,8 +132,8 @@ export class PolylineEditPlugin extends LineEditPlugin {
cp.y = tlp.y;
this.graphic.repaint();
});
if (this.onEditPointCreate) {
this.onEditPointCreate(this.graphic, dp, i);
if (this.options.onEditPointCreate) {
this.options.onEditPointCreate(this.graphic, dp, i);
}
this.editedPoints.push(dp);
}
@ -120,19 +150,33 @@ export class PolylineEditPlugin extends LineEditPlugin {
}
}
export interface BezierCurveEditPointOptions extends IEditPointOptions {
// 曲线控制点辅助连线颜色
auxiliaryLineColor?: string;
// // 拖拽点颜色
// pointColor?: string;
// 连接点处是否平滑(点左右控制点是否在一条直线),默认true
smooth?: boolean;
// 控制点是否完全对称(对称必平滑)
symmetry?: boolean;
}
export interface ICompleteBezierCurveEditPointOptions
extends BezierCurveEditPointOptions {
smooth: boolean;
}
/**
* 线
*/
export class BezierCurveEditPlugin extends LineEditPlugin {
static Name = 'bezier_curve_points_edit';
options: ICompleteBezierCurveEditPointOptions;
// 曲线控制点辅助线
auxiliaryLines: Graphics[] = [];
constructor(
g: JlGraphic,
linePoints: IPointData[],
onEditPointCreate?: onEditPointCreate
) {
super(g, linePoints, onEditPointCreate);
constructor(g: ILineGraphic, options?: BezierCurveEditPointOptions) {
super(g);
this.options = Object.assign({}, { smooth: true }, options);
this.name = BezierCurveEditPlugin.Name;
this.initEditPoints();
}
@ -143,6 +187,7 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
}
initEditPoints() {
console.log('initEditPoints');
const cps = this.graphic.localToCanvasPoints(...this.linePoints);
for (let i = 0; i < cps.length; i++) {
const p = cps[i];
@ -160,11 +205,47 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
this.drawAuxiliaryLine(line, p, np);
this.auxiliaryLines.push(line);
}
dp.on('rightclick', (e: FederatedMouseEvent) => {
// dp.getGraphicApp().openContextMenu(EditPointContextMenu.DefaultMenu);
if (c === 0) {
const app = dp.getGraphicApp();
app.registerMenu(EditPointContextMenu);
removeWaypoint.onClick = () => {
let points;
if (i === 0) {
// 第一个点
if (this.linePoints.length > 4) {
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);
}
});
dp.on('dragmove', (de: GraphicDragEvent) => {
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 fp = this.linePoints[i - 1];
const np = this.linePoints[i + 1];
@ -175,22 +256,33 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
} else if (c === 1 && i !== 1) {
const bp = this.linePoints[i - 1];
const fp2 = this.linePoints[i - 2];
let mp;
if (this.options.symmetry) {
mp = calculateMirrorPoint(bp, cp);
} else {
const distance = distance2(bp, fp2);
const mp = calculateMirrorPoint(bp, cp, distance);
mp = calculateMirrorPoint(bp, cp, distance);
}
fp2.x = mp.x;
fp2.y = mp.y;
} else if (c === 2 && i !== cps.length - 2) {
const bp = this.linePoints[i + 1];
const np2 = this.linePoints[i + 2];
let mp;
if (this.options.symmetry) {
mp = calculateMirrorPoint(bp, cp);
} else {
const distance = distance2(bp, np2);
const mp = calculateMirrorPoint(bp, cp, distance);
mp = calculateMirrorPoint(bp, cp, distance);
}
np2.x = mp.x;
np2.y = mp.y;
}
}
this.graphic.repaint();
});
if (this.onEditPointCreate) {
this.onEditPointCreate(this.graphic, dp, i);
if (this.options.onEditPointCreate) {
this.options.onEditPointCreate(this.graphic, dp, i);
}
this.editedPoints.push(dp);
if (this.auxiliaryLines.length > 0) {
@ -202,7 +294,11 @@ export class BezierCurveEditPlugin extends LineEditPlugin {
drawAuxiliaryLine(line: Graphics, p1: IPointData, p2: IPointData) {
line.clear();
if (this.options.auxiliaryLineColor) {
line.lineStyle(1, new Color(this.options.auxiliaryLineColor));
} else {
line.lineStyle(1, new Color('blue'));
}
line.moveTo(p1.x, p1.y);
line.lineTo(p2.x, p2.y);
}

View File

@ -1,4 +1,4 @@
import { Container, Graphics, Point, Rectangle, Text, utils } from 'pixi.js';
import { Color, Container, Graphics, Point, Rectangle, Text } from 'pixi.js';
import { GraphicApp } from '../app';
import { OutOfBound } from '../utils';
import {
@ -18,41 +18,11 @@ export class ContextMenuPlugin {
constructor(app: GraphicApp) {
this.app = app;
}
/**
*
*/
addContextMenu(menu: ContextMenu) {
if (this.contextMenuMap.has(menu.menuName)) {
console.log(`已经存在name=${menu.menuName}的菜单`);
}
registerMenu(menu: ContextMenu) {
this.contextMenuMap.set(menu.menuName, menu);
menu.plugin = this;
}
/**
*
* @param menuName
* @param global
*/
openMenu(menuName: string, global: Point) {
const menu = this.contextMenuMap.get(menuName);
if (menu) {
this.open(menu, global);
} else {
console.warn(`不存在name=${menuName}的菜单`);
}
}
/**
*
* @param menuName
*/
closeMenu(menuName: string) {
const menu = this.contextMenuMap.get(menuName);
if (menu) {
this.close(menu);
} else {
console.warn(`不存在name=${menuName}的菜单`);
}
}
/**
*
@ -131,13 +101,13 @@ export class ContextMenuPlugin {
}
/**
*
* ,
*/
export class ContextMenu extends Container {
_plugin?: ContextMenuPlugin;
parentMenuItem?: ContextMenuItem;
openedSubMenu?: ContextMenu;
menuOptions: MenuCompletionOptions;
_plugin?: ContextMenuPlugin;
opened = false;
bg: Graphics;
title?: Text;
@ -149,20 +119,32 @@ export class ContextMenu extends Container {
constructor(menuOptions: MenuOptions, parentMenuItem?: ContextMenuItem) {
super();
this.menuOptions = Object.assign({}, DefaultWhiteMenuOptions, menuOptions);
this.name = this.menuOptions.name;
this.bg = new Graphics();
this.addChild(this.bg);
this.groups = [];
this.init();
this.parentMenuItem = parentMenuItem;
}
static init(options: MenuOptions): ContextMenu {
return new ContextMenu(options);
}
public get style(): MenuCompletionStyleOptions {
return this.menuOptions.style;
}
/**
*
*/
public get parentMenu(): ContextMenu | undefined {
return this.parentMenuItem?.menu;
}
/**
*
*/
public get rootMenu(): ContextMenu {
if (this.parentMenu) {
return this.parentMenu.rootMenu;
@ -226,7 +208,7 @@ export class ContextMenu extends Container {
const options = this.menuOptions;
let maxItemWidth = 0;
let borderHeight = 0;
options.groups.forEach(group => {
options.groups.forEach((group) => {
const menuGroup = new MenuGroup(this, group);
this.groups.push(menuGroup);
borderHeight += menuGroup.totalHeight;
@ -246,10 +228,10 @@ export class ContextMenu extends Container {
if (options.style.border) {
this.bg.lineStyle(
options.style.borderWidth,
utils.string2hex(options.style.borderColor)
new Color(options.style.borderColor)
);
}
this.bg.beginFill(utils.string2hex(options.style.backgroundColor));
this.bg.beginFill(new Color(options.style.backgroundColor));
if (options.style.borderRoundRadius > 0) {
this.bg.drawRoundedRect(
0,
@ -263,10 +245,7 @@ export class ContextMenu extends Container {
}
this.bg.endFill();
let groupHeight = 0;
this.bg.lineStyle(
splitLineWidth,
utils.string2hex(options.style.borderColor)
);
this.bg.lineStyle(splitLineWidth, new Color(options.style.borderColor));
for (let i = 0; i < this.groups.length; i++) {
const group = this.groups[i];
group.updateItemBox(maxItemWidth);
@ -281,7 +260,6 @@ export class ContextMenu extends Container {
groupHeight = splitLineY + splitLineWidth;
}
this.addChild(this.bg);
this.addChild(...this.groups);
this.interactive = true;
@ -293,12 +271,82 @@ export class ContextMenu extends Container {
});
}
initGroups() {
this.groups = [];
const options = this.menuOptions;
options.groups.forEach((group) => {
const menuGroup = new MenuGroup(this, group);
this.groups.push(menuGroup);
});
this.addChild(...this.groups);
}
initTitle() {
if (this.menuOptions.title) {
this.title = new Text(this.menuOptions.title, { align: 'left' });
}
}
updateBg() {
const options = this.menuOptions;
let maxItemWidth = 0;
let borderHeight = 0;
const splitLineWidth = 1;
this.groups.forEach((menuGroup) => {
borderHeight += menuGroup.totalHeight;
if (menuGroup.maxWidth > maxItemWidth) {
maxItemWidth = menuGroup.maxWidth;
}
});
const bgWidth = maxItemWidth + this.padding * 2;
const bgHeight =
borderHeight +
this.groups.length * this.padding * 2 +
(this.groups.length - 1) * splitLineWidth;
if (options.style.border) {
this.bg.lineStyle(
options.style.borderWidth,
new Color(options.style.borderColor)
);
}
this.bg.beginFill(new Color(options.style.backgroundColor));
if (options.style.borderRoundRadius > 0) {
this.bg.drawRoundedRect(
0,
0,
bgWidth,
bgHeight,
options.style.borderRoundRadius
);
} else {
this.bg.drawRect(0, 0, bgWidth, bgHeight);
}
this.bg.endFill();
let groupHeight = 0;
this.bg.lineStyle(splitLineWidth, new Color(options.style.borderColor));
for (let i = 0; i < this.groups.length; i++) {
const group = this.groups[i];
group.updateItemBox(maxItemWidth);
group.position.set(this.padding, groupHeight + this.padding);
if (i === this.groups.length - 1) {
// 最后一个
break;
}
const splitLineY = groupHeight + group.height + this.padding * 2;
this.bg.moveTo(0, splitLineY);
this.bg.lineTo(bgWidth, splitLineY);
groupHeight = splitLineY + splitLineWidth;
}
}
update() {
this.groups.forEach((group) => group.update());
this.updateBg();
}
public get menuName(): string {
return this.menuOptions.name;
}
@ -324,6 +372,7 @@ export class ContextMenu extends Container {
if (this.parentMenu) {
this.parentMenu.openSub(this, global);
} else {
this.update();
this.plugin.open(this, global);
}
}
@ -370,7 +419,7 @@ class MenuGroup extends Container {
super();
this.config = config;
this.menu = menu;
config.items.forEach(item => {
config.items.forEach((item) => {
this.items.push(new ContextMenuItem(menu, item));
});
for (let i = 0; i < this.items.length; i++) {
@ -392,12 +441,12 @@ class MenuGroup extends Container {
public get maxWidth(): number {
const maxNameWidth = this.items
.map(item => item.nameBounds.width)
.sort()
.map((item) => item.nameBounds.width)
.sort((a, b) => a - b)
.reverse()[0];
const maxShortcutWidth = this.items
.map(item => item.shortcutKeyBounds.width)
.sort()
.map((item) => item.shortcutKeyBounds.width)
.sort((a, b) => a - b)
.reverse()[0];
const maxWidth =
maxNameWidth +
@ -410,12 +459,18 @@ class MenuGroup extends Container {
public get totalHeight(): number {
let total = 0;
this.items.forEach(item => (total += item.totalHeight));
this.items.forEach((item) => (total += item.totalHeight));
return total;
}
update() {
this.items.forEach((item) => item.update());
}
updateItemBox(maxItemWidth: number) {
this.items.forEach(item => item.updateBox(maxItemWidth, item.totalHeight));
this.items.forEach((item) =>
item.updateBox(maxItemWidth, item.totalHeight)
);
}
}
@ -428,7 +483,7 @@ class ContextMenuItem extends Container {
/**
*
*/
nameText?: Text;
nameText: Text;
/**
*
*/
@ -446,7 +501,12 @@ class ContextMenuItem extends Container {
this.box = new Graphics();
this.addChild(this.box);
this.initNameText();
this.nameText = new Text(this.config.name, {
fontSize: this.fontSize,
fill: this.fontColor,
});
this.addChild(this.nameText);
this.initShortcutKeyText();
this.initSubMenu();
}
@ -553,15 +613,6 @@ class ContextMenuItem extends Container {
return this.style.fontColor;
}
initNameText(): Text {
this.nameText = new Text(this.config.name, {
fontSize: this.fontSize,
fill: this.fontColor,
});
this.addChild(this.nameText);
return this.nameText;
}
initShortcutKeyText(): Text | undefined {
if (this.config.shortcutKeys && this.config.shortcutKeys.length > 0) {
this.shortcutKeyText = new Text(this.config.shortcutKeys.join('+'), {
@ -603,7 +654,7 @@ class ContextMenuItem extends Container {
if (this.style && this.style.hoverColor) {
hoverColor = this.style.hoverColor;
}
box.beginFill(utils.string2hex(hoverColor));
box.beginFill(new Color(hoverColor));
if (style.borderRoundRadius > 0) {
box.drawRoundedRect(0, 0, width, height, style.borderRoundRadius);
} else {
@ -665,4 +716,26 @@ class ContextMenuItem extends Container {
}
});
}
update() {
this.nameText.text = this.config.name;
this.nameText.style.fontSize = this.fontSize;
this.nameText.style.fill = this.fontColor;
if (this.shortcutKeyText) {
if (this.config.shortcutKeys && this.config.shortcutKeys.length > 0) {
this.shortcutKeyText.text = this.config.shortcutKeys.join('+');
this.shortcutKeyText.style.fontSize = this.fontSize;
this.shortcutKeyText.style.fill = this.fontColor;
} else {
this.shortcutKeyText.visible = false;
}
} else {
this.initShortcutKeyText();
}
if (this.subMenu) {
this.subMenu.update();
}
}
}

View File

@ -81,6 +81,10 @@ export interface MenuItemOptions {
* ,
*/
disabled?: boolean;
/**
* ,
*/
visible?: boolean;
/**
*
*/
@ -89,10 +93,6 @@ export interface MenuItemOptions {
*
*/
onClick?: () => void;
// /**
// * 菜单项字体样式覆盖MenuOptions中的通用样式
// */
// style?: MenuItemStyle;
fontColor?: string;
/**
*
@ -109,10 +109,6 @@ export interface MenuItemStyle {
*
*/
fontColor?: string;
// /**
// * 字体对齐
// */
// align?: TextStyleAlign;
/**
* hover颜色
*/
@ -127,8 +123,13 @@ export interface MenuItemStyle {
padding: number[] | number; // 内边距
}
export interface MenuCompletionItemStyle extends MenuItemStyle {
/**
*
*/
fontColor: string;
// align: TextStyleAlign;
/**
*
*/
hoverColor: string;
/**
*
@ -157,7 +158,6 @@ export const DefaultWhiteStyleOptions: MenuCompletionStyleOptions = {
fontSize: 16,
fontColor: '#000000',
padding: [5, 25],
// align: 'left',
hoverColor: '#1E78DB',
disabledFontColor: '#9D9D9D',
},

View File

@ -79,18 +79,18 @@ export function recursiveFindChild(
return null;
}
export interface BezierPoints {
export interface BezierParam {
p1: IPointData;
p2: IPointData;
cp1: IPointData;
cp2: IPointData;
}
export function convertToBezierPoints(points: IPointData[]): BezierPoints[] {
if (points.length < 4 && points.length % 3 !== 1) {
export function convertToBezierParams(points: IPointData[]): BezierParam[] {
if (points.length < 4 || points.length % 3 !== 1) {
throw new Error(`bezierCurve 数据错误: ${points}`);
}
const bps: BezierPoints[] = [];
const bps: BezierParam[] = [];
for (let i = 0; i < points.length - 3; i += 3) {
const p1 = new Point(points[i].x, points[i].y);
const p2 = new Point(points[i + 3].x, points[i + 3].y);
@ -106,6 +106,69 @@ export function convertToBezierPoints(points: IPointData[]): BezierPoints[] {
return bps;
}
/**
* 线
* @param basePoints
* @param segmentsCount
* @returns
*/
export function calculateBezierPoints(
basePoints: IPointData[],
segmentsCount: number
): Point[] {
const bps = convertToBezierParams(basePoints);
const points: Point[] = [];
bps.forEach((bp) => {
points.push(
...calculateOneBezierPoints(bp.p1, bp.p2, bp.cp1, bp.cp2, segmentsCount)
);
});
return points;
}
/**
* 线
* @param basePoints
* @param segmentsCount
* @returns
*/
export function calculateOneBezierPoints(
p1: IPointData,
p2: IPointData,
cp1: IPointData,
cp2: IPointData,
segmentsCount: number
): Point[] {
const points: Point[] = [];
const fromX = p1.x;
const fromY = p1.y;
const n = segmentsCount;
let dt = 0;
let dt2 = 0;
let dt3 = 0;
let t2 = 0;
let t3 = 0;
const cpX = cp1.x;
const cpY = cp1.y;
const cpX2 = cp2.x;
const cpY2 = cp2.y;
const toX = p2.x;
const toY = p2.y;
points.push(new Point(p1.x, p1.y));
for (let i = 1, j = 0; i <= n; ++i) {
j = i / n;
dt = 1 - j;
dt2 = dt * dt;
dt3 = dt2 * dt;
t2 = j * j;
t3 = t2 * j;
const px = dt3 * fromX + 3 * dt2 * j * cpX + 3 * dt * t2 * cpX2 + t3 * toX;
const py = dt3 * fromY + 3 * dt2 * j * cpY + 3 * dt * t2 * cpY2 + t3 * toY;
points.push(new Point(px, py));
}
return points;
}
/**
*
*/
@ -367,7 +430,7 @@ export function calculateIntersectionPointOfCircleAndPoint(
*
* @param bp
* @param p
* @param distance p到基准点的距离
* @param distance p到基准点的距离,
* @returns
*/
export function calculateMirrorPoint(

View File

@ -25,13 +25,21 @@ onMounted(() => {
drawApp = new JlDrawApp(dom);
initDrawApp(drawApp);
loadDrawDatas(drawApp);
window.addEventListener('resize', onResize);
}
});
function onResize() {
if (drawApp) {
drawApp.onDomResize(document.body.clientWidth, document.body.clientHeight);
}
}
onUnmounted(() => {
if (drawApp) {
drawApp.destroy();
}
window.removeEventListener('resize', onResize);
document.body.style.overflow = 'auto';
});
</script>