很久没有写博客了,贴上这个月前一段关于动态创建svg的js库吧。
/** SVGObject 库针对于SVG封装部分功能 为SVG脚本开发提供方便,后期会加入 于VML兼容的封装,实现IE下面可以正 常的执行 @author: 追本溯源 @time: 2015-5-5 */ SVGObject = {version: '1.1'} SVGObject.SVG_NS = 'http://www.w3.org/2000/svg'; SVGObject.XLINK_NS = "http://www.w3.org/1999/xlink"; SVGObject.MAP_TRANS = { //一些特殊的属性需要替换 svgclass: 'class', svghref: 'href' } SVGObject.ARROW_ID = 'arrow_id'; SVGObject.get = function(el) { el = typeof el == 'string'? document.getElementById(el) : el; var svgEl = SVGObject.Element.prototype.findSpecilSvgElement(el); svgEl == null? svgEl = new SVGObject.Element(el) : svgEl; return svgEl; } SVGObject.svgHelper = function() { function getIdByDate() { var date = new Date(); return 'svg_' + date.getTime(); }; function cloneObj(datas) { //数组对象涉及深拷贝 var values; if(SVGObject.isObject(datas) || SVGObject.isArray(datas)) { values = SVGObject.isArray(datas)? [] : {}; } for(var key in datas) { values[key] = datas[key]; } return values; } /** 将原生的svg元素注册成封装的SVGElemeng*/ function regToElement(el) { var svgEl = SVGObject.Element.prototype.findSpecilSvgElement(el); if(svgEl == null) { svgEl = new SVGObject.Element(el); } return svgEl; }; return { reg2Element: regToElement, getId: getIdByDate, clone: cloneObj } }(); SVGObject.apply = function(attr1, attr2) { attr1 = attr1 || {}; if(SVGObject.isObject(attr2)) { //浅拷贝 for(var key in attr2) { attr1[key] = attr2[key]; } } return attr1; } SVGObject.isObject = function(attr1) { return Object.prototype.toString.call(attr1) == '[object Object]'; } SVGObject.isArray = function(attr) { return Object.prototype.toString.call(attr) == '[object Array]'; } SVGObject.extend = function(attr1, attr2, attr3) { if(SVGObject.isObject(attr2)) { attr3 = attr2; attr2 = attr1; attr1 = attr3.constructor != Object.prototype.constructor? attr3.constructor : function() { attr2.apply(this, arguments); } } var tempFn = function() {}; tempFn.prototype = attr2.prototype; /** 采用中间方法防止attr2.prototype上属性被修改*/ attr1.prototype = new tempFn(); attr1.prototype.constructor = attr1; attr1.prototype.superclass = attr2.prototype; SVGObject.apply(attr1.prototype,attr3); attr1.prototype.apply = function(obj) { SVGObject.apply(attr1,obj); } // add more code here return attr1; } SVGObject.ComponentList = { } /** 组件基类,所有图形都是基于该方法 <rect id='moban_rect' x='0' y='0' width='50' height='30' fill='blue' fill-opacity='0.5'></rect> */ SVGObject.Component = function() { //数据拷贝,整理 this.datas = SVGObject.svgHelper.clone(arguments[0] || {}); SVGObject.apply(this,this.datas); this.initComponent(); //this.addEvents('svgclick', 'svgmousemove', 'svgdblclick', 'svgmouseenter', 'svgmouseout'); //this.superclass.addElements(this); SVGObject.Element.components[this.id] = this.cEl; //将该对象存放到一个集合 //组件插入 if(this.renderTo) { this.render(this.renderTo); delete this.renderTo; //为的是不让下面设置参数的时候获取到该参数 } this.addAttribute(this.datas); } SVGObject.apply(SVGObject.Component.prototype, { addAttribute: function(arg) { if(this.cEl == null || this.cEl == 'undefined') { //必须给该组件创建相应的SVGElement 才能增加属性 return; } if(typeof arguments[0] == 'string') { //暂时为 object 类型和 2个参数string类型的属性提供该功能 var ary = []; arg = ary[argument[0]] = arguments[1]; } for(key in arg) { this.cEl.addAttributeForEl(key,arg[key]); //具体的校验到SVGObject.Element提供的方法里面进行 } }, initComponent: function() { this.id = this.id? this.id : SVGObject.svgHelper.getId(); /** 一些初始化属性需要在下面执行才能让正常运行*/ if(this.cEl == 'undefined' || this.cEl == null) { var el = document.createElementNS(SVGObject.SVG_NS, this.typeName); this.cEl = SVGObject.svgHelper.reg2Element(el); } }, getAttribute: function() { }, render: function(renderTo) { if(renderTo == null || renderTo == 'undefined') { return ; } this.renderTo = SVGObject.svgHelper.reg2Element(renderTo); this.renderTo.dom.appendChild(this.cEl.dom); } }) SVGObject.EventManager = function() { return { attachListener: function(targetEl, ename, callbackFtn) { targetEl.addEventListener(ename, callbackFtn, false); }, fireEvents: function(ename,element, evt) { var cacheElObj = this.findSpecilElCache(element); //this指的是下边fireEvents.call()第一个参数作为作用域 var listeners = cacheElObj.listeners[ename]; for(var index = 0; index < listeners.length; index++) { var listen = listeners[index]; element.firing = true; if(listen.call(element, evt) === false) { element.firing = false; return false; } } element.firing = false; return true; } }; }() SVGObject.Element = function(el) { el = typeof el == 'string' ? SVGObject.getSVGEl().dom.getElementById(el) : el; if(el && !this.hasTargetElCache(el)) { this.addElCache(el); } var temp = SVGObject.Element.svgElements[0]; this.id = el.id ; this.dom = el ; } SVGObject.Element.elCache = []; SVGObject.Element.svgElements = []; SVGObject.Element.components = {}; SVGObject.apply(SVGObject.Element.prototype, { addElCache: function(element) { //el为原生的svg元素 var nt = element.nodeName; var obj = { nametype: nt, el: element, data: [], attrs: {}, //这个el所包含的属性 listeners: {} } SVGObject.Element.elCache.push(obj); SVGObject.Element.svgElements.push(this); SVGObject.Element.components[this.id] = this; }, addListener: function(ename, eFn, scope, option) { //SVGObject.get('id').addListener('click',ftn); var tempthis = this; /*tempthis.events = tempthis.events || {}; var listeners = tempthis.events[ename] || []; */ scope = scope || tempthis; var targetEl = tempthis.dom; function callbackFtn(evt) { SVGObject.EventManager.fireEvents.call(tempthis,ename,tempthis.dom,evt); } this.addListenerAtElCache(ename, eFn); SVGObject.EventManager.attachListener(targetEl, ename, callbackFtn); }, hasTargetElCache: function(element) { var len = SVGObject.Element.elCache.length; var array = SVGObject.Element.elCache; for(var index = 0; index < length; index++) { var obj = array[index]; if(obj.el == element) { return true; } } return false; }, findSpecilElCache: function(element) { element = element || this; var addr = SVGObject.Element.prototype.findSpecilElCacheAddr(element); if(addr == null) { return null; } return SVGObject.Element.elCache[addr]; }, findSpecilElCacheAddr: function(element) { element = element || this; if(element.findSpecilElCacheAddr) { //如果是SVGElement类型 element = element.dom; } var len = SVGObject.Element.elCache.length; var array = SVGObject.Element.elCache; for(var index = 0; index < len; index++) { var obj = array[index]; if(obj.el == element) { return index; } } return null; }, findSpecilSvgElement: function(element) { element = element || this; if(element.findSpecilSvgElement) { return element; } var addr = SVGObject.Element.prototype.findSpecilElCacheAddr(element); if(addr == null) { return null; } return SVGObject.Element.svgElements[addr]; }, addListenerAtElCache: function(ename,eFn) { var cacheElObj = this.findSpecilElCache(); if(cacheElObj == null) { return ; } cacheElObj.listeners[ename] = cacheElObj.listeners[ename] || []; if(this.firing) { cacheElObj.listeners[ename] = Array.prototype.slice.call(cacheElObj.listeners[ename],0); } cacheElObj.listeners[ename].push(eFn); }, addAttributeForEl: function(attr, value) { if(/^svghref$/.test(attr)) { // 对于href这种属性,前面有命名空间,这里暂时录入href attr = attr in SVGObject.MAP_TRANS ? SVGObject.MAP_NS[attr] : attr; this.addAttributeNSForEl(attr,value) }else { attr = attr in SVGObject.MAP_TRANS ? SVGObject.MAP_NS[attr] : attr; this.dom.setAttribute(attr, value); } }, addAttributeNSForEl: function(attr, value) { attr = attr in SVGObject.MAP_TRANS ? SVGObject.MAP_NS[attr] : attr; this.dom.setAttribute(SVGObject.XLINK_NS, attr, value); }, removeCurrentEl: function(el) { var parentEl = el.dom; parentEl.removeChild(this.dom); } }); SVGObject.SVGRect = SVGObject.extend(SVGObject.Component,{ initComponent: function() { //重写基类初始化方法 2015/5/6 this.typeName = this.typeName || 'rect'; SVGObject.SVGRect.prototype.superclass.initComponent.apply(this,arguments); /** 子类间的差异性 */ }, addAttribute: function(argu) { //重写基类属性添加方法 2015/5/6 SVGObject.SVGRect.prototype.superclass.addAttribute.apply(this,arguments); }, render: function(svgEl) { SVGObject.SVGRect.prototype.superclass.render.call(this,svgEl); } }) /** Svg线条元素 2015/5/7。 */ SVGObject.SVGLine = SVGObject.extend(SVGObject.Component, { initComponent: function() { this.typeName = this.typeName || 'line'; SVGObject.SVGLine.prototype.superclass.initComponent.apply(this,arguments); /** 子类间的差异性 */ }, addAttribute: function(argu) { //重写基类属性添加方法 2015/5/6 SVGObject.SVGLine.prototype.superclass.addAttribute.apply(this,arguments); }, render: function(svgEl) { SVGObject.SVGLine.prototype.superclass.render.call(this,svgEl); } }); /** SVG path 元素封装 @time 2015/5/7 */ SVGObject.SVGPath = SVGObject.extend(SVGObject.Component, { initComponent: function() { this.typeName = this.typeName || 'path'; SVGObject.SVGPath.prototype.superclass.initComponent.apply(this,arguments); /** 子类间的差异性 */ }, addAttribute: function(argu) { //重写基类属性添加方法 2015/5/6 SVGObject.SVGPath.prototype.superclass.addAttribute.apply(this,arguments); }, render: function(svgEl) { SVGObject.SVGPath.prototype.superclass.render.call(this,svgEl); } }); /** SVG defs 元素封装 @time 2015/5/7 */ SVGObject.SVGDefs = SVGObject.extend(SVGObject.Component, { initComponent: function() { this.typeName = this.typeName || 'defs'; SVGObject.SVGDefs.prototype.superclass.initComponent.apply(this,arguments); /** 子类间的差异性 */ }, addAttribute: function(argu) { //重写基类属性添加方法 2015/5/6 SVGObject.SVGDefs.prototype.superclass.addAttribute.apply(this,arguments); }, render: function(svgEl) { SVGObject.SVGDefs.prototype.superclass.render.call(this,svgEl); } }); /** SVG marker元素封装 */ SVGObject.SVGMarker = SVGObject.extend(SVGObject.Component, { initComponent: function() { this.typeName = this.typeName || 'marker'; SVGObject.SVGMarker.prototype.superclass.initComponent.apply(this,arguments); /** 子类间的差异性 */ }, addAttribute: function(argu) { //重写基类属性添加方法 2015/5/6 SVGObject.SVGMarker.prototype.superclass.addAttribute.apply(this,arguments); }, render: function(svgEl) { SVGObject.SVGMarker.prototype.superclass.render.call(this,svgEl); } }); /** SVG自定义封装的箭头组合图形 @time 2015/5/8 */ SVGObject.SVGArrow = SVGObject.extend(SVGObject.Component, { initComponent: function() { this.arrowEl = this.arrowEl || SVGObject.Element.components[SVGObject.ARROW_ID]; if(this.arrowEl == 'undefined' || this.arrowEl == null) { this.defsEl = this.defEl? this.defEl : new SVGObject.SVGDefs({ renderTo: document.documentElement }).cEl; this.markerEl = this.markerEl? this.markerEl : new SVGObject.SVGMarker({ renderTo: this.defsEl, id: SVGObject.ARROW_ID, refX: '0', refY: '0', markerWidth: '15', markerHeight: '15', orient: 'auto', viewBox: '0,-3,10,6' }).cEl; this.path = this.path? this.path : new SVGObject.SVGPath({ renderTo: this.markerEl, d: 'M0 -3 L0 3 L10 0', fill: this.fill || 'black', 'stroke-width': '0', style: this.style || '' }).cEl; } //<line class="link" marker-end="url(#arrow)" x1="0" y1="0" x2="80" y2="80" stroke='black' fill='black' stroke-width='1' ></line> this.typeName = this.typeName || 'line'; this.renderTo = this.renderTo || document.documentElement; SVGObject.SVGArrow.prototype.superclass.initComponent.apply(this,arguments); /** 子类间的差异性 */ }, addAttribute: function(argu) { //重写基类属性添加方法 2015/5/6 argu.x1 = this.arrowX1; delete argu.arrowX1; argu.y1 = this.arrowY1; delete argu.arrowY1; argu.x2 = this.arrowX2; delete argu.arrowX2; argu.y2 = this.arrowY2; delete argu.arrowY2; argu['marker-end'] = 'url(#' + SVGObject.ARROW_ID + ')'; // <line class="link" marker-end="url(#arrow)" x1="0" y1="0" x2="80" y2="80" stroke='black' fill='black' stroke-width='1' ></line> SVGObject.SVGArrow.prototype.superclass.addAttribute.call(this, argu); }, render: function(svgEl) { SVGObject.SVGArrow.prototype.superclass.render.call(this,svgEl); } });
xml调用方式
<?xml version="1.0" standalone="no"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg width='100%' height='100%' onload='init(evt)' version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink='http://www.w3.org/1999/xlink'> <script xlink:href="SVGObject.js" language="JavaScript"/> <script type="text/ecmascript"><![CDATA[ var init = function() { var sharp; var curEl, preEl; //当前El 上一个El var qx,qy,zx,zy; var mousedown; var svgDoc,svgRoot,moban_rect,moban_circle,moban_line; //工具模板颜色释放 var releaseOther = function(cs1,cs2) { cs2 = typeof cs2 == 'string'? SVGObject.get(cs2) : cs2; var childNodes = cs2.dom.childNodes; for(var key in childNodes) { if(childNodes[key].nodeType == 1) { if(childNodes[key].id != cs1) { if(childNodes[key].id == 'moban_arrow') { childNodes[key].setAttribute('fill','blue'); document.getElementById('moban_arrow1').setAttribute('stroke','blue'); return; } childNodes[key].setAttribute('fill','blue'); }else { if(childNodes[key].id == 'moban_arrow') { document.getElementById('moban_arrow1').setAttribute('stroke','black'); childNodes[key].setAttribute('fill','black'); }else { childNodes[key].setAttribute('fill','black'); } } } } } //初始化相关属性 constructorOne = function(evt) { svgRoot = SVGObject.get(document.documentElement); moban_rect = SVGObject.get('moban_rect'); moban_yuanjiao = SVGObject.get('moban_yuanjiao'); moban_arrow = SVGObject.get('moban_arrow1'); } return function(evt) { constructorOne(evt); moban_rect.addListener('click',function() { releaseOther('moban_rect', 'moban'); sharp = 'rect'; }); moban_yuanjiao.addListener('click',function() { releaseOther('moban_yuanjiao', 'moban'); sharp = 'yuanjiao'; }); moban_arrow.addListener('click', function() { releaseOther('moban_arrow', 'moban'); sharp = 'arrow'; }); svgRoot.addListener('mousedown',function(evt) { qx = evt.clientX; qy = evt.clientY; mousedown = true; }); svgRoot.addListener('mousemove', function(evt) { var flag1 = sharp == undefined || sharp == '' || sharp == null || sharp == 'undefine'; //有图形且点击 if(!sharp || !mousedown ) { return ; //上边判断curEl是防止,画好一个元素,sharp不为空,继续移动而可以执行下面代码 } if(preEl) { //null undefined都为false preEl.removeCurrentEl(svgRoot); //js库SVGObject提供移除的方法 } zx = evt.clientX; zy = evt.clientY; switch(sharp) { case 'rect': curEl = new SVGObject.SVGRect({ //SVGObject库提供计算点到点距离的方法 'x': qx, 'y': qy, 'width': zx - qx, 'height': zy - qy, 'style': 'stroke: "blue"; stroke: "1"; fill: "black" ' }); break; case 'yuanjiao': curEl = new SVGObject.SVGRect({ //SVGObject库提供计算点到点距离的方法 'x': qx, 'y': qy, 'rx': '20', 'ry': '20', 'width': zx - qx, 'height': zy - qy, 'style': 'stroke: "blue"; stroke: "1"; fill: "black" ' }); break; case 'arrow': curEl = new SVGObject.SVGArrow({ //SVGObject库提供计算点到点距离的方法 'arrowX1': qx, 'arrowY1': qy, 'arrowX2': zx, 'arrowY2': zy, 'style': 'fill: black', 'stroke': 'black', 'stroke-width': '1' }); break; } curEl.render(svgRoot.dom); //SVGObject类库中重载父类render() 实现各自的功能 preEl = curEl.cEl; }); svgRoot.addListener('mouseup', function() { preEl = null; curEl = null; mousedown = false; }); } }(); ]]></script> <defs id='moban' > <rect id='moban_rect' x='0' y='0' width='50' height='30' fill='blue' fill-opacity='0.5'></rect> <rect id='moban_yuanjiao' x='60' y='0' rx='7' ry='7' width='50' height='30' fill='blue' fill-opacity='0.5'></rect> <marker id="moban_arrow" refX="0" refY="0" markerWidth="15" markerHeight="15" orient="auto" viewBox="0, -3, 10, 6" > <path d="M0 -3 L0 3 L10 0" stroke-width='0'></path> </marker> </defs> <use xlink:href='#moban_rect'></use> <use xlink:href='#moban_yuanjiao'></use> <line id="moban_arrow1" marker-end="url(#moban_arrow)" x1="120" y1="15" x2="150" y2="15" stroke='blue' fill='blue' stroke-width='2' ></line> </svg>
上边的js库看起来很熟悉是吗,其实我只读了Ext一部分源码,思维被影响了,应该读更多的优秀的js库的。我现在工作是基于java后台开发,前端兴趣所至,写的不好的地方,请指教