先介绍各个js负责功能模块
svg.js —— 图形绘制、图形层次结构
svg.select.js(svg.select.css) —— 图形选择功能+图形选择样式
svg.resize.js —— 图形拉伸、收缩和旋转
svg.draggable.js ——图形拖动功能
请注意svg的版本问题,由于svg.select.js与3.0以上版本的svg.js暂时不兼容,一旦使用svg.select.js里的selectize方法便会抛出this.parents.set…错误,所以本文采用的svg版本是2.7
首先对网页页面的功能区域进行划分:
左侧:作为工具选择栏,基础绘图功能:直线、折线、矩形、圆形、文字和图片
上侧:作为菜单栏
中部:作为绘图区域(draw-workarea)
右侧:作为图形属性显示窗口(properties)
下侧:操作提示框
// 先导入select.js的样式文件
<link href="${base}static/css/svg.select.min.css" rel="stylesheet" type="text/css"/>
// 引用js文件
<script src="jquery-1.11.1.min.js"></script>
<script src="svg.min.js"></script>
<script src="svg.select.min.js"></script>
<script src="svg.resize.min.js"></script>
<script src="svg.draggable.js"></script>
// 自定义js文件
<script src="main.js"></script>
<div class="easyui-layout" fit="true">
<div data-options="region:'north'" style="height:32px">
<div style="line-height:30px;padding:0 10px">
<a href="javascript:void(0)" class="easyui-linkbutton" id="pickProject" data-options="iconCls:'icon-box-project',plain:true" >选项1</a>
</div>
</div>
<!-- 左侧工具栏区域-->
<div data-options="region:'west'" style="width:160px;">
<div class="box-menu">
<dl id="menu1">
<dt>栏目1</dt>
</dl>
<dl id="menu2">
<dt>栏目2</dt>
</dl>
<dl id="menu3">
<dt>栏目3</dt>
</dl>
</div>
</div>
<!-- 画布工作区域-->
<div data-options="region:'center'" style="background:#eeeeee">
<div class="box-workarea" id="box-workarea">
</div>
</div>
<!-- 消息区域-->
<div data-options="region:'south'" style="height:26px;">
<div id="currentProjectMessage"></div>
<div class="work-message" id="workMessage"><span>暂无消息</span></div>
</div>
</div>
在js文件中,创建svg将其命名为svgroot,高和宽我们都设置为100%
var svgRoot = SVG('box-workarea').size('100%','100%').id('svgroot')
在3.0版本svg的创建
var svgRoot = SVG('').addTo('box-workarea').size('100%','100%')id('svgroot')
在svg.js中提供了检测浏览器是否支持svg的方法
if (SVG.supported) {
var svgRoot = SVG('box-workarea').size('100%','100%').id('svgroot')
} else {
alert('您的浏览器版本不支持SVG,请更换浏览器或者升级版本!')
}
创建完svg后,要创建一层grouproot层用来包裹所有元素,而图形之间连线用group_polyline包裹,每个图形都具有引脚的属性,用作触发连线的点,依次类推;接着用js定义图形对象
var groupRoot = svg.group().id('groupRoot').size('3000px','1500px').attr('style','background-color:white') // all
var groupPolyLine = groupRoot.group('group_polyline') // 连线层
var selectDrawObjList = []; // 用来存放选中的图形集合
var changeLineX = false; // 连线的起线变换是否位于X轴
var changeLiney = false; // 连线的起线变换是否位于Y轴
var component = new Component(); //创建图形操作对象
var originScale = 1; // 原始比例
var gridNumber = 10; //栅格数(移动步长)
var isSelectBox = false;// 矩形拉选
var isMulSelect = false;// 是否处于多选状态
var isMoveMulSelect = false; // 是否开启批量移动
var currentMousePointer = {}; // 当前点击的点坐标
var outpattern = defs.pattern(10, 10, function(add) {
add.path('M 10 0 L 0 0 0 10').fill('none').stroke({color:'#eee',width:0.5})
add.pattern(10,10,function(add){
add.rect().attr({width:10,height:10,fill:'url(#smallGrid)'})
add.path('M 100 0 L 0 0 0 100').fill('none').stroke({color:'#eee',width:1})
}).attr({'id':'grid'})
}).attr({'id':'smallGrid'})
var gridRect = svgRootGroup.rect().size('3000px','1500px').fill('url(#grid)').id('gridrect') // 网格
svgRootGroup.add(gridRect)
var polylineGroup = svgRootGroup.group().id('polylinegroup') // 连线组对象
var crossLine_X = svgRoot.path('M 0 0').attr({'id':'cross_line_x','stroke-dasharray':'2,2', 'fill':'none','stroke':'red', 'stroke-width':1})
var crossLine_Y = svgRoot.path('M 0 0').attr({'id':'cross_line_y','stroke-dasharray':'2,2', 'fill':'none','stroke':'red', 'stroke-width':1})
var selectRectBox = svgRoot.rect('0,0').attr({'id':'select_rect_box','stroke-dasharray':'3,3','fill':'none','stroke':'#0000ff','stroke-width':1}).hide()
画板的响应事件应该对应有:
mousedown、mouseup(移动结束或连线结束)、mousewheel(放大和缩小)、mousemove(移动)、keyup(鼠标按键)、mouseenter(移动画板时触发)、mouseleave(离开画板时触发)
//画布键盘相应事件
$(document).on('keyup',function (e) {
switch (e.keyCode) {
case 46:;break; // 删除
case 82:;break; //旋转
//...复制等等
}
})
svgRoot.on('mousedown',function(e){
// 判断是否鼠标左键
if(1 == e.which){
// 选中图形逻辑等等
if(){
}
// 若无选中图形则开启矩形选择
if (!isSelectBox && isComplatePath) {
isSelectBox = true
isMulSelect = false
isMoveMulSelect = false
selectRectBox.attr({'x':e.offsetX,'y':e.offsetY,'width':0,'height':0})
currentMousePointer.x = e.offsetX
currentMousePointer.y = e.offsetY
selectRectBox.show()
return;
}
}
})
svgRoot.on('mouseup',function(e){
if(1 == e.which){
// 取消矩形选择
selectRectBox.hide()
}
})
// 鼠标滚轮发大和缩小
svgRoot.on('mousewheel',function(e){
var target = $(e.target).context
var delta = e.originalEvent.wheelDelta
var scale = originScale;
originScale = originScale * 10
if(delta > 0 && originScale < 50)
originScale += 2
else if (delta < 0 && originScale > 2 && originScale <= 50)
originScale -= 2
originScale = originScale / 10
var t = svgRoot.transform();
svgRoot.scale(originScale,(e.offsetX - (t.e + svgRoot.node.clientWidth/2))/scale,(e.offsetY - (t.f + svgRoot.node.clientHeight/2))/scale)
})
// 根据自己需求书写
svgRoot.on('mouseenter',function(e){
})
// 根据自己需求书写
svgRoot.on('mouseleave',function(e){
})
svgRoot.on('mousemove',function(e){
// 矩形选择
if (e.which == 1) {
if (isSelectBox) {
var x = currentMousePointer.x
var y = currentMousePointer.y
var d = {}
d.x = Math.abs(x- e.offsetX)
d.y = Math.abs(y- e.offsetY)
if (x <= e.offsetX) {
selectRectBox.attr('x',x);
}else {
selectRectBox.attr('x',e.offsetX);
}
if (y <= e.offsetY) {
selectRectBox.attr('y',y);
}else {
selectRectBox.attr('y',e.offsetY);
}
selectRectBox.size(d.x,d.y)
}
// 调用自身的移动方法
if (isMulSelect && isMoveMulSelect) {
selectDrawObjList.forEach(function(obj){
obj.group.dmove(d.x,d.y)
})
}
}
})
function DrawObj(){
this.id=''; // 图形id
this.displayName=''; // 图形显示名称
this.value=''; // 数据化操作
this.foots=[]; // 引脚集合
this.location='' // 图形左上角坐标
this.svgcontent='' //svg描述字符串,如'<rect>xxxx</rect>,<line></line>'
// 定义绘画方法
this.draw = function(){
// 每个图形创建一个group
var group = grouproot.group()
var groupfoot = group.group() // 包裹在图形group里
var objStrList = this.svgcontent.split(',')
objStrList.forEach(function(v){
// svg.js有个通过字符串导入svg对象的方法
var obj = svgroot.svg(v)
group.add(obj)
})
// 添加引脚,用圆形来代替引脚
this.foots.forEach(function(foot){
var circle = svgroot.circle().attr({
id:foot.id,
cx:foot.x,
cy:foot.y,
r:3,
fill:'#ffffff00' //设置透明色将引脚隐藏
})
// 定义移动到引脚上时时间
circle.on('mouseover',function(e){
// 显示引脚
circle.fill('red')
})
// 定义点击引脚创建连线事件
circle.on('mousedown',function(e){
// 创建连线事件
createPolyLine()
})
// 定义离开引脚事件
circle.on('mouseout',function(e){
circle.fill('#ffffff00')
})
})
// 定义group选中方法和resize方法,若是一维图形如直线、折线等,则selectize({deepSelect:true})可添加参数描述
group.selectize().resize();
// 定义图形拖动,这里添加一个移动时发生的事件
group.draggable().on('dragmove', e => {
})
}
// 删除图形
this.remove = function(){
if (!_this.isLock) {
_this.group.remove()
}
}
function SVGPolyline(){
this.startFoot = null; // 起点引脚对象
this.endFoot = null; // 终点引脚对象
this.points=[]; //连线点集合
// 移动图形时,改变连接处点
this.moveDrawObj(polyline){
}
}
function Component(){
this.id='';
this.drawObjList=[];
// 取消全部选中器件
this.unSelectAll = function(){
selectDrawObjList.forEach(function(v){
v.unSelected()
})
}
// 添加图形
this.addDrawObj=function(obj){
}
// 移除图形
this.removeDrawObj=function(obj){
obj.remove()
}
// 重置画板
this.resetDrawObj=function(){
}
}
function createPolyLine(){
var polyLine = svgroot.polyLine()
...
}
以上代码非完整,请勿全部复制粘贴,仅提供大体的结构思路,欢迎指正和讨论。楼主已经做出了一个版本了(正常使用),这是之前构思的初稿,基本思路没有问题。