当前位置: 首页 > 工具软件 > magnify.js > 使用案例 >

SVG.js 开发画图工具2.0

班安平
2023-12-01

初稿:https://blog.csdn.net/qq_44941500/article/details/105021135

1 定义全局变量

let svgRoot = SVG('box-workarea').id('svgroot').size('3000px','1500px').attr('style','background-color:white').addClass('noselectText') // 画板svg根对象
let svgRootGroup = svgRoot.group().id('svgRootGroup')
let svgComponent = new SvgComponent; // 器件组管理对象
var svgmenu = new menu(); // 菜单对象
var selectCmpt = null; // 选中单个器件
var selectCmpts = []; // 选中多个器件
var selectLines = null; // 选中的连线
var selectPolylines = []; // 选中多条连线
var originScale = 1; // 原始比例
var isComplatePath = true;  // 绘制连线
var changeLineX = false; // 连线X变向
var changeLineY = false; // 连线Y变向
var isAddCmpt = false; // 是否处于添加器件状态
var activeCmpt = null; // 刚添加器件
var isSelectBox = false;// 矩形拉选
var isMulSelect = false;// 是否处于多选状态
var isMoveMulSelect = false; // 是否开启批量移动
var isEditPolyline = false; // 是否进入编辑连线状态
var editPolyLine = null; // 编辑连线对象
var currentMousePointer = {}; // 当前点击的点坐标
var currentFootTransform = null; // 当前器件变换矩阵
var currentPolyline = null;  // 当前连线
var currentPolylineObj = null; // 当前连线对象
var gridNumber = 10; //栅格数(移动步长)
var svgWidth = 0;
var svgHeight =0;
var svgPolyLineNum = 0;
var defs = svgRoot.defs()
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()

2 定义画板事件

mousedownmouseup(移动结束或连线结束)、mousewheel(放大和缩小)、mousemove(移动)、keyup(鼠标按键)、mouseenter(移动画板时触发)、mouseleave(离开画板时触发)

// 格子缩放计算
function changeGrid(num) {
	return Math.round(num/gridNumber)*gridNumber;
}
//画布键盘相应事件
$(document).on('keyup',function (e) {
    switch (e.keyCode) {
        // 删除选中的器件
        case 46:removeSelect();break;
        case 82:selectCmpt.rotateCmpt();break;
    }
})
// 右键菜单事件
$('#svgroot').on("contextmenu",function(e){
		 if(isAddCmpt){
	    	activeCmpt.group.remove()
			activeCmpt.group = null
	    	isAddCmpt = false;
			activeCmpt = null;
			return false;
		 }
		 if (!isComplatePath && currentPolyline != null){
		    	var points = currentPolyline.attr('points').split(' ')
		    	var l = points.length
		    	// 仅剩起点
		    	if (l <= 3) {
		    		currentPolyline.remove()
		    		currentPolylineObj.startCmpt.circuitMap.delete('startFoot_'+currentPolylineObj.startFoot.attr('id')+'_'+svgPolyLineNum)
		    		svgComponent.finishLine(false)
		    		return false;
				}
		        // 添加的点
				points.splice(l-1,1)
		        currentPolyline.attr('points',points)
		        return false;
		   }
		// 显示快捷菜单
		if(selectCmpt !=null){
			svgmenu.selectCmptMenu(selectCmpt)
			selectCmpts.push(selectCmpt)
		}
		$('#svgmenu').menu('show', {
			left: e.pageX,
			top: e.pageY,
			hideOnUnhover: false,
			noline:false
		});
		currentMousePointer.x = e.offsetX
		currentMousePointer.y = e.offsetY
		return false	
});

// 取消选择器件 + 点击引脚事件
svgRoot.on('mousedown',function(e){
    if(1 == e.which){
    	// 编辑连线
    	var target = $(e.target).context
    	if (isEditPolyline) {
    		editPolyLine.polyline.stroke('#800')
    		editPolyLine = null;
    		isEditPolyline = false;
		}
    	// 添加器件过程
    	if (!isAddCmpt && activeCmpt == null){
			// 矩形选中
    		if (!isSelectBox && isComplatePath) {
    			svgComponent.unSelectAll()
				isSelectBox = true
				isMulSelect = false
				isMoveMulSelect = false
				svgmenu.disableOtherMenu()
				selectRectBox.attr({'x':e.offsetX,'y':e.offsetY,'width':0,'height':0})
				currentMousePointer.x = e.offsetX
				currentMousePointer.y = e.offsetY
				selectRectBox.show()
				return;
			}
	        // 添加连线点
	        if (!isComplatePath && currentPolyline != null ){
	            // 最终连接引脚
	                var points = currentPolyline.attr('points').split(' ')
	                var l = points.length
	                // 添加的点
	                var addP = points[l-2]
					points.splice(l-2,0,addP)
	                currentPolyline.attr('points',points)
	        }
		}else{
        	if (typeof(activeCmpt.addCallback) == 'function') {
    				activeCmpt.addCallback()
    			}
        		activeCmpt.index = svgComponent.cmpts.length
        		svgComponent.cmpts.push(activeCmpt)
        		isAddCmpt = false;
        		activeCmpt = null;
        		return;
        	}
    }
    if (!isComplatePath && currentPolyline != null && 3 == e.which){
    	var points = currentPolyline.attr('points').split(' ')
    	var l = points.length
    	// 仅剩起点
    	if (l <= 3) {
    		currentPolyline.remove()
    		currentPolylineObj.startCmpt.circuitMap.delete('startFoot_'+currentPolylineObj.startFoot.attr('id')+'_'+svgPolyLineNum)
    		svgComponent.finishLine(false)
    		return;
		}
        // 添加的点
		points.splice(l-1,1)
        currentPolyline.attr('points',points)
    }
})

svgRoot.on('mouseup',function(e){
	if(e.which == 1){
		// 隐藏选中框
		if(isSelectBox && !isMulSelect){
			svgComponent.cmpts.forEach(function(cmpt){
				cmpt.selectedJudge()
			})
			svgComponent.circuits.forEach(function(c){
				c.selectedLine()
			})
			selectRectBox.hide()
			isSelectBox = false
			if (selectCmpts.length !=0 || selectPolylines.length != 0) {
				isMulSelect = true // 打开多选
				svgmenu.enableMenu()//启用菜单
			}
			return;
		}
		// 批量移动
		if (isMulSelect && isMoveMulSelect) {
			selectCmpts.forEach(function(cmpt){
				var t = cmpt.group.transform()
				var d = {}
				d.x = changeGrid(t.e) - t.e
				d.y = changeGrid(t.f) - t.f
				// 调用连线调整方法
				cmpt.circuitMap.forEach(function(v,k){
				if(!v.isSelected){
					if (k.split('_')[0] == 'startFoot') {
						v.moveFootPolyline(1,d)
					}else {
						v.moveFootPolyline(2,d)
					}
				}
				})
				cmpt.group.move(changeGrid(t.e),changeGrid(t.f))
			})
			selectPolylines.forEach(function(l){
				var t = l.group.transform()
				var d = {}
				d.x = t.e - changeGrid(t.e) 
				d.y = t.f - changeGrid(t.f) 
				l.group.move(changeGrid(t.e),changeGrid(t.f))
				// 调用自身调整方法
				if (!l.startCmpt.isSelected) {
					l.moveFootPolyline(1,d)
				}
				if (!l.endCmpt.isSelected) {
					l.moveFootPolyline(2,d)
				}
			})
			svgRootGroup.attr('style','cursor:default')
			return;
		}
	}else if (e.which == 2) {
		svgRootGroup.attr('style','cursor:default')
	}
})

