这一章实现的连接线,目前仅支持直线连接,为了能够不影响原有的其它功能,尝试了2、3个实现思路,最终实测这个实现方式目前来说最为合适了。
请大家动动小手,给我一个免费的 Star 吧~
大家如果发现了 Bug,欢迎来提 Issue 哟~
相关定义
- 连接点
记录了连接点相关信息,并不作为素材而存在,仅记录信息,即导出导入的时候,并不会出现所谓的连接点节点。
它存放在节点身上,因此导出、导入自然而然就可以持久化了。
src/Render/draws/LinkDraw.ts
// 连接点
export interface LinkDrawPoint {
id: string
groupId: string
visible: boolean
pairs: LinkDrawPair[]
x: number
y: number
}
- 连接对
一个连接点,记录从该点出发的多条连接线信息,作为连接对信息存在。
src/Render/draws/LinkDraw.ts
// 连接对
export interface LinkDrawPair {
id: string
from: {
groupId: string
pointId: string
}
to: {
groupId: string
pointId: string
}
}
- 连接点(锚点)
它是拖入素材的时候生成的真实节点,归属于所在的节点中,存在却不可见,关键作用是同步连接点真实坐标,尤其是节点发生 transform 时候,必须依赖它获得 transform 后连接点变化。
src/Render/handlers/DragOutsideHandlers.ts
// 略
drop: (e: GlobalEventHandlersEventMap['drop']) => {
// 略
const points = [
// 左
{
x: 0, y: group.height() / 2 },
// 右
{
x: group.width(),
y: group.height() / 2
},
// 上
{
x: group.width() / 2, y: 0 },
// 下
{
x: group.width() / 2,
y: group.height()
}
]
// 连接点信息
group.setAttrs({
points: points.map(
(o) =>
({
...o,
id: nanoid(),
groupId: group.id(),
visible: true,
pairs: []
}) as LinkDrawPoint
)
})
// 连接点(锚点)
for (const point of group.getAttr('points') ?? []) {
group.add(
new Konva.Circle({
name: 'link-anchor',
id: point.id,
x: point.x,
y: point.y,
radius: this.render.toStageValue(1),
stroke: 'rgba(0,0,255,1)',
strokeWidth: this.render.toStageValue(2),
visible: false
})
)
}
group.on('mouseenter', () => {
// 显示 连接点
this.render.linkTool.pointsVisible(true, group)
})
// hover 框(多选时才显示)
group.add(
new Konva.Rect({
id: 'hoverRect',
width: image.width(),
height: image.height(),
fill: 'rgba(0,255,0,0.3)',
visible: false
})
)
group.on('mouseleave', () => {
// 隐藏 连接点
this.render.linkTool.pointsVisible(false, group)
// 隐藏 hover 框
group.findOne('#hoverRect')?.visible(false)
})
// 略
}
// 略
- 连接线
根据连接点信息,绘制的线条,也不作为素材而存在,导出导入的时候,也不会出现所谓的连接点节点。不过,在导出图片、SVG和用于预览框的时候,会直接利用线条节点导出、显示。
src/Render/tools/ImportExportTool.ts
// 略
/**
* 获得显示内容
* @param withLink 是否包含线条
* @returns
*/
getView(withLink: boolean = false) {
// 复制画布
const copy = this.render.stage.clone()
// 提取 main layer 备用
const main = copy.find('#main')[0] as Konva.Layer
const cover = copy.find('#cover')[0] as Konva.Layer
// 暂时清空所有 layer
copy.removeChildren()
// 提取节点
let nodes = main.getChildren((node) => {
return !this.render.ignore(node)
})
if (withLink) {
nodes = nodes.concat(
cover.getChildren((node) => {
return node.name() === Draws.LinkDraw.name
})
)
}
// 略
}
// 略
src/Render/draws/PreviewDraw.ts
override draw() {
// 略
const main = this.render.stage.find('#main')[0] as Konva.Layer
const cover = this.render.stage.find('#cover')[0] as Konva.Layer
// 提取节点
const nodes = [
...main.getChildren((node) => {
return !this.render.ignore(node)
}),
// 补充连线
...cover.getChildren((node) => {
return node.name() === Draws.LinkDraw.name
})
]
// 略
}
- 连接线(临时)
起点鼠标按下 -> 拖动显示线条 -> 终点鼠标释放 -> 产生连接信息 LinkDrawPoint. LinkDrawPair
// 连接线(临时)
export interface LinkDrawState {
linkingLine: {
group: Konva.Group
circle: Konva.Circle
line: Konva.Line
} | null
}
代码文件
新增几个关键的代码文件:
src/Render/draws/LinkDraw.ts
根据 连接点.链接对 绘制 连接点、连接线,及其相关的事件处理
它的绘制顺序,应该放在绘制 比例尺、预览框之前。
src/Render/handlers/LinkHandlers.ts
根据 连接线(临时)信息,绘制/移除 连接线(临时)
src/Render/tools/LinkTool.ts
移除连接线,控制 连接点 的显示/隐藏
移除连接线,实际上就是移除其 连接对 信息
// 略
export class LinkTool {
// 略