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

SVG.js+扩展工具(svg.select.js+svg.resize.js+svg.draggable.js)联合开发画图工具

戚逸清
2023-12-01

SVG.js+扩展工具(svg.select.js+svg.resize.js+svg.draggable.js)联合开发画图工具

先介绍各个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)
下侧:操作提示框

文件准备,引入js文件

// 先导入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>

前端框架easyUI搭建页面

  <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>

main.js

在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 = {}; // 当前点击的点坐标

构造网格线,10为单位

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()

画板响应事件

画板的响应事件应该对应有:
mousedownmouseup(移动结束或连线结束)、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()
	...
}

以上代码非完整,请勿全部复制粘贴,仅提供大体的结构思路,欢迎指正和讨论。楼主已经做出了一个版本了(正常使用),这是之前构思的初稿,基本思路没有问题。

 类似资料: