jointJS是一个基于svg的图形化工具库,拥有强大的功能,但是也有2个缺点
1.没有中文的官方文档
2.很多功能只存在于其收费版Rappid中,价格不菲
动态修改元素大小这是一个非常有用功能,Rappid中有这个功能,但也些不如人意的地方,最后决定自己写一个
基本思路如下
1.创建一个自定义元素,可以拖动大小和位置
2.把这个自定义元素绑定到其他元素,让其他元素的大小和位置与自定义元素同步
效果图如下
class ResizeControl{
parentBbox=null;
paper=null;
resizeElement=null;
bindElement=null;
Resize = joint.dia.Element.define('custom.Resize', {
markup: [{
tagName: 'rect',
selector: 'body'
},{
tagName: 'rect',
selector: 'topLeft'
},{
tagName: 'rect',
selector: 'topRight'
},{
tagName: 'rect',
selector: 'bottomLeft'
},{
tagName: 'rect',
selector: 'bottomRight'
}],
attrs: {
body: {
opacity:'0',
stroke: '#000',
refWidth: '100%',
refHeight: '100%'
},
topLeft: {
fill: '#fff',
stroke: '#000',
x:-3,
y:-3,
width: 7,
height: 7,
event: 'resize:topLeft',
cursor: 'nwse-resize'
},
topRight: {
fill: '#fff',
stroke: '#000',
refX: '100%',
x:-3,
y:-3,
width: 7,
height: 7,
event: 'resize:topRight',
cursor: 'nesw-resize'
},
bottomLeft: {
fill: '#fff',
stroke: '#000',
refY: '100%',
x:-3,
y:-3,
width: 7,
height: 7,
event: 'resize:bottomLeft',
cursor: 'nesw-resize'
},
bottomRight: {
fill: '#fff',
stroke: '#000',
refX: '100%',
refY: '100%',
x:-3,
y:-3,
width: 7,
height: 7,
event: 'resize:bottomRight',
cursor: 'nwse-resize'
},
}
});
constructor(paper){
this.paper=paper;
var that=this;
//事件响应
paper.on('resize:topLeft', function(elementView, evt) {
evt.stopPropagation();
that.drag(evt.offsetX,evt.offsetY,1,1,-1,-1);
});
paper.on('resize:bottomRight', function(elementView, evt) {
evt.stopPropagation();
that.drag(evt.offsetX,evt.offsetY,0,0,1,1);
});
paper.on('resize:topRight', function(elementView, evt) {
evt.stopPropagation();
that.drag(evt.offsetX,evt.offsetY,0,1,1,-1);
});
paper.on('resize:bottomLeft', function(elementView, evt) {
evt.stopPropagation();
that.drag(evt.offsetX,evt.offsetY,1,0,-1,1);
});
paper.on('element:pointerclick', function(cellView, evt,x,y) {
evt.stopPropagation();
that.bind(cellView.model);
});
paper.on('blank:pointerclick', function(evt,x,y) {
that.unbind();
});
paper.on('element:mouseenter', function(cellView,evt) {
cellView.model.attr('body/fill-opacity','0.1');
});
paper.on('element:mouseleave', function(cellView,evt) {
cellView.model.attr('body/fill-opacity','1');
});
this.parentBbox = g.Rect(0,0,paper.options.width,paper.options.height);
paper.model.on('change:position', function(cell) {
//边界检查,确保元素不被移动到边界外
var cellBbox = cell.getBBox();
if (that.parentBbox.containsPoint(cellBbox.origin()) &&
that.parentBbox.containsPoint(cellBbox.topRight()) &&
that.parentBbox.containsPoint(cellBbox.corner()) &&
that.parentBbox.containsPoint(cellBbox.bottomLeft())) {
// All the four corners of the child are inside
// the parent area.
return;
}
// Revert the child position.
cell.set('position', cell.previous('position'));
});
}
//绑定元素
bind(element){
//如果点击自己,则返回
if(element.attributes.type=='custom.Resize') return;
//释放上一个绑定
this.unbind();
//绑定元素
this.bindElement=element;
var that=this;
this.resizeElement = (new this.Resize())
.size(element.attributes.size.width,element.attributes.size.height)
.position(element.attributes.position.x,element.attributes.position.y)
.addTo(this.paper.model);
//让绑定元素位置和大小同步
this.resizeElement.on('change:position', function(element, position) {
that.bindElement.position(position.x,position.y);
});
this.resizeElement.on('change:size', function(element, size) {
that.bindElement.resize(size.width,size.height);
});
}
//拖动元素尺寸
drag(startX,startY,xDir,yDir,wDir,hDir){
var that=this;
//记录初始坐标
var elementX=this.resizeElement.attributes.position.x;
var elementY=this.resizeElement.attributes.position.y;
const MIN_SIZE=15;//最小尺寸
var elementWidth=this.resizeElement.attributes.size.width;
var elementHeight=this.resizeElement.attributes.size.height;
$(this.paper.el).bind('mousemove',function(event) {
//计算移动量
var dx=event.offsetX - startX;
var dy=event.offsetY - startY
//计算新坐标
var newX=elementX + dx*xDir;
var newY=elementY + dy*yDir;
newX=(elementX+elementWidth)-newX>=MIN_SIZE?newX:(elementX+elementWidth)-MIN_SIZE,
newY=(elementY+elementHeight)-newY>=MIN_SIZE?newY:(elementY+elementHeight)-MIN_SIZE;
//计算新尺寸
var newWidth=elementWidth + dx*wDir;
var newHeight=elementHeight + dy*hDir;
newWidth=newWidth>=MIN_SIZE?newWidth:MIN_SIZE,
newHeight=newHeight>=MIN_SIZE?newHeight:MIN_SIZE;
//移动元素
that.resizeElement.position(newX,newY);
that.resizeElement.resize(newWidth,newHeight);
});
$("body").bind('mouseup',function(event) {
$(that.paper.el).unbind('mousemove');
$("body").unbind('mouseup');
});
}
//释放元素
unbind(){
if(this.resizeElement){
this.resizeElement.remove();
this.resizeElement=null;
}
}
}
使用方法
var resizeControl=new ResizeControl(paper);
下面是完整的例子,复制到html里面就可以使用