$('#svgroot').on('mouseenter',function(e){
	if (isAddCmpt && activeCmpt != null && activeCmpt.group == null) {
		activeCmpt.component()
	}
})
$('#svgroot').on('mouseleave',function(e){
	if (isAddCmpt && activeCmpt != null) {
		activeCmpt.group.remove()
		activeCmpt.group = null
	}
})
$('#box-workarea').on('mousewheel',function (e) {
    e.preventDefault();
    e.stopPropagation();
    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)
    if (originScale <= 0.6) {
		gridRect.hide()
	}else{
		gridRect.show()
	}
})

$('#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) {
			var d = {},td = {}
			d.x = e.originalEvent.movementX/originScale
			d.y = e.originalEvent.movementY/originScale
			td.x = -d.x 
			td.y = -d.y
			selectCmpts.forEach(function(cmpt){
				if (!cmpt.isLock) {
					cmpt.group.dmove(d.x,d.y)
					// 调用连线调整方法
					cmpt.circuitMap.forEach(function(v,k){
					if(!v.isSelected){
						if (k.split('_')[0] == 'startFoot') {
							v.moveFootPolyline(1,d)
						}else {
							v.moveFootPolyline(2,d)
						}
					}
                })
				}
			})
			selectPolylines.forEach(function(l){
				l.group.dmove(d.x,d.y)
				// 调用自身调整方法
				if (!l.startCmpt.isSelected) {
					l.moveFootPolyline(1,td)
				}
				if (!l.endCmpt.isSelected) {
					l.moveFootPolyline(2,td)
				}
			})
			svgRootGroup.attr('style','cursor:move')
			return;
		}
	}
	
	// 鼠标中键全部器件拖动
	if (e.which == 2) {
		var t = svgRoot.transform()
		t.e = t.e+e.originalEvent.movementX
		t.f = t.f+e.originalEvent.movementY
		svgRoot.transform(t)
		svgRootGroup.attr('style','cursor:move')
		return;
	}
	
	// 添加器件中
	if (isAddCmpt && activeCmpt != null && activeCmpt.group != null) {
		activeCmpt.svgCmptMove(changeGrid(e.offsetX - activeCmpt.group.attr('width')/2),changeGrid(e.offsetY - activeCmpt.group.attr('height')/2))
	}
    // 绘制连线中
    if (!isComplatePath && currentPolyline != null && !isAddCmpt){
         var transform = currentFootTransform
         var x = changeGrid(e.offsetX) ,y = changeGrid(e.offsetY) ;
         var points = currentPolyline.attr('points').split(' ')
         var endP = null;
         var preP = null;
         var nearP = null;
         if(points.length>=3){
              endP = points.pop().split(',')
             nearP = points.pop().split(',')
             preP = points[points.length - 1].split(',')
         }else {
             endP = points.pop().split(',')
             preP = points[points.length - 1].split(',')
             nearP = preP
         }
        // 终点与第三个点比较

        if (Math.abs(preP[0] - x)<5 && !changeLineX){
            changeLineX = true;
            changeLineY = false;
        }
        if (Math.abs(preP[1] - y)<5 && !changeLineY){
            changeLineX = false;
            changeLineY = true;
        }
        if(changeLineX && !changeLineY){
            nearP[0] = preP[0]
            nearP[1] = y
        }else {
            nearP[0] = x
            nearP[1] = preP[1]
        }
        points.push(nearP.toString())
        points.push(x+','+y)
        currentPolyline.attr('points',points)
        crossLine_X.plot('M'+x+' 0 v'+$('#svgroot').height())
        crossLine_Y.plot('M0 '+y+' h'+$('#svgroot').width())
    }
})

// 右键菜单对象
function menu(){
	let _this = this
	// 菜单 DOM元素
	let menu = $('#svgmenu')
	// 删除按钮 DOM元素
	this.delBtnEl = $('#delCmpts')[0];
	// 锁定按钮 DOM元素
	this.lockBtnEl = $('#lockCmpts')[0];
	// 解锁按钮 DOM元素
	this.unLockBtnEl = $('#unLockCmpts')[0];
	// 引脚按钮 DOM元素
	this.viewFootEl = $('#viewFoot')[0];
	// 全部引脚按钮 DOM元素
	this.viewAllFootEl = $('#viewAllFoot')[0];
	// 启用菜单
	this.enableMenu = function(){
		_this.enableMenu(_this.delBtnEl)
		_this.enableMenu(_this.lockBtnEl)
		_this.enableMenu(_this.unLockBtnEl)
	}
	// 禁用菜单
	this.disableOtherMenu = function(){
		_this.disableMenu(_this.delBtnEl)
		_this.disableMenu(_this.lockBtnEl)
		_this.disableMenu(_this.unLockBtnEl)
		_this.hideMenu(_this.viewFootEl)
	}
	// 单个器件
	this.selectCmptMenu = function(cmpt){
		if (cmpt.isLock) {
			_this.disableMenu(_this.delBtnEl)
			_this.disableMenu(_this.lockBtnEl)
			_this.enableMenu(_this.unLockBtnEl)
		}else {
			_this.enableMenu(_this.delBtnEl)
			_this.enableMenu(_this.lockBtnEl)
			_this.disableMenu(_this.unLockBtnEl)
		}
		_this.showMenu(_this.viewFootEl)
		if (!cmpt.isDisplayFootText) {
			menu.menu('setText', {target: _this.viewFootEl,text: '显示引脚'});
		}else {
			menu.menu('setText', {target: _this.viewFootEl,text: '隐藏引脚'});
		}
	}
	// 删除器件
	this.delCmpts = function(){
		removeSelect()
	}
	// 锁定器件
	this.lockCmpts = function(){
		selectCmpts.forEach(function(cmpt){
			cmpt.unSelected()
			cmpt.isLock = true;
		})
		selectPolylines.forEach(function(l){
			
		})
		selectCmpts = []
		selectPolylines = []
	}
	// 解锁器件
	this.unLockCmpts = function(){
		selectCmpts.forEach(function(cmpt){
			cmpt.unSelected()
			cmpt.isLock = false;
			
		})
		selectPolylines.forEach(function(l){
			
		})
		selectCmpts = []
		selectPolylines = []
	}
	// 合并器件
	this.addGroup = function(){
		
	}
	// 分解器件
	this.unGroup = function(){
		
	}
	// 引脚显示
	this.viewFoot = function(){
		selectCmpts.forEach(function(cmpt){
			cmpt.viewFoot()
		})
	}
	// 显示全部引脚
	this.viewAllFoot = function(){
		var item = menu.menu('getItem', _this.viewAllFootEl);
		if (item.text == '显示全部引脚') {
			menu.menu('setText', {target: _this.viewAllFootEl,text: '隐藏引脚'});
			svgComponent.cmpts.forEach(function(cmpt){
				if (!cmpt.isDisplayFootText) {
					cmpt.viewFoot()
				}
			})
		}else{
			menu.menu('setText', {target: _this.viewAllFootEl,text: '显示引脚'});
			svgComponent.cmpts.forEach(function(cmpt){
				if (cmpt.isDisplayFootText) {
					cmpt.viewFoot()
				}
			})
		}
	}
	// 放大或缩小
	this.zoomChange = function(type){
		var scale = originScale
		originScale = originScale * 10
		switch (type) {
		// 缩小
		case 'shrink':
			if (originScale > 2 && originScale <= 50)
		        originScale -= 2
			break;
		// 放大
		case 'magnify':
			if(originScale < 50)
		        originScale += 2
			break;
		}
	    originScale = originScale / 10
	    var t = svgRoot.transform();
		svgRoot.scale(originScale,(currentMousePointer.x - (t.e + svgRoot.node.clientWidth/2))/scale,(currentMousePointer.y - (t.f + svgRoot.node.clientHeight/2))/scale)
		if (originScale <= 0.6) {
			gridRect.hide()
		}else{
			gridRect.show()
		}
	}
	// 菜单禁用
	this.disableMenu = function(el){
		menu.menu('disableItem',el)
	}
	// 菜单启用
	this.enableMenu = function(el){
		menu.menu('enableItem',el)
	}
	// 隐藏菜单
	this.hideMenu = function(el){
		menu.menu('hideItem',el)
	}
	// 显示菜单
	this.showMenu = function(el){
		menu.menu('showItem',el)
	}
}
// 菜单处理
function menuHandler(obj){
	var type = obj.name
	switch (type) {
	// 缩小
	case 'shrink':
	// 放大
	case 'magnify':
		svgmenu.zoomChange(type)
		break;
	// 删除
	case 'delete':
		svgmenu.delCmpts()
		break;
	// 锁定
	case 'lock':
		svgmenu.lockCmpts()
		break;
	// 解锁
	case 'unlock':
		svgmenu.unLockCmpts()
		break;
//	// 合并
//	case 'combine':
//		svgmenu.combineCmpts()
//		break;
//	// 分解
//	case 'decompose':
//		svgmenu.decompose()
//		break;
	case 'viewFoot':
		svgmenu.viewFoot();
		break;
	case 'viewAllFoot':
		svgmenu.viewAllFoot();
		break;
	case 'viewProperty':
		pubMessage('【属性】功能正在开发中...');
		break;
	}
}

