Topology地址
Github地址: github.com/le5le-com/t…
官方文档 :www.yuque.com/alsmile/top…
起步
- 首先需要安装核心库(核心绘图引擎)
npm install @topology/core (注意查看topology版本更新内容)
- 初始化
<template>
<div class="tools">
<div v-for="(item, index) in tools" :key="index">
<div class="title">{{ item.group }}</div>
<div class="buttons">
<a
v-for="(btn, i) in item.children"
:key="i"
:title="btn.name"
:draggable="btn.data"
@dragstart="onDrag($event, btn)"
>
<img :src="btn.data.image" alt="" srcset="" v-if="btn.data.image" />
<i v-else :class="`iconfont ${btn.icon}`"></i>
</a>
</div>
</div>
</div>
<div id="topology-wrapper"></div>
<!-- 需要在template中先创建一个topology组件的容器(父节点) -->
</template>
<script>
// 在页面中引入Topology
import { Topology } from '@topology/core'
export default {
data(){
return {
topologyInstance:null
}
},
mounted(){
this.initTopology()
},
methods:{
initTopology(){
/**
* 生成一个topology实例,第一个参数为template中容器(父节点)的id,第
* 二个参数为topology的配置项,具体参数参考官方文档
*/
this.topologyInstance = new Topology('topology-wrapper',{})
},
// 拖拽事件
onDrag (event, node) {
event.dataTransfer.setData('Text', JSON.stringify(node.data))
},
// topology消息通信
onMessage (event, data) {
if (event === 'addNode' || event === 'addLine') {
console.log('onMessage', event, data)
}
// 右侧输入框编辑状态时点击编辑区域其他元素,onMessage执行后才执行onUpdateProps方法,通过
setTimeout让onUpdateProps先执行
setTimeout(() => {
switch (event) {
case 'node':
break
case 'addNode':
break
case 'line':
case 'addLine':
break
case 'multi':
break
case 'space':
break
case 'moveOut':
break
case 'moveNodes':
break
case 'resizeNodes':
break
case 'resize':
break
case 'scale':
break
case 'locked':
break
}
}, 0)
},
}
}
</script>
问题
- 基本上的绘制 都基于canvas 因此 官方文档没有提供的api 都可以去看下canvas提供的熟悉 去源码查下
- Node(节点) Line (线) Pen (画笔) 官方文档写了一句话 继承于画笔的成员变量这里省略(重要) 有些熟悉在节点和node找不到的去查看画笔
- Pen 的 type 属性 画笔类型:是节点还是连线 拖拽是设置name为line 拖拽时返回的还是node节点 记得设置type : 1
- 目前线不支持设置线性渐变但支持设置背景图。strokeImage属性 设置线的背景图
- 修改源码设置线的线性渐变
Line.prototype.drawBkLinearGradient = function (ctx) {
if (!this.gradientFromColor || !this.gradientToColor) {
return
}
var from = new Point(this.rect.x, this.rect.center.y)
var to = new Point(this.rect.ex, this.rect.center.y)
if (this.gradientAngle % 90 === 0 && this.gradientAngle % 180) {
if (this.gradientAngle % 270) {
from.x = this.rect.center.x
from.y = this.rect.y
to.x = this.rect.center.x
to.y = this.rect.ey
} else {
from.x = this.rect.center.x
from.y = this.rect.ey
to.x = this.rect.center.x
to.y = this.rect.y
}
} else if (this.gradientAngle) {
from.rotate(this.gradientAngle, this.rect.center)
to.rotate(this.gradientAngle, this.rect.center)
}
// contributor: https://github.com/sunnyguohua/topology
var grd = ctx.createLinearGradient( from.x, from.y, to.x, to.y )
grd.addColorStop(0, this.gradientFromColor)
grd.addColorStop(1, this.gradientToColor)
ctx.strokeStyle = grd
}
Line.prototype.drawBkRadialGradient = function (ctx) {
if (!this.gradientFromColor || !this.gradientToColor) {
return
}
var r = this.rect.width
if (r < this.rect.height) {
r = this.rect.height
}
r *= 0.5
var grd = ctx.createRadialGradient(
this.rect.center.x,
this.rect.center.y,
r * this.gradientRadius,
this.rect.center.x,
this.rect.center.y,
r
)
grd.addColorStop(0, this.gradientFromColor)
grd.addColorStop(1, this.gradientToColor)
ctx.strokeStyle = grd
}
draw 方法的时候去调用一下 也可以设置 选择线性渐变或者径向渐变
switch (this.bkType) {
case 1:
this.drawBkLinearGradient(ctx)
break
case 2:
this.drawBkRadialGradient(ctx)
break
}
1 - 线性渐变,配合设置下面属性:
gradientFromColor
gradientToColor
gradientAngle
2 - 径向渐变,配合设置下面属性
gradientFromColor
gradientToColor
gradientRadius
- 设置线的边缘lineCap 设置线宽的时候可以看到
round: 圆角
butt: 平直
square: 正方形
- 开启网格背景
grid boolean 是否显示背景网格
gridColor string 背景网格颜色
gridSize number 背景网格格子大小
rule boolean 是否显示背景标尺
rule Colorstring 标尺颜色
- canvas.delete(param?: string | Pen[], force?: boolean) 方法
为空时,删除选中元素,为string时,删除id或tag的元素为数组时,删除数组中的元素
params string 为string时,删除id或tag的元素为数组时,删除数组中的元素
force boolean 是否强制删除,否则locked下无法删除
节点添加子节点设置 rectInParent的bug
Node.prototype.scale = function (scale, center) {
if (!center) {
center = this.rect.center;
}
this['oldRect'] = this.rect.clone();
this.rect.x = center.x - (center.x - this.rect.x) * scale;
this.rect.y = center.y - (center.y - this.rect.y) * scale;
this.textOffsetX *= scale;
this.textOffsetY *= scale;
this.z *= scale;
this.rect.width *= scale;
this.rect.height *= scale;
if (this.name === 'rectangle') {
console.log(this.rect.width, this.rect.height, scale, '执行scale');
}
this.rect.ex = this.rect.x + this.rect.width;
this.rect.ey = this.rect.y + this.rect.height;
this.lineWidth *= scale;
if (this.imageWidth) {
this.imageWidth *= scale;
}
if (this.imageHeight) {
this.imageHeight *= scale;
}
this.lastImage = null;
this.fontSize *= scale;
this.iconSize *= scale;
if (typeof this.paddingLeft === 'number') {
this.paddingLeft *= scale;
}
if (typeof this.paddingTop === 'number') {
this.paddingTop *= scale;
}
if (typeof this.paddingRight === 'number') {
this.paddingRight *= scale;
}
if (typeof this.paddingBottom === 'number') {
this.paddingBottom *= scale;
}
if (this.rectInParent) {
if (typeof this.rectInParent.x === 'number') {
this.rectInParent.x *= scale;
}
if (typeof this.rectInParent.y === 'number') {
this.rectInParent.y *= scale;
}
if (typeof this.rectInParent.width === 'number') {
console.log(this.rectInParent.width, scale, '老值');
## bug rectInParent 初始值不能 * scale 直接取node.rect / scale 的值 重新初始化
## 重新 / scale 是因为 初始化时会根据rectInParent重新 * scale
// this.rectInParent.width *= scale; (源代码)
this.rectInParent.width = this.rect.width / scale;
console.log('新值', this.rectInParent.width, scale);
}
if (typeof this.rectInParent.height === 'number') {
// this.rectInParent.height *= scale;
this.rectInParent.height = this.rect.height / scale;
}
if (typeof this.rectInParent.marginLeft === 'number') {
this.rectInParent.marginLeft *= scale;
}
if (typeof this.rectInParent.marginTop === 'number') {
this.rectInParent.marginTop *= scale;
}
if (typeof this.rectInParent.marginRight === 'number') {
this.rectInParent.marginRight *= scale;
}
if (typeof this.rectInParent.marginBottom === 'number') {
this.rectInParent.marginBottom *= scale;
}
}
this.rect.calcCenter();
if (this.animateFrames && this.animateFrames.length) {
for (var _i = 0, _a = this.animateFrames; _i < _a.length; _i++) {
var item = _a[_i];
if (item.initState) {
if (!item.initState.scale) {
item.initState = new Node(item.initState);
}
item.initState.scale(scale, center);
}
if (item.state) {
if (!item.state.scale) {
item.state = new Node(item.state);
}
item.state.scale(scale, center);
}
if (item.initState && item.state) {
item.state.fontSize = item.initState.fontSize;
}
}
}
this.scalePoints(scale, scale);
this.elementRendered = false;
this.init();
if (this.children) {
for (var _b = 0, _c = this.children; _b < _c.length; _b++) {
var item = _c[_b];
item.scale(scale, center);
}
}
if (this.animateReady && this.animateReady.scale) {
this.animateReady.scale(scale, center);
}
};