Skip to content

Commit

Permalink
v0.0.8
Browse files Browse the repository at this point in the history
1. 新增分组图层设置面板
2. 新增图层列表支持右键菜单
3. 优化蓝图性能,
4. 优化图层列表操作体验
5. 优化画布组件拖拽体验,支持shift固定方向拖拽
6. 优化代码结构与命名 (broken change)
7. 修复撤销与重做相关bug
8. 修复快捷键相关bug
9. 修复蓝图线条、节点相关bug
  • Loading branch information
xiaopujun committed Dec 2, 2023
1 parent 3dcd80a commit 1777529
Show file tree
Hide file tree
Showing 171 changed files with 1,503 additions and 1,430 deletions.
30 changes: 18 additions & 12 deletions src/blueprint/BPCanvas.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React, {useEffect} from "react";
import LineLayer from "./line/LineLayer";
import {NodeLayer} from "./node/NodeLayer";
import {reRenderLine} from "./drag/BPMovable";
import bpStore from "./store/BPStore";
import {reRenderAllLine} from "./drag/BPMovable";
import bpStore, {IBPLine} from "./store/BPStore";
import CanvasUtil from "./util/CanvasUtil";
import {BPLineType} from "./BPTypes";

/**
* todo 该方法要做性能优化,考虑防抖避免频繁采样和线段重复绘制,用算法做好碰撞检测
Expand All @@ -19,29 +18,30 @@ const lineSegmentCollisions = (event: MouseEvent) => {
selectedLines.forEach((line) => {
line.lineWidth = 1;
line.color = '#a2a2a2';
//todo 需要做好性能优化,非必要的线可以不用重复渲染
reRenderLine();
reRenderAllLine();
CanvasUtil.updSegmentSamplingPoint();
});
setSelectedLines([]);
}
if (!shiftKey) return;
const {bpLines, canvasOffset} = bpStore;
const targetLines: BPLineType[] = [];
const targetLines: IBPLine[] = [];
const mousePoint = {x: clientX - canvasOffset.x, y: clientY - canvasOffset.y};
Object.values(bpLines).forEach((line) => {
CanvasUtil.isMouseInRectangle(mousePoint, line.startPoint!, line.endPoint!) && targetLines.push(line);
});
if (targetLines.length === 0) return;
//遍历有效范围内的线条是否命中
const hitLines: BPLineType[] = [];
const hitLines: IBPLine[] = [];
for (let i = 0; i < targetLines.length; i++) {
const targetLine = targetLines[i];
const {samplePoints} = targetLine!;
if (CanvasUtil.isMouseOnLine(mousePoint, samplePoints!, 5)) {
targetLine.lineWidth = 2;
targetLine.color = '#1ad6ff';
targetLine.color = '#d9d9d9';
hitLines.push(targetLine);
CanvasUtil.drawBezierCurves(downCtx!, targetLine);
CanvasUtil.drawBezierCurves(downCtx!, [targetLine]);
CanvasUtil.updSegmentSamplingPoint();
break;
}
}
Expand All @@ -52,14 +52,20 @@ export const BPCanvas: React.FC = () => {
useEffect(() => {
//加载完毕后绘制链接线(由于节点组件的创建与渲染都是异步的,需要等节点的锚点都渲染完毕后才能确定连线的位置,因此暂时使用异步延时连线的渲染时机)
// todo 后续要调整为更精确的时机
setTimeout(() => {
reRenderLine();
const renderLineTimer = setTimeout(() => {
reRenderAllLine();
CanvasUtil.updSegmentSamplingPoint();
clearTimeout(renderLineTimer);
}, 50);
const {nodeContainerRef} = bpStore;
nodeContainerRef?.addEventListener('click', lineSegmentCollisions);
}, [])
return (
<div className={'blue-print'} id={'blue-print'} style={{overflow: "hidden"}}>
<div className={'blue-print-canvas'} style={{
overflow: "hidden",
width: window.innerWidth - 670,
height: window.innerHeight - 75,
}}>
<LineLayer/>
<NodeLayer/>
</div>
Expand Down
18 changes: 0 additions & 18 deletions src/blueprint/BPTypes.ts

This file was deleted.

35 changes: 18 additions & 17 deletions src/blueprint/drag/BPMovable.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import React, {useEffect} from "react";
import Moveable, {OnDrag, OnDragEnd, OnDragStart} from "react-moveable";
import bpStore from "../store/BPStore";
import bpStore, {IBPLine, IPoint} from "../store/BPStore";
import {observer} from "mobx-react";
import CanvasUtil from "../util/CanvasUtil";
import {BPLineType, PointType} from "../BPTypes";

export interface BPMovableProps {
children?: React.ReactNode;
}

export const reRenderLine = () => {
const {bpLines, downCtx, canvasOffset} = bpStore;
export const reRenderAllLine = () => {
const {bpLines} = bpStore;
reRenderLine(Object.values(bpLines));
}

export const reRenderLine = (lines: IBPLine[]) => {
const {downCtx, canvasOffset, nodeContainerRef} = bpStore;
const {width: canvasW, height: canvasH} = nodeContainerRef?.getBoundingClientRect()!;
//更新每条线的起始点和终点
Object.values(bpLines).forEach((line: BPLineType) => {
lines.forEach((line: IBPLine) => {
//重新设置连线的起始点和终点
const {startAnchorId, endAnchorId} = line;
const startDom = document.getElementById(startAnchorId!);
Expand All @@ -32,19 +37,14 @@ export const reRenderLine = () => {
const controlPoi = CanvasUtil.calculateControlPoint(line.startPoint, line.endPoint);
line.firstCP = controlPoi.firstCP;
line.secondCP = controlPoi.secondCP;
//重新计算采样点
const {startPoint, firstCP, secondCP, endPoint} = line;
line.samplePoints = CanvasUtil.sampleBezierCurve(startPoint!, firstCP!, secondCP!, endPoint, 20);
});

//重新绘制连线
downCtx!.clearRect(0, 0, 10000, 10000);
Object.values(bpLines).forEach((line: BPLineType) => {
CanvasUtil.drawBezierCurves(downCtx!, line);
})
downCtx!.clearRect(0, 0, canvasW, canvasH);
CanvasUtil.drawBezierCurves(downCtx!, lines);
}

export const updNodeAndLinePos = (nodeId: string, position: PointType) => {
export const updNodeAndLinePos = (nodeId: string, position: IPoint) => {
const {
bpNodeControllerInsMap, bpAPLineMap, bpLines,
updLinePos, updBpNodeLayout, canvasOffset
Expand Down Expand Up @@ -100,8 +100,7 @@ export const BPMovable = observer((props: BPMovableProps) => {
const onDrag = (e: OnDrag) => {
const {target, beforeTranslate} = e;
target.style.transform = `translate(${beforeTranslate[0]}px, ${beforeTranslate[1]}px)`;
//重绘连线
reRenderLine();
reRenderAllLine();
}

const onDragStart = (e: OnDragStart) => {
Expand All @@ -118,12 +117,13 @@ export const BPMovable = observer((props: BPMovableProps) => {
const {beforeTranslate} = lastEvent;
const nodeId = target.id.split(':')[1];
updNodeAndLinePos(nodeId, {x: beforeTranslate[0], y: beforeTranslate[1]});
CanvasUtil.updSegmentSamplingPoint();
}

const onDragGroup = (e: any) => {
e.events.forEach((ev: any) => ev.target.style.transform = ev.transform);
//重绘连线
reRenderLine();
reRenderAllLine();
}

const onDragGroupEnd = (e: any) => {
Expand All @@ -135,6 +135,7 @@ export const BPMovable = observer((props: BPMovableProps) => {
updNodeAndLinePos(nodeId, {x: beforeTranslate[0], y: beforeTranslate[1]});
}
})
CanvasUtil.updSegmentSamplingPoint();
}

const {selectedNodes} = bpStore;
Expand All @@ -145,7 +146,7 @@ export const BPMovable = observer((props: BPMovableProps) => {
target={selectedNodes}
draggable={true}
origin={false}
hideDefaultLines={false}
hideDefaultLines={true}
onDragStart={onDragStart}
onDrag={onDrag}
onDragEnd={onDragEnd}
Expand Down
2 changes: 1 addition & 1 deletion src/blueprint/drag/BPSelectable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const BPSelectable: React.FC<BPSelectableProps> = (props) => {
<>
{children}
<Selecto ref={selectorRef}
dragContainer={".blue-print"}
dragContainer={".blue-print-canvas"}
selectableTargets={[".bp-node-container"]}
hitRate={0}
ratio={0}
Expand Down
3 changes: 1 addition & 2 deletions src/blueprint/left/BPLeft.less
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
.bp-left {
display: flex;
padding: 3px;
height: 100%;
background-color: #1a1a1a;
border-right: 1px solid #2e2e2e;

.bp-node-sort-list {
height: 100%;
width: 70px;
border-right: 1px solid #2e2e2e;

.bp-left-item:hover {
background-color: #3d3d3d;
Expand Down Expand Up @@ -40,7 +40,6 @@
background-color: #1a1a1a;
z-index: 1;
font-size: 12px;
border-right: 1px solid #2e2e2e;

.bp-node-list-header {
width: 100%;
Expand Down
12 changes: 9 additions & 3 deletions src/blueprint/left/BPLeft.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,18 @@ const dragover = (event: any) => {
event.preventDefault(); // 阻止默认行为以允许拖放
}
//释放拖拽元素
const drop = (event: any) => {
const drop = (event: DragEvent) => {
event.preventDefault();
let nodeId = (event as any).dataTransfer.getData('nodeId');
const type = (event as any).dataTransfer.getData('type');
const {bpDragContentRef, canvasScale} = bpStore;
const contentPos = bpDragContentRef?.getBoundingClientRect();
//获取鼠标位置
const position = {x: event.layerX, y: event.layerY};
const position = {
x: (event.clientX - (contentPos?.x || 0)) / canvasScale,
y: (event.clientY - (contentPos?.y || 0)) / canvasScale
};
console.log('position', position)
if (type === 'layer-node') {
const {setUsedLayerNodes} = bpLeftStore;
setUsedLayerNodes(nodeId, true);
Expand All @@ -96,7 +102,7 @@ export const BPNodeList = observer(() => {
const NodeList = nodeListMapping[activeMenu];

useEffect(() => {
const dropContainer = document.getElementById("blue-print");
const dropContainer = document.getElementById("bp-ds-container");
const dragElements = document.getElementsByClassName("bp-node-list-item");
Array.from(dragElements).forEach((element) => {
element.removeEventListener('dragstart', (event) => dragStart(event, element));
Expand Down
49 changes: 25 additions & 24 deletions src/blueprint/line/LineLayer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React from 'react';
import React, {CSSProperties} from 'react';
import './LineLayer.less';
import CanvasUtil from "../util/CanvasUtil";
import bpStore from "../store/BPStore";
import bpStore, {IBPLine} from "../store/BPStore";
import {AnchorPointType} from "../node/core/AbstractBPNodeController";
import {BPLineType} from "../BPTypes";
import IdGenerate from "../../utils/IdGenerate";

class LineLayer extends React.Component {
Expand All @@ -13,7 +12,7 @@ class LineLayer extends React.Component {
//下层
downLayer: HTMLCanvasElement | null = null;

currentLine: BPLineType = {
currentLine: IBPLine = {
color: "#c0c0c0",
lineWidth: 1,
lineDash: [10, 10],
Expand All @@ -36,8 +35,8 @@ class LineLayer extends React.Component {
const {target} = e;
if (!target || !(target as HTMLElement).classList.contains('ap-circle')) return;
const pointDom = e.target as HTMLElement;
const pointInfoArr = pointDom.id.split("_");
if (pointInfoArr && pointInfoArr.length === 4 && pointInfoArr[3] === AnchorPointType.INPUT.toString())
const pointInfoArr = pointDom.id.split(":");
if (pointInfoArr && pointInfoArr.length === 3 && pointInfoArr[2] === AnchorPointType.INPUT.toString())
return;

//设置起始点坐标
Expand All @@ -47,25 +46,30 @@ class LineLayer extends React.Component {
this.keyDown = true;
});

//todo 待处理,蓝图的点击事件不要绑定在document上
document.addEventListener('mouseup', (e) => {
if (!this.keyMove || !e.target || !(e.target as HTMLElement).classList.contains('ap-circle')) {
const {nodeContainerRef, upCtx} = bpStore;
const {width: canvasW, height: canvasH} = nodeContainerRef?.getBoundingClientRect()!;
const endElem = e.target as HTMLElement;
if (!this.keyMove || !endElem || !endElem.classList.contains('ap-circle')
|| endElem.id?.split(":")[2] !== AnchorPointType.INPUT.toString()) {
//清空画布
bpStore.upCtx?.clearRect(0, 0, 10000, 10000);
upCtx?.clearRect(0, 0, canvasW, canvasH);
this.keyDown = false;
return;
}
this.keyMove = false;
this.keyDown = false;
bpStore.upCtx!.clearRect(0, 0, 10000, 10000)
bpStore.upCtx!.clearRect(0, 0, canvasW, canvasH)
//在下层绘制当前操作的线条
this.currentLine.id = IdGenerate.generateId();
this.currentLine.lineDash = [];
this.currentLine.lineWidth = 1;
this.currentLine.color = "#a2a2a2";
this.currentLine.endAnchorId = (e!.target as HTMLElement).id;
const {x, y, width, height} = (e.target as HTMLElement).getBoundingClientRect();
this.currentLine.endPoint = {x: x + width / 2 - canvasOffset.x, y: y + height / 2 - canvasOffset.y}
CanvasUtil.drawBezierCurves(bpStore.downCtx!, this.currentLine)
const {x, y, width: apw, height: aph} = (e.target as HTMLElement)?.getBoundingClientRect();
this.currentLine.endPoint = {x: x + apw / 2 - canvasOffset.x, y: y + aph / 2 - canvasOffset.y}
CanvasUtil.drawBezierCurves(bpStore.downCtx!, [this.currentLine])
//计算线条的采样点,用于计算线条是否被选中
const {
id, startPoint, endPoint, firstCP, secondCP, lineDash,
Expand Down Expand Up @@ -96,39 +100,36 @@ class LineLayer extends React.Component {
if (!this.keyDown) return;
this.keyMove = true;
const {startPoint, endPoint} = this.currentLine;
const {canvasOffset} = bpStore;
const {nodeContainerRef, canvasOffset} = bpStore;
const {width: canvasW, height: canvasH} = nodeContainerRef?.getBoundingClientRect()!;
//设置鼠标坐标
this.currentLine.endPoint = {x: e.clientX - canvasOffset.x, y: e.clientY - canvasOffset.y}

const contPoi = CanvasUtil.calculateControlPoint(startPoint!, endPoint!)
this.currentLine.firstCP = contPoi.firstCP
this.currentLine.secondCP = contPoi.secondCP
//清空画布
bpStore.upCtx!.clearRect(0, 0, 10000, 10000)
CanvasUtil.drawBezierCurves(bpStore.upCtx!, {
bpStore.upCtx!.clearRect(0, 0, canvasW, canvasH)
CanvasUtil.drawBezierCurves(bpStore.upCtx!, [{
color: "#c0c0c0",
lineWidth: 1,
lineDash: [10, 10],
startPoint: this.currentLine.startPoint,
endPoint: this.currentLine.endPoint,
firstCP: this.currentLine.firstCP,
secondCP: this.currentLine.secondCP
})
}])
});

}

render() {
const width = window.innerWidth - 670, height = window.innerHeight - 75;
const _canvasStyle: CSSProperties = {position: "inherit", top: 0, left: 0};
return (
<div style={{position: "absolute"}}>
<canvas style={{position: "inherit", top: 0, left: 0}}
width={window.innerWidth - 670}
height={window.innerHeight - 75}
ref={ref => this.downLayer = ref}/>
<canvas style={{position: "inherit", top: 0, left: 0}}
width={window.innerWidth - 670}
height={window.innerHeight - 75}
ref={ref => this.upLayer = ref}/>
<canvas style={_canvasStyle} width={width} height={height} ref={ref => this.downLayer = ref}/>
<canvas style={_canvasStyle} width={width} height={height} ref={ref => this.upLayer = ref}/>
</div>
)
}
Expand Down
Loading

0 comments on commit 1777529

Please sign in to comment.