3 定义画布中的一个对象

function SvgCmpt(){
	let _this = this;
    // 器件id
    this.id='';
    // 器件类型   0 —— 普通器件,1 —— vss,2 —— 输入,输出 ,3 —— 电阻、电容、电感
    this.cmptType = 0; 
    // 输入、输出对象
    this.instr = null;
    // 模块id
    this.moduleId = '';
    // 模块位置
    this.modulePos = '';
    // 模块在集合中的位置
    this.index = 0;
    // 显示名称
    this.displayName='';
    // 显示值
    this.displayValue='';
    // 器件左上角坐标
    this.location='';
    // 最近移动点x坐标
    this.laster_X=0;
    // 最近移动点y坐标
    this.laster_y=0;
    // 器件线宽
    this.stroke ='';
    // 器件颜色
    this.strokeColor = '';
    // 器件引脚集合
    this.foots = [];
    // 旋转角度 90,180,270
    this.angle = 0;
    // 器件描述内容
    this.cmptContent = '';
    // 器件所在组
    this.group = null;
    // clone对象
    this.cloneGroup = null;
    // 引脚文字对象
    this.footTextGroup = null;
    // 添加器件回调函数
    this.addCallback = null;
    // 删除器件回调函数
    this.delCallback = null;
    // 器件面积
    this.size = {};
    // 器件状态(是否锁定)
    this.isLock = false;
    // 是否合并
    this.isCombine = false;
    // 合并后的父group
    this.parentGroup = null;
    // 器件是否选中
    this.isSelected = false;
    // 器件外边框
    this.outRect = null
    // 器件引脚预连线
    this.isPreLine = false;
    // 器件是否在移动
    this.isMoving = false;
    // 是否被移除
    this.isDelFlag = false;
    // 是否显示引脚名称
    this.isDisplayFootText = false;
    // VCC VEE GND
    this.isOpenVCC = false;
    this.isOpenVEE = false;
    this.isOpenGND = false;
    // 器件上面的连线集合
    this.circuitMap = new Map;
    // 器件resize 放大缩小
    this.svgCmptResize = function(size){
        // 放大或缩小group

    }
    // 移动器件
    this.svgCmptMove = function(offsetX,offsetY){
        // 移动group
        if(_this.group!=null)
        	_this.group.move(offsetX,offsetY)
    }
    // 选中绘制样式
    this.drawTracker = function () {
        //初始化点击事件
    }
    // 旋转图形
    this.rotateCmpt = function(){
    	_this.angle += 90
    	_this.group.rotate(_this.angle)
    }
    // 取消选中
    this.unSelected = function(){
    	if (_this.isSelected) {
    		_this.isSelected = false
        	_this.outRect.hide()
		}
    }
    // 判断是否选中
    this.selectedJudge = function () {
    	var t = _this.group.transform()
    	var x = t.e + _this.size.width/2
    	var y = t.f + _this.size.height/2
		if (selectRectBox.inside(x,y)) {
			selectCmpts.push(_this)
    		_this.isSelected = true
        	_this.outRect.show()
		}
    }
    // 删除器件
    this.remove = function(){
    	if (!_this.isLock) {
    		_this.group.remove()
        	// 删除连线
        	_this.circuitMap.forEach(function(v,k){
        		var data = v.polyline.attr('id')
            	var req = new ReqToSystem(214,data)
            	req.response=function(resp){
    			if(resp!=null){
    				v.removePolyline();
    			}else{
    				pubMessage('删除连线失败!','error');
    			}};
    			if (!v.isDeleted){
    				send(req);
    			}
        	})
    		}
    	}
    this.viewFoot = function (){
    	// 是否显示引脚名称
	  if (_this.isDisplayFootText) {  
		  _this.isDisplayFootText = false
	       _this.footTextGroup.hide()
		}else{
		  _this.isDisplayFootText = true
		  _this.footTextGroup.show()
		}
    }
    this.component = function(){
        //创建器件组
        var group = svgRootGroup.group().size(_this.size.width,_this.size.height).attr({x:20,y:20})
        var cmptGroup = group.group().id('svgcmpt_'+_this.id).size(_this.size.width,_this.size.height).attr({x:20,y:20})
        var outRect = group.rect().size(_this.size.width,_this.size.height).fill('#ffffff00').stroke({color:'#f96a6a8f',width:'1'})
        group.add(outRect)
        _this.outRect = outRect;
        outRect.hide()
        //添加器件图形
        var list = cmptGroup.svg(this.cmptContent)
        //显示器件值或器件名称
        if(_this.cmptType == 3){
        	$('#displayeName'+_this.id).text(_this.displayName)
        	$('#displayValue'+_this.id).text(_this.displayValue)
        }
        // 添加器件引脚
        var footgroups = group.group().id('foot')
        var footTextGroups = group.group().id('textFoot')
        var footTextStr = ''
        this.foots.forEach(function (v) {
        	var pos = 0
        	if (_this.modulePos <= 16) {
        		pos = Number(v.index) + _this.modulePos * 16
        		footTextStr = _this.modulePos + '-' + (Number(v.index))
			}else if(_this.modulePos>192 && _this.modulePos <= 203){
				pos = _this.modulePos + Number(v.index)
				footTextStr = parseInt(_this.modulePos/16) + '-' + (Number(v.index)+1)
			}
        	var cx = v.x
            var cy = v.y
            // 引脚文字
            var tx = v.x
            var ty = v.y
            var text = svgRoot.text(footTextStr).font({x:tx,y:ty,family:'Times New Roman',size:5}).fill('green')
            footTextGroups.add(text)
            var circle = svgRoot.circle().attr({
                cx:cx,cy:cy,r:5,stroke:'#ffffff00',fill:'#ffffff00',id:'foot_'+pos
            }).stroke({width:1})
            circle.mouseenter(function (e) {
            	e.preventDefault()
            	$(e.target).attr('fill','red')
            	// 处于欲连线状态
            	if (e.which == 0 && isComplatePath && currentPolyline == null && !isMulSelect) {
					_this.isPreLine = true
				}
            	if (e.which == 0 && _this.isSelected && !isMulSelect) {
					selectCmpt = null
		        	_this.isSelected = false
		        	_this.outRect.hide()
				}
            })
            circle.mouseleave(function (e) {
                $(e.target).attr('fill','#ffffff00')
                // 关闭欲连线状态
                if (e.which == 0) {
                	_this.isPreLine = false
				}
            })
            circle.mouseup(function (e) {
            	e.preventDefault()
            	if(isAddCmpt && activeCmpt !=null){
            		isAddCmpt = false;
            		activeCmpt = null;
            		return;
            	}
            	_this.isPreLine = false
            	//完成连线
            	if (!isComplatePath && currentPolyline != null && !isMulSelect){
            		var points = currentPolyline.attr('points').split(' ')
                    points.pop(); // 删除末点
            		var nearP = points[points.length-1].split(',')
            		points.pop();// 删除邻近点
                    // 偏移坐标
                    var t = circle.parent().parent().transform()
                    // 绝对坐标
                    var x = circle.attr('cx') + t.e , y =circle.attr('cy') + t.f;
                    if (changeLineX) {
						nearP[1] = y
					}
                    if (changeLineY) {
						nearP[0] = x
					}
                    points.push(nearP.join(','))
                    points.push(x+','+y)
                    // 发送验证连线请求,若连线无效则返回
                   
                     // 添加末点引脚连线,先本地检查,后网络检查
                    currentPolylineObj.endPos = circle.attr('id').split('_')[1]
                    if(!checkPolyLine(currentPolylineObj.startPos,currentPolylineObj.endPos)){
                    	// 无效连线,并删除连线
                		currentPolyline.remove()
                		pubMessage('无效连线!','error');
                    	currentPolylineObj.startCmpt.circuitMap.delete('startFoot_'+currentPolylineObj.startFoot.attr('id')+'_'+svgPolyLineNum)
                    	svgComponent.finishLine(false)
                    	return ;
                    }
                    	var data = currentPolylineObj.polylinePosResult() 
                    	var req = new ReqToSystem(213,data)
                    	req.response=function(resp){
            				if(resp!=null){
            					// svg连线对象
            					currentPolyline.attr('points',points)
            					currentPolyline.attr('id',data)
                                currentPolylineObj.polyline = currentPolyline
                                currentPolylineObj.points = points 
                                currentPolylineObj.endFoot = circle
                                currentPolylineObj.endCmpt = _this
                                currentPolylineObj.index = svgPolyLineNum
                                svgComponent.addCircuit(currentPolylineObj)
                                _this.circuitMap.set('endFoot_'+circle.attr('id')+'_'+svgPolyLineNum,currentPolylineObj)
                                svgPolyLineNum++;
            					svgComponent.finishLine(true);
            				}else{
            					pubMessage('连线失败!','error');
            				}
            			}
                    	send(req);
                    return;
            	}
                // 开启连线
            	svgComponent.createRootPath(e,circle,_this)
                // 添加起点连线
                _this.circuitMap.set('startFoot_'+circle.attr('id')+'_'+svgPolyLineNum,currentPolylineObj)
            })
            footgroups.add(circle)
        })
        group.add(footgroups)
        group.on('mousedown',function (e) {
        	if (e.which == 1) {
        		if(isAddCmpt && activeCmpt !=null){
            		if (typeof(activeCmpt.addCallback) == 'function') {
        				activeCmpt.addCallback()
        			}
            		svgComponent.cmpts.push(activeCmpt)
            		isAddCmpt = false;
            		activeCmpt = null;
            		return;
            	}else if(isMulSelect && !isMoveMulSelect){
            		isMoveMulSelect = true
				}
			}
        })
        // 鼠标悬浮在器件上
        group.on('mouseover',function(e){
        	if (isComplatePath && !_this.isSelected && !_this.isMoving && !isAddCmpt && !_this.isPreLine && !isSelectBox && !isMulSelect) {
        		selectCmpt = _this
        		_this.isSelected = true
            	outRect.show()
			}
        })
        // 鼠标离开器件
        group.on('mouseleave',function(e){
        	if(!isSelectBox && selectCmpt != null && !isMulSelect){
        		selectCmpt = null
            	_this.isSelected = false
            	outRect.hide()
        	}
        })
        // 双击事件
        if (_this.cmptType == 2) {
			group.on('dblclick',function(){	
				if(!instrument)
				  instrument=new Instrument();
				if(_this.id=='DS')
				  instrument.ds.open(_this.instr);
				else if(_this.id=='DG')
				  instrument.dg.open(_this.instr);
			});
		}
        group.draggable().on('beforedrag',e =>{
        	if (!_this.isMoving && !_this.isPreLine && !isMulSelect && !_this.isLock && !isAddCmpt) {
                		var d = e.detail
                		var t = _this.group.transform()
                		_this.laster_X = changeGrid(t.x)
                        _this.laster_Y = changeGrid(t.y)
                        _this.group.attr('style','cursor:move')
        	 }
        }).on('dragmove',e =>{
        	e.preventDefault()
        	var d = e.detail
        	if (!_this.isLock && !_this.isMoving && d.event.type == 'mousemove' && isComplatePath && !_this.isPreLine && !isMulSelect) {
        		_this.isMoving = true;
        		_this.cloneGroup = group.clone();
        		_this.cloneGroup.opacity(0.5)
            	_this.cloneGroup.attr('style','cursor:move')
			}else if(!_this.isLock && d.event.type == 'mousemove'&& isComplatePath && !_this.isPreLine  && !isMulSelect){
				_this.cloneGroup.dmove(d.event.movementX/originScale,d.event.movementY/originScale)
				//_this.cloneGroup.move(d.event.offsetX - _this.cloneGroup.attr('width')/2,d.event.offsetY-_this.cloneGroup.attr('height')/2)
			}
        }).on('dragend',e =>{
        	if (!_this.isLock && _this.isMoving && !_this.isPreLine && !isMulSelect) {
        		var detail = e.detail
        		var group = _this.group;
        		var movement = {};
        		var d = {}
        		var ct = _this.cloneGroup.transform()
        		var t = group.transform()
        		t.e = changeGrid(ct.x)
        		t.f = changeGrid(ct.y)
        		group.transform(t)
        		d.x = changeGrid(ct.x) - _this.laster_X
        		d.y = changeGrid(ct.y) - _this.laster_Y
        		_this.cloneGroup.remove()
        		_this.isMoving = false;
        		_this.circuitMap.forEach(function(v,k){
        			if (k.split('_')[0] == 'startFoot') {
        				v.moveFootPolyline(1,d)
        			}else {
        				v.moveFootPolyline(2,d)
        			}
                })
			}
        	 _this.group.attr('style','cursor:defalut')
        })
        // 是否显示引脚名称
        if (!_this.isDisplayFootText) { 
            footTextGroups.hide()
            _this.footTextGroup = footTextGroups
		}
        this.group =  group;
    }
}

