很久没有写博客了,贴上这个月前一段关于动态创建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%' οnlοad='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后台开发,前端兴趣所至,写的不好的地方,请指教