JointJS - Paper 事件和 Graph 事件
复习 Paper & Graph
Paper 是视图层(View)。数据和逻辑在 Graph 中操作,它是数据层(Model)。
在 Graph 中,有两个基本的图形——Element 和 Link。Element 是我们视图层所看到的可以控制、移动等操作的元素(椭圆、圆形、矩形等);Link 就是链接这些元素的线条。
JointJS 在 H5 中的效果全都是一组 g 标签,g 下面有 SVG 的基本图形:text、rect、circle 等。
tip:[start]
在 JointJS 中,我们可以把一个图形理解成一个 g 标签,或者说一个容器。容器下面许多的子图形,而我们 define 函数定义的就是一个容器,其下有许多的子图形。
一个图形是由一个 g 包裹起来的,其下面的图形是一个个子图形,一个图形的组成不单是一个矩形,也可以是矩形+圆形等各种复杂的图形组成。
tip:[end]
Paper 事件
Paper 事件是用户与视图层进行交互时的事件,如用户双击元素、右击元素,就会触发对应的 Paper 事件。更多的事件查阅文档:dia.Paper.events。比如,当用户双击视图上的某个元素时:
let rect = new joint.shapes.standard.Rectangle();
rect.position(100, 30);
rect.resize(100, 40);
rect.attr({
body: {
cursor: "pointer",
fill: "white",
stoke: "black"
},
label: {
text: "Element #1",
cursor: "pointer",
fill: "black"
}
});
rect.addTo(graph);
let rect2 = rect.clone();
rect2.translate(300, 0);
rect2.attr("label/text", "Element #2");
rect2.addTo(graph);
let link = new joint.shapes.standard.Link();
link.source(rect);
link.target(rect2);
link.attr({
line: {
stroke: "black"
}
});
link.labels([
{
markup: [
{
tagName: "rect",
selector: "body"
},
{
tagName: "text",
selector: "label"
}
],
attrs: {
label: {
cursor: "pointer",
text: "Link",
textAnchor: "middle",
textVerticalAnchor: "middle",
fontSize: 12,
fill: "black"
},
body: {
cursor: "pointer",
ref: "label",
refX: "-10%",
refY: "-10%",
refWidth: "120%",
refHeight: "120%",
fill: "white",
stroke: "black",
strokeWidth: 2
}
}
}
]);
link.addTo(graph);
let info = new joint.shapes.standard.Rectangle();
info.position(250, 70);
info.resize(100, 20);
info.attr({
body: {
visibility: "hidden",
cursor: "default",
fill: "white",
stoke: "black"
},
label: {
visibility: "hidden",
text: "Link clicked",
cursor: "default",
fill: "black",
fontSize: 12
}
});
info.addTo(graph);
lit:[paper.on("element:pointerdblclick", function (elementView) {
console.log("element", elementView);
resetAll(this);
let currentElement = elementView.model;
currentElement.attr("body/stroke", "orange");
});
function resetAll(paper) {
paper.drawBackground({
color: "white"
});
let elements = paper.model.getElements();
for (let i = 0, ii = elements.length; i < ii; i++) {
let currentElement = elements[i];
currentElement.attr("body/stroke", "black");
}
let links = paper.model.getLinks();
for (let j = 0, jj = links.length; j < jj; j++) {
let currentLink = links[j];
currentLink.attr("line/stroke", "black");
currentLink.label(0, {
attrs: {
body: {
stroke: "black"
}
}
});
}
}]:lit
Paper 事件是在视图层上的表现,用户很多的操作都是与视图层进行的,在逻辑层(Graph)中,一般涉及的是数据的改变,如坐标轴变化、属性变化等。下面就来学习 Graph 事件。
Graph 事件
Graph 事件一般都是由用户对视图层操作之后,逻辑层要响应视图层,并更新数据和处理业务并反馈给视图层。这期间,可能会改变位置、大小、属性以及转换,因此在 Graph 中也会有对应的事件,以便于我们自定义逻辑和业务。
// Element 变化时
graph.on('change:position', function(cell) {
let center = cell.getBBox().center();
let label = center.toString();
cell.attr('label/text', label);
});
// Link 变化时
graph.on('change:target', function(cell) {
let target = new g.Point(cell.target());
let label = target.toString();
cell.label(0, {
attrs: {
label: {
text: label
}
}
});
});
自定义子图形事件
我们可以给一个图形下的这些子图形自定义事件,前面提到 JointJS 中,一个图形是由一个 g 包裹起来的,其下面的图形是一个个子图形,一个图形的组成不单是一个矩形,也可以是矩形 + 圆形等各种复杂的图形组成。
定义图形
file:[自定义图形,设置默认(初始)参数]
// 命名空间是 examples.CustomElement,没有覆盖定义内置的 Rect
let CustomElement = joint.dia.Element.define('examples.CustomElement', {
attrs: {
// 属性 body,主要的图形,所以取名属性 body,也可以是其他,比如:main
body: {
// 使用 calc 函数,如果没有 ref,那就是根据这个图形的 g 标签进行计算,是一个相对于 g 计算的,前面提到,jointjs 中的一个图形就是一个 g 标签下面包含了许多子图形,比如:rect、text 这些 SVG 图形。
width: 'calc(w)',
// 相对于 g 的 h
height: 'calc(h)',
strokeWidth: 2,
stroke: 'black',
fill: 'white'
},
label: {
textVerticalAnchor: 'middle',
textAnchor: 'middle',
x: 'calc(0.5*w)',
y: 'calc(0.5*h)',
fontSize: 14,
fill: 'black'
},
button: {
cursor: 'pointer',
ref: 'buttonLabel',
width: 'calc(1.5*w)',
height: 'calc(1.5*h)',
x: 'calc(x-calc(0.25*w))',
y: 'calc(y-calc(0.25*h))'
},
// 点击 button 之后收起来的 button
buttonLabel: {
pointerEvents: 'none',
x: 'calc(w)',
y: 0,
textAnchor: 'middle',
textVerticalAnchor: 'middle'
}
}
}, {
// 对上面定义的属性,分别所对应的 SVG 图形
markup: [{
tagName: 'rect',
selector: 'body',
}, {
tagName: 'text',
selector: 'label'
}, {
tagName: 'rect',
selector: 'button'
}, {
tagName: 'text',
selector: 'buttonLabel'
}]
});
实例化图形
let element = new CustomElement();
// 初始坐标
element.position(250, 30);
// 初始高宽
element.resize(100, 40);
// 初始属性
element.attr({
label: {
pointerEvents: 'none',
visibility: 'visible',
text: 'Element'
},
body: {
cursor: 'default',
visibility: 'visible'
},
button: {
// 自定义事件名称,可以是其他的,比如:element:button:clickdown
lit:[event: 'element:button:pointerdown',]:lit
fill: 'orange',
stroke: 'black',
strokeWidth: 2
},
buttonLabel: {
text: '_', // fullwidth underscore
fill: 'black',
fontSize: 8,
fontWeight: 'bold'
}
});
element.addTo(graph);
自定义事件监听
通过自定义事件可以代替元素自己有的点击事件:
// 自定义事件名称,可以是其他的,比如:element:button:clickdown
lit:[paper.on("element:button:pointerdown", function (elementView, evt) {]:lit
evt.stopPropagation();
let model = elementView.model;
if (model.attr("body/visibility") === "visible") {
model.attr("body/visibility", "hidden");
model.attr("label/visibility", "hidden");
model.attr("buttonLabel/text", "+"); // 展开
} else {
model.attr("body/visibility", "visible");
model.attr("label/visibility", "visible");
model.attr("buttonLabel/text", "_"); // 收起
}
});
资源
更多的内容查看官方文档:Events - Tutorials。
更多的事件查阅文档:Element 的事件、Link 的事件、Graph 事件的种类。