4 删除连线函数

//删除选中器件、连线
function removeSelect(){
	if (selectCmpts.length>0) {
		selectCmpts.forEach(function(cmpt){
			if (cmpt != null && !cmpt.isLock) {
	        	if (typeof(cmpt.delCallback) == 'function') {
	        		cmpt.delCallback()
				}
	        	cmpt.remove();
			 }
		})
		selectCmpts = []
	}else if (selectCmpt != null && !selectCmpt.isLock) {
    	if (typeof(selectCmpt.delCallback) == 'function') {
    		selectCmpt.delCallback()
		}
    	selectCmpt.remove();
        // 删除器件
        //svgComponent.removeCmpt()
	 }
	// 删除连线
	if (selectPolylines.length>0) {
		selectPolylines.forEach(function(line){
				var data = line.polyline.attr('id')
	        	var req = new ReqToSystem(214,data)
	        	req.response=function(resp){
				if(resp!=null){
					line.removePolyline();
				}else{
					pubMessage('删除连线失败!','error');
				}};
				if (!line.isDeleted) {
					send(req);
				}
			})
			selectPolylines = [];
		}
	if(editPolyLine != null){
		var data = editPolyLine.polyline.attr('id')
		var req = new ReqToSystem(214,data)
    	req.response=function(resp){
		if(resp!=null){
			editPolyLine.removePolyline();
			editPolyLine = null;
			isEditPolyline = false;
		}else{
			pubMessage('删除连线失败!','error');
		}};
		if (!editPolyLine.isDeleted) {
			send(req);
		}
	}
	isMulSelect = false;
	isMoveMulSelect = false;
}

