zrender 的框架中管理元素:
接触最多的两个大元素类是Displayable 和 Group,两个元素都继承自基础Element类,而Element类继承了Animatable,Transformable和Eventful类。所以理论上Displayable 和 Group都原生支持鼠标事件,在测试中的确是这样没错。
// zr => zr对象
let rect = new zrender.Rect({
name:"example_rect",
shape:{-- some code here --},
onmousedown(){
console.log("mousedown in Rect")
}
});
let group = new zrender.Group({
position:[10,10]
onmousedown(){
console.log("mousedown in Group")
}
});
zr.add(group.add(rect));
// 在图像上点击之后会分别触发两次事件,事件的顺序和属性 z 的值有关,在上面的先触发。同时可以通过e.stop()实现冒泡阻止
但是Group所代表的组元素没有实体属性,其大小形状和包围盒只能由添加到内部的Displayable元素填充确定。在Group上绑定事件进行交互大部分情况下是没有意义的,会带来些不必要的问题。其作用主要是方便的对多个Displayable实例进行统一管理,比如说鼠标的拖动的实现,或者获取某个Displayable实例对其属性进行设置。
Group中有个特有的方法 childOfName(),由此可以获得对应的子元素,由此就可以对子元素进行设置。
group.childOfName("example_rect").attr({
shape:{-- some new code here --}
})
反过来也可以,Group内的Displayable元素可以通过自身的parent属性获取到Group
group == group.childOfName("example_rect").parent => true
这样,前置条件的准备完成了。我们来实现这样一个需求:有两个Displayable类的元素,比如说两个矩形框,我们需要拖动其中一个矩形框的时候,两个矩形框一起移动。虽然我们可以通过计算两个矩形框的位置差来分别获取两个矩形框的坐标并在拖动的过程中实时的达到需求。但是这并不方便,更何况若果矩形框不止两个呢?
比较好的方法是将两个矩形框放到一个gorup中,其中一个矩形框绑定拖动事件,不过最终拖动的是这个Group。下面是个示例
zr.add(
new zrender.Group({ // => 添加一个Group
id: "group",
position: [100, 100],
onmousedown() { console.log("down in Gorup") }
}
).add( // => Group添加第一个矩形框
new zrender.Rect({
shape: { r: 0, x: 0, y: 50, width: 100, height: 40 },
style: { fill: "red" }
})
).add( // => Group添加第二个矩形框
new zrender.Rect({
name: "title",
shape: { r: 5, x: 0, y: 0, width: 100, height: 40 },
style: { fill: "#ffffff" },
onmousedown(e) { // => 第二个矩形框绑定事件
let fix = []; // => fix 代表鼠标按下的位置和Group左上角的距离差
fix[0] = e.offsetX - this.parent.position[0];
fix[1] = e.offsetY - this.parent.position[1];
// 移动事件绑到zr全局对象上,这样可以避免鼠标快速移动后脱离group造成事件意外停止
zr.on("mousemove", (e) => { // 箭头函数的好处之一就是下面的this指向了我们的矩形框
this.parent.attr({
position: [e.offsetX - fix[0], e.offsetY - fix[1]]
})
})
this.onmouseup = function () { // 鼠标松开后接除捆绑在zr上的移动事件,以及自己的鼠标松开事件,还您一个蓝天。。
zr.off("mousemove")
this.off("mouseup")
}
},
})
)
)
整个过程中只需要稍加注意this的指向即可。
开发一个类似流程图形式的波形模拟器还有几个坑需要填,多个Displayable元素之间如何互相交互,如果构成流程,如果规范数据的格式等等。在之后的文章里我会同步开发进度一起总结。
2020/2/27
我需要进行一些改进,我发现了另一种拖动的方法。我们知道Displayable元素原生有draggable的拖动属性,当其值为true时可以拖动这个元素。通过检查拖动前后的元素,可以发现是通过设置这个元素的position属性实现的。
这样我们就得到另一种方法实现Group内部所有元素一起拖动了。还是上面的例子,我们用另一种方法实现:
zr.add(
new zrender.Group({ // => 添加一个Group
id: "group",
position: [100, 100],
onmousedown() { console.log("down in Gorup") }
}
).add( // => Group添加第一个矩形框
new zrender.Rect({
shape: { r: 0, x: 0, y: 50, width: 100, height: 40 },
style: { fill: "red" }
})
).add( // => Group添加第二个矩形框
new zrender.Rect({
draggable: true,
name: "title",
shape: { r: 5, x: 0, y: 0, width: 100, height: 40 },
style: { fill: "#ffffff" },
ondrag(e){
this.parent.attr({ // => 设置父group的position以实现移动
position:[
this.parent.position[0] + e.event.movementX,
this.parent.position[1] + e.event.movementY,
]
});
this.attr({ // => 设置自身的位置相对Group固定。
position:[0, 0]
})
}
})
)
)
代码更加的简洁了,而且目测没有牵扯到zr实体上?(如果在js里指定给zr.off('mousemove')则会失效,但是通过查看e.event发现这个可能是绑定在另一层div(查看渲染的html代码即可知)。