5 定义 对象+线 的组合对象

function SvgCircuit(){
	let _this = this;
	// index序列
	this.index = 0;
	// groupId
	this.groupId = '';
	// group属性
	this.group = null;
    // 连线对象
    this.polyline = null
    // 连线起点引脚对象
    this.startFoot = null;
    // 连线终点引脚对象 
    this.endFoot = null;
    // 连线颜色
    this.strokeColor = '';
    // 连线起点在大节点位置
    this.startPos = '';
    // 连线终点在大节点位置
    this.endPos = '';
    // 连线起点器件对象 
    this.startCmpt ='';
    // 连线末点器件对象
    this.endCmpt = '';
    // 连线点集合
    this.points = [];
    // 是否被选中
    this.isSelected = false;
    // 是否已经被删除
    this.isDeleted = false;
    // 移动器件时更改连线  传入引脚参数  1-起点 2-终点
    this.moveFootPolyline=function(type,movement){
    	if (!isComplatePath && _this.endFoot == null) {
    		return;
    	}
    	var l = _this.points.length
    	if (l > 4 ) {
    		_this.points = svgComponent.optimizePolyline(_this.points)
		}
    	if (l>=3) {
    		switch (type) {
    		case 1:
    			var sp = _this.points[0].split(',')
    			var np = _this.points[1].split(',')
    			var tp = _this.points[2].split(',')
    			_this.points.splice(0,1,(Number(sp[0])+movement.x)+','+(Number(sp[1])+movement.y))
    			 if(sp[0] == np[0] && sp[1] != np[1]){
    				_this.points.splice(1,1,(Number(np[0])+movement.x)+','+np[1])
    			}else if(sp[0] != np[0]  && sp[1] == np[1]){
    				_this.points.splice(1,1,np[0]+','+(Number(np[1])+movement.y))
    			}
    			if (tp[0] == np[0]) {
    				_this.points.splice(1,1,tp[0]+','+(Number(sp[1])+movement.y))
    			}else if (tp[1] == np[1]){
    				_this.points.splice(1,1,(Number(sp[0])+movement.x)+','+tp[1])
    			}else {
    				if (Math.abs((Number(sp[0])+movement.x)-tp[0])>Math.abs((Number(sp[1])+movement.y)-tp[1])) {
    					_this.points.splice(1,1,tp[0]+','+(Number(sp[1])+movement.y))
    				}else {
    					_this.points.splice(1,1,(Number(sp[0])+movement.x)+','+tp[1])
    				}
    			}
    			_this.polyline.attr('points',_this.points)
    			break;

    		case 2:
    			var ep = _this.points[l-1].split(',')
    			var np = _this.points[l-2].split(',')
    			var tp = _this.points[l-3].split(',')
    			_this.points.splice(l-1,1,(Number(ep[0])+movement.x)+','+(Number(ep[1])+movement.y))
    			if (ep[0] == np[0] && ep[1] != np[1]) {
    				_this.points.splice(l-2,1,(Number(np[0])+movement.x)+','+np[1])
    			}else if(ep[0] != np[0] && ep[1] == np[1]){
    				_this.points.splice(l-2,1,np[0]+','+(Number(np[1])+movement.y))
    			}
    			if (tp[0] == np[0]) {
    				_this.points.splice(l-2,1,tp[0]+','+(Number(ep[1])+movement.y))
    			}else if (tp[1] == np[1]){
    				_this.points.splice(l-2,1,(Number(ep[0])+movement.x)+','+tp[1])
    			}else {
    				if (Math.abs((Number(ep[0])+movement.x)-tp[0])>Math.abs((Number(ep[1])+movement.y)-tp[1])) {
    					_this.points.splice(l-2,1,tp[0]+','+(Number(ep[1])+movement.y))
    				}else {
    					_this.points.splice(l-2,1,(Number(ep[0])+movement.x)+','+tp[1])
    				}
    			}
    			_this.polyline.attr('points',_this.points)
    			break;
    		}
		}
    }
    this.polylinePosResult = function() {
    	if (_this.startPos != '' && _this.endPos != '') {
    		return _this.startPos +',' + _this.endPos
		}else {
			return '';
		}
    }
    // 移除连线
    this.removePolyline=function(){
    	if (!_this.isDeleted) {
    		// 删除线段
        	_this.isDeleted = true;
        	_this.group.remove();
        	// 移除起点和末点
        	_this.startCmpt.circuitMap.delete('startFoot_'+_this.startFoot.attr('id')+'_'+_this.index)
        	_this.endCmpt.circuitMap.delete('endFoot_'+_this.endFoot.attr('id')+'_'+_this.index)
        	
        	//执行删除连线回调
        	$('#sendState').show();
		}
    }
    // 选中连线逻辑
    this.selectedLine=function(){
    	var flag = 0;
    	var points = _this.polyline.attr('points').split(' ')
    	var t = _this.group.transform()
    	points.forEach(function(pt){
    		var p = pt.split(',')
    		if (selectRectBox.inside(Number(p[0])+t.e,Number(p[1])+t.f)){
    			flag++
    		}else {
    			flag--
			}
    	})
    	if (flag >= points.length - 1) {
    		selectPolylines.push(_this)
    		_this.isSelected = true
        	_this.polyline.stroke('#ff0000')
		}
    }
    // 取消选中
    this.unSelected=function(){
    	_this.isSelected = false;
    	_this.polyline.stroke('#800')
    }
}

6 再定义全局操作对象

function  SvgComponent(){
	let _this =this
    // 初始化svg对象
    this.svgroot = svgRoot
    // 基本器件类型
    this.basicCmpt = ['电阻','电容','电感']
    //存放器件
    this.cmpts=[];
    //电路连线
    this.circuits=[];
    //画图方法
    this.draw=function(svgCmpt){
    	svgCmpt.component()
    }
    // 取消全部选中器件
    this.unSelectAll = function(){
    	selectCmpts.forEach(function(cmpt){
    		cmpt.unSelected()
    	})
    	selectPolylines.forEach(function(circuit){
    		circuit.unSelected()
    	})
    	selectCmpts = []
    	selectPolylines = []
    }
    //添加器件
    this.addCmpt=function(cmpt){
        // 初始化器件
	    svgCmpt = new SvgCmpt
	    svgCmpt.id = cmpt.id
	    svgCmpt.moduleId = cmpt.moduleId
	    svgCmpt.displayName = cmpt.displayName
	    svgCmpt.isOpenVCC = cmpt.openVcc >0?true:false;
	    svgCmpt.isOpenVEE = cmpt.openVee >0?true:false;
	    svgCmpt.isOpenGND = cmpt.openGnd >0?true:false;
	    svgCmpt.modulePos = cmpt.pos
	    var footContent = cmpt.cmptSvg.footContent.split('</circle>')
	    var foots = []
	    JSON.parse(cmpt.foots).forEach(function(v,i){
	    	var foot = {}
	    	var el = new DOMParser().parseFromString(footContent[i]+'</circle>','text/html').body.childNodes[0]
	 	    foot['id'] = "node_"+v.node +"_foot_"+v.foot;
	    	foot['index'] = v.node
	    	foot['pos'] = v.foot
	 	    foot['x'] = el.getAttribute('cx');
	 	    foot['y'] = el.getAttribute('cy');
	 	   foots.push(foot)
	    })
//	    svgCmpt.cmptContent = '<path d="M 10 10 h -10" fill="none" stroke="#000fff" stroke-width="1" id="gge1201"></path>,' +
//	    					 '<path d="M 10 20 h -10" fill="none" stroke="#000fff" stroke-width="1" id="gge1202"></path>,' +
//	    					 '<path d="M 10 30 h -10" fill="none" stroke="#000fff" stroke-width="1" id="gge1203"></path>,' +
//	    					 '<path d="M 10 40 h -10" fill="none" stroke="#000fff" stroke-width="1" id="gge1204"></path>,' +
//	    					 '<path d="M 10 50 h -10" fill="none" stroke="#000fff" stroke-width="1" id="gge1205"></path>,' +
//	    					 '<path d="M 10 60 h -10" fill="none" stroke="#000fff" stroke-width="1" id="gge1206"></path>,' +
//	    					 '<path d="M 10 70 h -10" fill="none" stroke="#000fff" stroke-width="1" id="gge1207"></path>,' +
//	    					 '<path d="M 10 80 h -10" fill="none" stroke="#000fff" stroke-width="1" id="gge1208"></path>,' +
//	    					 '<rect id="gge1189" locked="0" c_etype="" x="10" y="0" width="80" height="100" stroke="#000fff" stroke-width="1" fill="#ffffff00" c_shapetype="line"></rect>,' +
//	                        '<path d="M 90 10 h 10" fill="none" stroke="#000fff" stroke-width="1" id="gge1195"></path>,'+
//	                        '<path d="M 90 20 h 10" fill="none" stroke="#000fff" stroke-width="1" id="gge1196"></path>,'+
//	                        '<path d="M 90 30 h 10" fill="none" stroke="#000fff" stroke-width="1" id="gge1197"></path>,'+
//	                        '<path d="M 90 40 h 10" fill="none" stroke="#000fff" stroke-width="1" id="gge1198"></path>,'+
//	                        '<path d="M 90 50 h 10" fill="none" stroke="#000fff" stroke-width="1" id="gge1199"></path>,'+
//	                        '<path d="M 90 60 h 10" fill="none" stroke="#000fff" stroke-width="1" id="gge1200"></path>,'+
//	                        '<path d="M 90 70 h 10" fill="none" stroke="#000fff" stroke-width="1" id="gge1194"></path>,'+
//	                        '<path d="M 90 80 h 10" fill="none" stroke="#000fff" stroke-width="1" id="gge1201"></path>'
	    var svgContent = cmpt.cmptSvg.svgContent
	    if($.inArray(cmpt.cmptItem.itemName,_this.basicCmpt) != -1){
	    	svgCmpt.cmptType = 3
	    	svgCmpt.displayValue = cmpt.proval+cmpt.unit
	    	svgContent = svgContent.replace('"displayName"','"displayeName'+svgCmpt.id+'"').replace('displayValue','displayValue'+svgCmpt.id)
	    }
	    svgCmpt.cmptContent = svgContent  //'<rect x="10" y="10" width="300" height="300" fill="#ffffff00" stroke="#000fff" stroke-width="1"></rect><rect x="150" y="50" width="100" height="200" fill="#ffffff00" stroke="#000fff" stroke-width="1"></rect><path d="M 0 100 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 0 120 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 0 140 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 0 160 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 0 200 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 0 220 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 10 100 h130" fill="none" stroke="#e3c8c8" stroke-width="1"></path><path d="M 10 120 h130" fill="none" stroke="#e3c8c8" stroke-width="1"></path><path d="M 10 140 h130" fill="none" stroke="#e3c8c8" stroke-width="1"></path><path d="M 10 160 h130" fill="none" stroke="#e3c8c8" stroke-width="1"></path><path d="M 140 80 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 140 100 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 140 120 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 140 140 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 140 160 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 70 220 v10" stroke="#000fff" stroke-width="1"></path><path d="M 60 230 h20" stroke="#000fff" stroke-width="1"></path><path d="M 60 240 h20" stroke="#000fff" stroke-width="1"></path><path d="M 70 240 v10" stroke="#000fff" stroke-width="1"></path><path d="M 200 260 v-10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 200 260 v50" fill="none" stroke="#e3c8c8" stroke-width="1"></path><path d="M 200 320 v-10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 250 130 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 260 130 h50" fill="none" stroke="#e3c8c8" stroke-width="1"></path><path d="M 310 130 h10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 40 240 v10" stroke="#000fff" stroke-width="1"></path><path d="M 30 250 h20" stroke="#000fff" stroke-width="1"></path><path d="M 30 260 h20" stroke="#000fff" stroke-width="1"></path><path d="M 40 260 v10" stroke="#000fff" stroke-width="1"></path><path d="M 200 10 v-10" fill="none" stroke="#000fff" stroke-width="1"></path><path d="M 200 10 v40" fill="none" stroke="#e3c8c8" stroke-width="1"></path><path d="M 200 50 v-10" fill="none" stroke="#000fff" stroke-width="1"></path><polyline points="140,80 100,80 100,30 200,30" fill="none" stroke="#e3c8c8" stroke-width="1"></polyline><polyline points="10,200 70,200 70,220" fill="none" stroke="#e3c8c8" stroke-width="1"></polyline><polyline points="10,220 40,220 40,240" fill="none" stroke="#e3c8c8" stroke-width="1"></polyline><polyline points="40,270 40,280 200,280" fill="none" stroke="#e3c8c8" stroke-width="1"></polyline><polyline points="70,240 70,280" fill="none" stroke="#e3c8c8" stroke-width="1"></polyline>'
	    						//+'<text id="foot_name" x="20" y="100" font-family="Times New Roman" font-size="9pt" fill="#000fff">DIS</text><text id="foot_name" x="20" y="120" font-family="Times New Roman" font-size="9pt" fill="#000fff">TH</text><text id="foot_name" x="20" y="140" font-family="Times New Roman" font-size="9pt" fill="#000fff">TR</text><text id="foot_name" x="20" y="160" font-family="Times New Roman" font-size="9pt" fill="#000fff">CV</text><text id="foot_name" x="20" y="200" font-family="Times New Roman" font-size="9pt" fill="#000fff">7</text><text id="foot_name" x="20" y="220" font-family="Times New Roman" font-size="9pt" fill="#000fff">5</text><text id="foot_name" x="155" y="80" font-family="Times New Roman" font-size="9pt" fill="#000fff">RST</text><text id="foot_name" x="155" y="100" font-family="Times New Roman" font-size="9pt" fill="#000fff">DIS</text><text id="foot_name" x="155" y="120" font-family="Times New Roman" font-size="9pt" fill="#000fff">THR</text><text id="foot_name" x="155" y="140" font-family="Times New Roman" font-size="9pt" fill="#000fff">TRI</text><text id="foot_name" x="155" y="160" font-family="Times New Roman" font-size="9pt" fill="#000fff">CON</text><text id="foot_name" x="220" y="130" font-family="Times New Roman" font-size="9pt" fill="#000fff">OUT</text><text id="foot_name" x="290" y="130" font-family="Times New Roman" font-size="9pt" fill="#000fff">VO</text><text id="foot_name" x="190" y="245" font-family="Times New Roman" font-size="9pt"fill="#000fff">GND</text><text id="foot_name" x="205" y="305" font-family="Times New Roman" font-size="9pt"fill="#000fff">GND</text><text id="foot_name" x="50" y="250" font-family="Times New Roman" font-size="6pt"fill="#000fff">C29</text><text id="C29_value" x="50" y="260" font-family="Times New Roman" font-size="6pt"fill="#000fff">0.1uF</text><text id="foot_name" x="80" y="230" font-family="Times New Roman" font-size="6pt"fill="#000fff">C30</text><text id="C30_value" x="80" y="240" font-family="Times New Roman" font-size="6pt"fill="#000fff">0.01uF</text><text id="foot_name" x="205" y="25" font-family="Times New Roman" font-size="9pt"fill="#000fff">VCC</text>'
	    svgCmpt.foots = foots // ['0,100','0,120','0,140','0,160','0,200','0,220','200,320','320,130','200,0']
	    svgCmpt.drawTracker()
	    var size = cmpt.cmptSvg.svgSize.split(',')
	    svgCmpt.size.width = size[0]
    	svgCmpt.size.height = size[1]
	    isAddCmpt = true; // 开启添加器件
	    if (typeof(cmpt.addCallback) == 'function') {
	    	svgCmpt.addCallback = function(){
				cmpt.addCallback()
			}
		}
	    if (typeof(cmpt.delCallback) == 'function') {
	    	svgCmpt.delCallback = function(){
				cmpt.delCallback()
			}
		}
	    activeCmpt = svgCmpt;
    }
    this.addVss = function(type,text,callback){
    	svgCmpt = new SvgCmpt
    	svgCmpt.size.width = 60
    	svgCmpt.size.height = 50
    	svgCmpt.cmptType = 1
    	svgCmpt.id = type
    	switch (type) {
		case 'VCC':
			svgCmpt.cmptContent = '<rect id="ggrect_gnd" x="0" y="0" width="60" height="50" stroke="#ffffff00" stroke-width="1" fill="#ffffff00"></rect>'+
								  '<text x="15" y="5" font-family="Times New Roman" font-size="9pt" fill="#000fff" id="gge1257">+5V</text>'+
								  '<path d="M 5 10 h 50" fill="none" stroke="#000fff" stroke-width="1" id="gge1259"></path>' +
								  '<path d="M 30 10 v 40" fill="none" stroke="#000fff" stroke-width="1" id="gge1258"></path>'							
			svgCmpt.foots = [{
				id: "node_201_foot_vcc",index:0,pos:201,x:30,y:50
			}]
			svgCmpt.modulePos = 201
			break;
		case 'VEE':
			svgCmpt.cmptContent = '<rect id="ggrect_gnd" x="0" y="0" width="60" height="50" stroke="#ffffff00" stroke-width="1" fill="#ffffff00"></rect>'+
								  '<text x="15" y="5" font-family="Times New Roman" font-size="9pt" fill="#000fff" id="gge1287">-5V</text>'+
								  '<path d="M 5 10 h 50" fill="none" stroke="#000fff" stroke-width="1" id="gge1289"></path>' +
								  '<path d="M 30 10 v 40" fill="none" stroke="#000fff" stroke-width="1" id="gge1299"></path>'							
			svgCmpt.foots = [{
					id: "node_202_foot_vee",index:0,pos:202,x:30,y:50
			}]
			svgCmpt.modulePos = 202
			break;
		case 'GND':
			svgCmpt.cmptContent = 	'<rect id="ggrect_gnd" x="0" y="0" width="60" height="60" stroke="#ffffff00" stroke-width="1" fill="#ffffff00"></rect>'+
									'<path d="M 30 0 v 30" fill="none" stroke="#000fff" stroke-width="1" id="gge1268"></path>' +
									'<path d="M 5 30  h 50" fill="none" stroke="#000fff" stroke-width="1" id="gge1269"></path>'+
									'<path d="M 15 40 h 30" fill="none" stroke="#000fff" stroke-width="1" id="gge1270"></path>'+
									'<path d="M 20 50 h 20" fill="none" stroke="#000fff" stroke-width="1" id="gge1271"></path>'
			svgCmpt.foots = [{
				id: "node_203_foot_gnd",index:0,pos:203,x:30,y:0
			}]
			svgCmpt.modulePos = 203
			break;
		}
	    isAddCmpt = true; // 开启添加器件
		if (typeof(callback) == 'function') {
			svgCmpt.addCallback = function(){
				callback()
			}
		}
		if (typeof(callback) == 'function') {
			svgCmpt.delCallback = function(){
				callback()
			}
		}
		activeCmpt = svgCmpt;
    }
    // 添加仪器
    this.addInstr = function(instr){
    	svgCmpt = new SvgCmpt
    	svgCmpt.id = instr.type
    	svgCmpt.instr = instr
    	var x = 25
    	switch (instr.name) {
		case '示波器':
		case '信号源':
			x = 10;
			break;
		}
    	svgCmpt.cmptContent = '<rect id="ggrect_in" x="0" y="0" width="100" height="100" stroke="#ffffff00" stroke-width="1" fill="#ffffff00"></rect>'+
		'<rect id="ggrect_1" x="2" y="2" width="96" height="76" stroke="#000fff" stroke-width="1" fill="#ffffff00" ></rect>'+
		'<text id="ggtext_in" x="'+x+'" y="40" font-family="Times New Roman" font-size="20pt" fill="#000fff" >'+instr.name+'</text>'+
		'<path d="M 20 60 v 40" fill="none" stroke="#000fff" stroke-width="1" id="ggpath_1"></path>'+
		'<path d="M 40 60 v 40" fill="none" stroke="#000fff" stroke-width="1" id="ggpath_2"></path>'+
		'<path d="M 60 60 v 40" fill="none" stroke="#000fff" stroke-width="1" id="ggpath_3"></path>'+
		'<path d="M 80 60 v 40" fill="none" stroke="#000fff" stroke-width="1" id="ggpath_4"></path>'
    	svgCmpt.size.width = 100
    	svgCmpt.size.height = 100
    	svgCmpt.cmptType = 2
		switch (instr.type) {
    	// 输入
		case 'in':
			svgCmpt.modulePos = 193
			break;
    	// 输出
    	case 'out':
    		svgCmpt.modulePos = 197
			break;
    	}
    	for (var i = 0; i < 4; i++) {
			var foot ={}
			foot ={id: 'node_'+i+'_foot_'+instr.type,index:i,pos:svgCmpt.modulePos,x:20+i*20,y:100}
			svgCmpt.foots.push(foot)
		}
		isAddCmpt = true; // 开启添加器件
		if (typeof(instr.addCallback) == 'function') {
			svgCmpt.addCallback = function(){
				instr.addCallback()
			}
		}
		if (typeof(instr.delCallback) == 'function') {
			svgCmpt.delCallback = function(){
				instr.delCallback()
			}
		}
		activeCmpt = svgCmpt;
    }
    //删除器件方法
    this.removeCmpt=function(SvgCmpt){
        SvgCmpt.isDelFlag = true;
    }
    //添加连线方法
    this.addCircuit=function(circuit){
        this.circuits.push(circuit)
    }
    //删除连线方法
    this.removeCircuit=function(i){
    	this.circuits.splice(i,1)
    }
    // 开启连线
    this.createRootPath=function(e,circle,cmpt){
        isComplatePath = false;
        // 偏移坐标
        var t = circle.parent().parent().transform()
        // 绝对坐标
        var x = circle.attr('cx') + t.e , y =circle.attr('cy') + t.f;
        var groupId = uuid19()
        var group = polylineGroup.group().id(groupId)
        var circuit = new SvgCircuit();
        circuit.startFoot = circle;
        circuit.startPos = circle.attr('id').split('_')[1]
        circuit.startCmpt = cmpt
        circuit.groupId = groupId
        var polyline = svgRoot.polyline(x+','+y+' '+(x+10)+','+y+' '+(x+10)+','+(y+10)).stroke('#800').fill('none').stroke({ width: 1 })
        group.add(polyline);
        circuit.group = group
        currentFootTransform = t;
        currentPolyline = polyline
        currentPolylineObj = circuit;
    }
    // 完成连线
    this.finishLine=function(isSuccess){
    	if (isSuccess) {
    		 // 连线编辑功能
    		var p = currentPolyline
    		var o = currentPolylineObj
    		p.on('mouseup',function(e){
    			isEditPolyline = true
    			p.stroke('#ff0000')
    			editPolyLine = o 
    		})
    	    p.on('mouseover',function(e){
    	    	if (!isMulSelect) {
    	    	selectPolylines.push(o)
    	    	p.stroke('#ff0000')
    	     }
    	    })
    	    p.on('mouseout',function(e){
    	    	if (!isMulSelect && !isEditPolyline) {
    	    		selectPolylines = []
    		    	p.stroke('#800')
    			}
    	    })
    	    //执行添加连线回调
    	    $('#sendState').show();
    	}
        crossLine_X.plot('M0 0')
        crossLine_Y.plot('M0 0')
        isComplatePath = true;
        currentFootTransform = null;
        currentPolyline = null;
        currentPolylineObj = null;
    }
 // 连线优化函数
    this.optimizePolyline=function(points){
    	var l = points.length
    	for (var i = 1; i < l-4;i++) {
    			var preP =  points[i].split(',')
    	    	var midP = points[i+1].split(',')
    	    	var aP = points[i+2].split(',')
    	    	// 删除无用的中间点
    	    	if ((preP[0] == midP[0] && midP[0]  == aP[0]) || (preP[1] == midP[1] && midP[1]== aP[1])) {
    	    		points.splice(i+1,1)
    			}
    	}
    	return points;
    }
    //重置画布方法
    this.resetPage=function(){
    	svgRootGroup.clear()
    	selectCmpt = null; // 选中器件列表
    	selectCmpts = []; // 选中多个器件
    	selectLines = null; // 选中的连线
    	selectPolylines = []; // 选中多条连线
    	originScale = 1; // 原始比例
    	isComplatePath = true;  // 绘制连线
    	isSelectBox = false;// 矩形拉选
    	isMulSelect = false;// 是否处于多选状态
    	isMoveMulSelect = false; // 是否开启批量移动
    	changeLineX = false; // 连线X变向
    	changeLineY = false; // 连线Y变向
    	isAddCmpt = false; // 是否处于添加器件状态
    	activeCmpt = null; // 刚添加器件
    	svgRoot.attr('transform','scale('+originScale+')') // 初始化画板比例
    	_this.cmpts.forEach(function(cmpt){
    		if (typeof(cmpt.delCallback) == 'function') {
    			cmpt.delCallback()
    		}
    	})
    	_this.cmpts.splice(0)
    	_this.circuits.splice(0)
		defs = svgRoot.defs()
		svgRootGroup.add(gridRect)
		polylineGroup = svgRootGroup.group().id('polylinegroup')
		_this.finishLine(false)
	}
}
 类似资料: