Posted in Javascript onJanuary 12, 2011
因为编辑器会对代码有所破坏,所以提供一个在线演示
http://demo.3water.com/chengxu/smipleChart.htm
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>smipleChart</title> <style type="text/css"> .cc{ height:450px; width:800px; border:1px solid #999; position:relative; margin:20px; } </style> </head> <body> <div id='t'></div> <div id='t1'> </div> <div id='line' class="cc"></div> <div id='area' class="cc"></div> <div id='zhu' class="cc"></div> <div id='zhu1' class="cc" style="height:600px;"></div> <div id='pie' class="cc"></div> <div id='pies' class="cc"></div> <div id='segment' class="cc"></div> <div id='vv' class="cc" style='height:300px; width:520px;'></div> <script type="text/javascript"> (function(doc,undefined){ var win = this, hasSVG = win.SVGAngle || doc.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure", "1.1"), isIE = /msie/i.test(navigator.userAgent) && !win.opera, path = hasSVG?'d':'path', seal = hasSVG?'z':'e', math = Math, mathRound = math.round, mathFloor = math.floor, mathCeil = math.ceil, mathMax = math.max, mathMin = math.min, mathAbs = math.abs, mathCos = math.cos, mathSin = math.sin, M = 'M', L = 'L'; win.$ = function(Id){ return document.getElementById(Id); }; win.extend = function(){ var target = arguments[0] || {}, i = 1, length = arguments.length, deep = true, options; if ( typeof target === "boolean" ) { deep = target; target = arguments[1] || {}; i = 2; } if ( typeof target !== "object" && Object.prototype.toString.call(target)!="[object Function]") target = {}; for(;i<length;i++){ if ( (options = arguments[ i ]) != null ) for(var name in options){ var src = target[ name ], copy = options[ name ]; if ( target === copy ) continue; if ( deep && copy && typeof copy === "object" && !copy.nodeType ){ target[ name ] = arguments.callee( deep, src || ( copy.length != null ? [ ] : { } ), copy ); } else if(copy !== undefined) target[ name ] = copy; } } return target; }; win.each = function ( object, callback, args ) { var name, i = 0, length = object.length; if ( args ) { args = Array.prototype.slice.call(arguments).slice(2); if ( length === undefined ) { for ( name in object ) if ( callback.apply( object[ name ],[name,object[ name ]].concat(args) ) === false ) break; } else for ( ; i < length; i++) if ( callback.apply( object[ i ],[i,object[ i ]].concat(args)) === false ) // break; } else { if ( length === undefined ) { for ( name in object ) if ( callback.call( object[ name ], name, object[ name ] ) === false ) break; } else for ( var value = object[0]; i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} } return object; }; //--------------------------------------------------------------- function processPoint( x ){ return isIE ? ~~x.toFixed(0) : ~~x.toFixed(0) + 0.5; }; function calTextLen(txt, cssStr){ var span = doc.createElement('span'); if(cssStr){ typeof cssStr === 'string' ? span.style.cssText = cssStr : extend(span.style,cssStr); }else{ extend(span.style,{ fontSiz : '12px', fontFamily : '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif' }); } span.innerHTML = txt || ''; span.style.visibility = 'hidden'; doc.body.appendChild(span); var width = span.offsetWidth, height = span.offsetHeight; doc.body.removeChild(span); return {w:width,h:height}; }; function angle(r,center,o,jingdu){ var hudu = Math.PI*2*(o/360), x = center[0]+ r*Math.sin(hudu), y = center[1]+ -r*Math.cos(hudu); return [x.toFixed(jingdu||0),y.toFixed(jingdu||0)]; } function xx(a,b,lineNum){ var t = 10000; var stf = ((b*t-a*t)/lineNum)/t, arr = [1,2,2.5,5,10], c = 1, v; // 分割线的基数是 [1,2,2.5,5,10] 这个步骤是查找 间隔 属于哪个范围 if(stf<arr[0]){ while( stf<arr[0] ){ c = c*10; arr[0]=arr[0]/c; } each([1,2,2.5,5,10],function(i,o){ arr[i]= o/c; }); }else if(stf>arr[4]){ while( stf>arr[4] ){ c = c*10; arr[4] = arr[4]*c; } each([1,2,2.5,5,10],function(i,o){ arr[i]= o*c; }); } //上面找到间隔后 找到间隔中最接近的一个 each(arr,function(i,o){ if(stf<o){ v = o; return false; } }); //a 是最小的 //b是最大 的 a = (a*t-mathAbs((a%v)*t))/t; b = (b*t+(b%v===0?0:(v-b%v))*t)/t; //看看还剩几条线没有画 var num = Math.max(0,lineNum - Math.round((b-a)/v)); if(a>=0){ //让图比较靠中间 /*while(num!==0){ num%2===0 ? a = (a*t-v*t)/t : b = (b*t+v*t)/t; num--; }*/ //坐标比较整数化 if(a!=0&&num!=0){ while(a!=0&&num!=0){ a = (a*t-v*t)/t; num--; if((a*t-v*num*t)/10000>0&&a%10===0) break; } } if(num!=0){ while(num!==0){ b = (b*t+v*t)/t num--; } } }else{ //让图比较靠中间 /*while(num!==0){ num%2===0 ? b = (b*t+v*t)/t : a = (a*t-v*t)/t num--; }*/ //坐标比较整数化 if(b<0&&num!=0){ while(b!=0&&num!=0){ b = (b*t+v*t)/t; num--; if((b*t+v*num*t)/t<0&&a%10===0) break; } } if(num!=0){ while(num!==0){ a = (a*t-v*t)/t num--; } } } return {min:a,max:b,stf:v}; } //--------------------------------------------------------------------------------------------------------------- //对svg vml元素的一些创建 修改属性 样式 删除 == 一些的操作 win.vector = function(){}; win.vector.prototype = { $c : function(graphic,nodeName){ this.element = this[0] = doc.createElementNS('http://www.w3.org/2000/svg', nodeName); this.graphic = graphic; return this; }, attr: function(hash,val){ var elem = this.element, key, value; if(typeof hash === 'string'){ if(val === undefined){ return elem.getAttribute(hash); }else{ elem.setAttribute(hash, val); return this; } } else { for(key in hash){ value = hash[key]; if(key === path){ value && value.join &&(value = value.join(' ')); /(NaN| |^$)/.test(value) &&(value = 'M 0 0'); } elem.setAttribute(key, value) } } return this; }, css: function(hash){ var str = ''; for(var key in hash){ if(isIE && key == "opacity"){ this.element.style['filter'] = "alpha(opacity="+ hash[key] * 100+")"; }else{ this.element.style[key] = hash[key]; } } return this; }, on: function(eventName, handler){ var self = this; this.element.addEventListener(eventName,function(){ handler.call(self) },false); return this; }, appendTo: function(parent){ if(parent){ parent.element ? parent.element.appendChild(this.element) : parent.appendChild(this.element) } else { this.graphic.container.appendChild(this.element); } return this; }, addText: function(str){ var elem = this.element; if(elem.nodeName === 'text'){ elem.appendChild(doc.createTextNode(str.toString() || ' ')); } return this; }, setOpacity : function(v){ this.attr('fill-opacity',v) }, toFront: function() { this.element.parentNode.appendChild(this.element) return this; }, show: function(){ this.element.style.display = 'block'; return this; }, hide: function(){ this.element.style.display = 'none'; return this; } }; //--------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------- //如果是vml修改其中的一些方法 if(!hasSVG){ //-------------创建vml环境----------------- doc.createStyleSheet().addRule(".vml", "behavior:url(#default#VML);display:inline-block;position:absolute;left:0px;top:0px"); !doc.namespaces.vml && !+"\v1"; doc.namespaces.add("vml", "urn:schemas-microsoft-com:vml"); //-------------修改一些方法----------------- extend(vector.prototype,{ $c : function(graphic,nodeName){ var name = nodeName || 'shape'; this.element= this[0] = (name === 'div' || name === 'span') ? doc.createElement(name) : doc.createElement('<vml:'+name+' class="vml">'); this.graphic = graphic; return this; }, on : function(eventName, handler){ var self = this; this.element.attachEvent("on" + eventName,function(){ handler.call(self); }); return this; }, addText : function(txt){ var elem = this.element; elem.innerHTML = txt || ''; return this; }, setOpacity : function(v){ this.opacity.opacity=v; } }); } //--------------------------------------------------------------------------------------------------- //画图类 //------------------------------------------------------------ win.smipleChart = function(){ this.init.apply(this,arguments); }; smipleChart.prototype = { options : { charts : { paddingRight : 20, radius : 200, style : { fontFamily : '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', fontSize : '12px', background : '#FFFFFF' } }, title : { text : '', y : 10, style : { fontFamily:'Verdana,Arial,Helvetica,sans-serif', fontSize:'16px', fontWeight:'bold' } }, subTitle : { text : '', y : 30, style : { fontFamily:'Verdana,Arial,Helvetica,sans-serif', fontSize:'12px', color: '#111' } }, yUnit : { text : '', style : { fontFamily:'Verdana,Arial,Helvetica,sans-serif', fontSize:'12px', color: '#111' }, lineNum :10 } }, init : function(container,options,width,height){ this.width = width || container.offsetWidth; this.height = height || container.offsetHeight; this.mainGroup = {}; //svg 里面画图 必须有一个svg标签 vml就用div了 this.container = hasSVG ? new vector().$c(1,'svg') .attr({ xmlns : 'http://www.w3.org/2000/svg', version : '1.1' }) .css({fontSize : '12px'}) .appendTo(container).element : new vector().$c(1,'div') .css({ fontSize : '12px', width : this.width +'px', height : this.height+'px' }) .appendTo(container).element; var c = extend(true,{},this.options) var opts = this.opts = extend(true,c,options), style = extend(opts.charts.style,{ width : this.width, height : this.height }); //计算绘画盘子的时候需要的一些参数 this.getDrawArea() .createTooltip() //创建提示信息的框框 .drawTitle() //画标题 //画盘子 if('line,area,pie'.indexOf(opts.charts.type)>=0){ opts.charts.panel = 'x'; } if(' pie,pies'.indexOf(opts.charts.type)<0){ this.drawPanel(); } this.drawLegend(opts.legend.type); //画色块条 var type = { line : 'drawLine', area : 'drawArea', columns : 'drawColumns', pie : 'drawPie', pies : 'drawPies', segment : 'drawSegment' }[opts.charts.type]; this[type](); }, createElement : function(nodeName){ return new vector().$c(this,nodeName); }, group: function(name){ return this.createElement(isIE?'div':'g').attr('mark',name); }, getDrawArea : function(){ var opts = this.opts, width = this.width, height = this.height, title = opts.title, subTitle = opts.subTitle, area = { // 去掉坐标轴左边的刻度文本宽度(预估) 80为定值 左边只留80的间距 areaWidth : width - 80, // 去掉坐标轴底下的文本和标线的高度 areaHeight : height - 40, //原点的X位置 下面会计算到 startX : 0, //原点的Y位置 下面会计算到 startY : 0, //中心的x坐标 画饼图的时候需要知道圆心的位置 centerX: 0, //中心的y坐标 画饼图的时候需要知道圆心的位置 centerY: 0 }; //如果主标题存在 减去主标题的高度 否则 减去10的高 area.areaHeight -=(title.text !== '') ? title.y : 10; // 去掉副标题高度 area.areaHeight -=(subTitle.text !== '') ? subTitle.y : 10 area.startX = 80; area.startY = height - 40; //圆心的位置 area.centerX = width / 2; area.centerY = height / 2; //右边留一些空隙 area.areaWidth -=20; //上边也留一些间距 area.areaHeight -=15; opts.area = area; return this; }, drawTitle : function(){ var opts = this.opts, self = this, arr = [opts.title,opts.subTitle,opts.yUnit], //3个标题坐标的位置的基本参数 config = [ { x : this.width / 2, y : opts.title.y }, { x : this.width / 2, y : opts.subTitle.y }, { x : opts.yUnit.x, y : this.height / 2 - 20 } ], tpanel = this.group('title') .appendTo(); each(arr,function(i,title){ var text = title.text; if(text){ var elem = self.baseDraw('span',{ 'text-anchor':'left', x : mathMax(config[i].x - calTextLen(text,title.style).w/2,10), y : config[i].y },calTextLen(title.text,title.style).h) .css(title.style) .addText(text) .appendTo(tpanel); //如果为2的时候 就说明是副标题 将它竖过来 if(i===2){ hasSVG ? elem.attr({transform : 'rotate(270, '+(opts.yUnit.x+10)+', ' + self.height / 2 + ')'}) : (elem.element.style.filter ='progid:DXImageTransform.Microsoft.BasicImage(rotation=3)') } } }); return this; }, //画盘子 比较麻烦 drawPanel : function(type){ var opts = this.opts, self = this, area = opts.area, isSegment= opts.charts.type==='segment', //盘子的类型 是横盘子 还是纵盘子 type = opts.charts.panel || 'x'; // 底板 var drawAreaWidth = area.areaWidth, drawAreaHeight = area.areaHeight, //原点的坐标 startX = area.startX, startY = area.startY; var allData = [], minValue = 0, maxValue = 10, lineNum = mathMax(opts.yUnit.lineNum,2), min = opts.yUnit.min, staff; //组合所有的数据 each(opts.chartData,function(i,o){ isSegment ? each(o.data,function(j,d){ allData[j] ? allData[j] = allData[j] + (~~d) : allData[j] = ~~d; }) : allData = allData.concat(o.data) }); //给所有的数据排序 为了下面求最大值 最小值 allData.sort(function(a,b){return a-b}); //求出最大值 最小值 maxValue = allData[allData.length - 1]; each(allData,function(i,o){ if(o){ minValue = o; return false; } }); //主盘子容器 this.panel = this.group('panel').appendTo(); var dd = xx(minValue,maxValue,lineNum), min = dd.min, max = dd.max, f = dd.stf; //表示画的是横坐标 if(type.toLowerCase()==='x'){ //横坐标单位间隔 var xPices = drawAreaWidth / opts.xUnit.units.length, //单位间隔的中心点 offset = xPices / 2, yPices = drawAreaHeight / lineNum; //--------------------------------画横向的点和文字--------------------------------------------------------- each(opts.xUnit.units,function(i,d){ self.baseDraw('path',{ border : 1, borderColor : '#C0C0C0', isfill : false, path : [ M, processPoint(startX + (i * xPices)), processPoint(startY), L, processPoint(startX + (i*xPices)), processPoint(startY + 5) ] }). appendTo(self.panel); var y = hasSVG?5:10, span = self.baseDraw('span',{ x : startX + offset + (i * xPices), y : startY+y, 'text-anchor':'middle' }) .css({ fontFamily:'Verdana,Arial,Helvetica,sans-serif', fontSize:'12px' }) .addText(opts.xUnit.units[i]) .appendTo(self.panel) .element; //vml没有x y的概念 所以要简单span宽度的一半 以后用span都需要减掉 !hasSVG span.style.left = parseInt(span.style.left) - span.offsetWidth/2+'px'; }); //--------------------------------画纵向的点和文字----------------------------------------------------------------------- for(i=0;i<=lineNum;i++){ self.baseDraw('path',{ border : 1, borderColor : '#C0C0C0', isfill : false, path : [M, startX, processPoint(startY - (i * yPices)), L, processPoint(startX + drawAreaWidth), processPoint(startY - (i *yPices))] }) .css({zIndex:-10}) .appendTo(self.panel); var span = self.baseDraw('span',{ x : startX - 15, y : startY - i * yPices-calTextLen(min+i*f+'').h/2, 'text-anchor':'middle' }) .css({ 'font-family':'Verdana,Arial,Helvetica,sans-serif', 'font-size':'12px', width: '40px', display:'block', textAlign:'right' }) .addText((min*1000+(i*1000*f/1000)*1000)/1000+'') .appendTo(self.panel) .element; if(!hasSVG){ span.style.top = parseInt(span.style.top) + span.offsetHeight/2 -5+'px'; span.style.left = parseInt(span.style.left) -35+'px' } } }else{ //横坐标单位间隔 var yPices = drawAreaHeight / (opts.xUnit.units.length), //单位间隔的中心点 offset = Math.round(yPices / 2); var x = hasSVG?25:70, vv = hasSVG?0:5; each(opts.xUnit.units,function(i,d){ self.baseDraw('path',{ border : 1, borderColor : '#C0C0C0', isfill : false, path : [ M, processPoint(startX-5), processPoint(startY-i * yPices), L, processPoint(startX), processPoint(startY-i * yPices), ] }) .appendTo(self.panel); var span = self.baseDraw('span',{ x : startX - x, y : startY -i * yPices-offset-calTextLen(d).h/2+vv, 'text-anchor':'middle' }) .css({ fontFamily:'Verdana,Arial,Helvetica,sans-serif', fontSize:'12px', width : '60px', textAlign:'right' }) .addText(d) .appendTo(self.panel) }); var xPices = drawAreaWidth / lineNum; for(var i=0;i<=lineNum;i++){ self.baseDraw('path',{ border : 1, borderColor : '#C0C0C0', isfill : false, path : [ M, processPoint(startX + (i * xPices)), processPoint(startY), L, processPoint(startX + (i*xPices)), processPoint(startY - drawAreaHeight) ] }). appendTo(self.panel); var span = self.baseDraw('span',{ x : startX - calTextLen(min+i*f+'').w/2 + i * xPices, y : startY, 'text-anchor':'left' }) .css({ fontFamily:'Verdana,Arial,Helvetica,sans-serif', fontSize:'12px' }) .addText(min+i*f+'') .appendTo(self.panel) .element; } } //----------------------------------------------------------------------------------------------------- //在画区域图的时候还需要起点的位置啊 //因为坐标很可能是负数 而且也能不是从0开始的 所以+上jianju var jianju =0 if(min>0)jianju = min; if(max<0)jianju = max; //计算开始的位置 startX = opts.charts.panel==='x' ? startX :startX-xPices*(min/f); startY = opts.charts.panel==='x' ? startY+yPices*(min/f) : startY; opts.draw = { startX : startX, // X 轴起点 startY : startY , // Y 轴起点 xPices : xPices, // X 轴每份的宽度 yPices : yPices, // Y 轴每份的宽度 offset : offset, // X 单分中心点位置偏移量 jianju : jianju*yPices/f, feed : f // Y 轴的每份有多少 }; return this; }, createTooltip : function(){ //一个组 this.tipC = this.group('tip') .css({zIndex: 200,height:'20px',width:'20px',position:'absolute'}) .appendTo() .hide() //画一个框框baseDraw this.tipBox = this.baseDraw('rect',{arc:0.22,fill:'#fff',border:2,borderColor:'#606060'}) .appendTo(this.tipC) //因为svg里面的g可以直接定位 但是vml里面的group渲染很慢 所以改div 所以这里的父不一洋 var p = isIE ?this.tipBox :this.tipC; this.tipTxtContainer = this.baseDraw('text',{fill:'#000000',x:5,y:19,'text-anchor':'left'}) .css({ fontFamily:'Verdana,Arial,Helvetica,sans-serif', fontSize:'12px', background: '#FFF' }) .appendTo(p); this.tipText = doc.createTextNode(''); this.tipTxtContainer[0].appendChild(this.tipText); return this; }, showTooltip : function(obj, x, y,data){ var txt =obj.name + ((' : ' + data)||''), size = calTextLen(txt,this.tipTxtContainer[0].style.cssText), pos = {x : x - (size.w + 5 * 2)/2 ,y : y - 32}; this.tipC .toFront() .show(); if(hasSVG){ this.tipC.attr({transform:'translate('+pos.x+','+pos.y+')'}); this.tipBox .attr({width : size.w + 5 * 2,height : size.h + 5 * 2,stroke : obj.color||'#606060'}); }else{ this.tipC.css({left:pos.x,top:pos.y}); this.tipBox .css({width:size.w + 5 * 2,height : size.h + 5 * 2}) this.tipBox[0].strokeColor = obj.color||'#606060'; } this.tipText.nodeValue = txt || ''; }, hideTooltip: function(){ this.tipC.hide(); }, drawLegend : function(type,redraw){ var self = this, opts = this.opts, //颜色块的大小 t_width = 20, t_height = 20, //块之间的距离 t_space = 5, datas = opts.chartData, len = datas.length, css = opts.legend.style, //最大长度 如果是纵着的 需要最大的长度 maxWidth = 10, maxHeight = 30, //这个东西的位置 orig_pos = opts.legend.pos?opts.legend.pos:[2,2], //显示隐藏组的函数 handle = function(i){ var g = self.mainGroup['chart'+i]; if(g.show){ g.chart.hide(); g.show = false; hasSVG ? this.attr({fill:'#ccc'}) : this[0].style.color = '#ccc' }else{ g.chart.show(); g.show = true; hasSVG ? this.attr({fill:'#000'}) : this[0].style.color = '#000' } }, arr = []; type = type ||'lateral'; var legendPanel = self.group('Legend') .appendTo(); if(type==='lateral'){ //如果是横着的 var top = orig_pos[1] + 5, th = hasSVG?0:3, left = orig_pos[0] + 5; each(datas, function(i,d){ left = i===0 ? left : t_space+left; //计算所有left的位置 self.baseDraw('rect',{ arc : 0.1, fill : d.color, border : 1, borderColor : d.color, left : left, top : top, width : t_width+'px', height : t_height+'px' }) .appendTo(legendPanel); left = left + t_width+2 + t_space; var w = calTextLen(d.name,css).w self.baseDraw('span',{ 'text-anchor':'left', x : left, y : top+th }) .css(extend(css,{cursor:'pointer'})) .on('click',function(){ if(opts.charts.type==='pies')return; handle.call(this,i); }) .addText(d.name) .appendTo(legendPanel); left = left + w; }); this.baseDraw('rect',{ arc : 0.1, fill : 'none', border : 1.5, borderColor : '#666666', width : left+ t_space- orig_pos[0], height : maxHeight, left : orig_pos[0], top : orig_pos[1] }) .appendTo(legendPanel); }else{ var top = orig_pos[1] + 5, th = hasSVG?0:3, left = orig_pos[0] + 5; each(datas, function(i,d){ top = i===0 ? top : t_space + top; self.baseDraw('rect',{ arc : 0.1, fill : d.color, border : 1, borderColor : d.color, left : left, top : top, width : t_width+'px', height : t_height+'px' }) .appendTo(legendPanel); var h = calTextLen(d.name,css).h; self.baseDraw('span',{ 'text-anchor':'left', x : left+t_width+2+t_space, y : top+th }) .css(extend(css,{cursor:'pointer'})) .addText(d.name) .on('click',function(){ if(opts.charts.type==='pies')return; handle.call(this,i); }) .appendTo(legendPanel); top = top + h+ t_space; maxWidth = Math.max(maxWidth,calTextLen(d.name,css).w); }); this.baseDraw('rect',{ arc : 0.1, fill : 'none', border : 1.5, borderColor : '#666666', width : maxWidth+22+15, height : top+t_space-orig_pos[1], left : orig_pos[0], top : orig_pos[1] }) .appendTo(legendPanel); } return this; }, drawLine : function(){ var self = this, opts = this.opts; draw = opts.draw; each(opts.chartData,function(i,o){ var id = 'chart'+i, lineGroup = self.group(id) .appendTo(); self.mainGroup[id]={chart:lineGroup,show:true}; var path = [M], data = o.data, line; for(var i = 0; i < data.length; i++){ if( data[i] == null){ //如果这个数据不存在 并且不是第一个数据 路径上加 M if(path[path.length - 1] !== M) path.push(M); }else{ //如果不是第一个数据 路径添加L i !== 0 && path.push("L"); //如果前面一个是null 并且不是第一个 把那个L去掉 if(i > 0 && data[i - 1] == null) path.pop(); //计算出 点的x,y的位置 var x = draw.startX + draw.offset + (i * draw.xPices), y = draw.startY - (data[i] * (draw.yPices / draw.feed)); if(isIE){ x = parseInt(x); y = parseInt(y); } path.push(x); path.push(y); //画点 self.baseDraw('circle',{ x : x, y : y, r : 4, fillColor : o.color }) .attr({data:data[i]}) .css({zIndex:10,cursor:'pointer'}) .on('mouseover',(function(o,x,y){ return function(){ hasSVG ? (this[0].setAttribute('r','5'),line.attr({'stroke-width':2.5})) : (this[0].strokeWeight = 3,line[0].strokeWeight = 2.5); self.showTooltip(o,x,y,this.attr('data')); } })(o,x,y)) .on('mouseout',function(){ hasSVG ? (this[0].setAttribute('r','4'),line.attr({'stroke-width':1.5})) : (this[0].strokeWeight = 1,line[0].strokeWeight = 1.5); self.hideTooltip() }) .on('click',function(){lineGroup.toFront(); }) .appendTo(lineGroup); } }; //画折线 line = self.baseDraw('path',{ border : 1.5, borderColor : o.color, isfill : false, path : path }) .css({zIndex:5}) .on('mouseover',function(){ hasSVG ? this.attr({'stroke-width':2.5}) : (this[0].strokeWeight = 2.5); }) .on('mouseout',function(){ hasSVG ? this.attr({'stroke-width':1.5}) : (this[0].strokeWeight = 1.5); }) .on('click',function(){lineGroup.toFront(); }) .appendTo(lineGroup); }); return this; }, drawArea : function(){ var self = this, opts = this.opts, draw = opts.draw; each(opts.chartData,function(i,o){ var id = 'chart' + i, areaGroup = self.group(id).appendTo(); self.mainGroup[id] = {chart : areaGroup,show : true}; //有2个路径 一个是区域的路径 一个是线的路径 var areaPath = [M, (draw.startX + draw.offset).toFixed(0), (draw.startY-draw.jianju).toFixed(0)], path = [M], data = o.data, line; for(var n=0,l=data.length;n<l;n++){ //如果数据是空的 var len = areaPath.length; if( data[n] === null){ //如果前面的一个不是m 就重新画 所以加上 M if(path[path.length - 1] !== M) path.push(M); //如果第1个 或者前面的都为bull 修改起点坐标 len===3 &&(areaPath[1] = (draw.startX +(n+1)*draw.xPices + draw.offset).toFixed(0)); //如果前面一个不是结束标识符 区域图结束 如果第一个数据是null 则不进行下面的操作 if(areaPath[len - 1] !== seal&&n!==0){ areaPath=areaPath.concat([ areaPath[len - 2], (draw.startY-draw.jianju).toFixed(0), seal ]); } }else{ n !== 0 && path.push(L); areaPath.push(L); //如果前面的那个数据是null 把之前的那个L去掉 if(n > 0 && data[n - 1] == null){ path.pop(); //如果是第一个为null 不删除L n!==1&&areaPath.pop(); } var x = draw.startX + draw.offset + (n * draw.xPices), y = draw.startY - ((data[n]) * (draw.yPices / draw.feed)); if(isIE){ x = parseInt(x); y = parseInt(y); } path.push(x); path.push(y); if(areaPath[len - 1] === seal){ areaPath = areaPath.concat([ M, x, parseInt(draw.startY-draw.jianju), L, x, y ]); }else{ areaPath.push(x); areaPath.push(y); } //如果是最后一个点 if(n === l - 1){ areaPath.push(x); areaPath.push(parseInt(draw.startY-draw.jianju)); } //画点 self.baseDraw('circle',{ x : x, y : y, r : 4, fillColor : o.color }) .attr({data:data[n]}) .on('mouseover',(function(o,x,y){ return function(){ hasSVG ? (this[0].setAttribute('r','5'),line.attr({'stroke-width':2.5})) : (this[0].strokeWeight = 3,line[0].strokeWeight = 2.5); self.showTooltip(o,x,y,this.attr('data')); } })(o,x,y)) .on('mouseout',function(){ hasSVG ? (this[0].setAttribute('r','4'),line.attr({'stroke-width':1.5})) : (this[0].strokeWeight = 1,line[0].strokeWeight = 1.5); self.hideTooltip() }) .on('click',function(){areaGroup.toFront(); }) .css({zIndex:10,cursor:'pointer'}) .appendTo(areaGroup); } } areaPath.push(seal) self.baseDraw('path',{ border : 0, isfill : true, fillColor : o.color, opacity : 0.5, path : areaPath }) .css({zIndex:5}) .appendTo(areaGroup); line = self.baseDraw('path',{ border : 2.5, borderColor : o.color, isfill : false, path : path }) .on('mouseover',function(){ hasSVG ? this.attr({'stroke-width':2.5}) : (this[0].strokeWeight = 2.5); }) .on('mouseout',function(){ hasSVG ? this.attr({'stroke-width':1.5}) : (this[0].strokeWeight = 1.5); }) .on('click',function(){areaGroup.toFront(); }) .css({zIndex:-1}) .appendTo(areaGroup); }); return this; }, drawColumns : function(){ var self = this, opts = this.opts, draw = opts.draw, chartData = opts.chartData, dataLen = chartData.length, //多个柱子之间的间距 columnSpace = 3, //一个位置中 所有的间隔之和 columnPadding = columnSpace * dataLen + columnSpace, //每个柱子的宽度 columnSize = self.opts.charts.panel==='x' ? Number(((draw.xPices - columnPadding) / dataLen).toFixed(0)) : Number(((draw.yPices - columnPadding) / dataLen).toFixed(0)); each(chartData, function(i,o){ var data = o.data, id = 'chart' + i, isX = opts.charts.panel==='x', colGroup = self.group(id).appendTo(), //每个点开始的位置 start = self.opts.charts.panel==='x' ? draw.startX + columnSpace + i*(columnSize+columnSpace) : draw.startY + columnSpace + i*(columnSize+columnSpace) self.mainGroup[id] = {chart:colGroup,show:true}; for(var j = 0,l=data.length; j < l ; j++){ if(data[j]===null) continue; //如果是横盘子 if(isX){ var x = Number((start + j *draw.xPices ).toFixed(0)), y = Number((draw.startY).toFixed(0)), height = Number((data[j] * (draw.yPices / draw.feed)).toFixed(0)), path = [ M, x, y, L, x, y -height, L, x + columnSize, y - height, L, x + columnSize, y, seal ]; var pos = [x+columnSize/2,data[j]>0?y-height:draw.startY]; }else{ var x = Number((draw.startX).toFixed(0)), width = Number((data[j]*((draw.xPices / draw.feed))).toFixed(0)), y = Number((start - (j+1) *draw.yPices ).toFixed(0)), path = [ M, x, y, L, x+ width, y , L, x + width, y + columnSize, L, x , y+ columnSize, seal ]; var pos = [draw.startX+width/2,y-columnSize]; } self.baseDraw('path',{ border : 0, isfill : true, fillColor : o.color, opacity : 1, path : path }) .attr({data:data[j]}) .css({zIndex:5,cursor:'pointer'}) .on('mouseover',(function(x,y,d){ return function(){ this.setOpacity(0.85); self.showTooltip(o,x,y,this.attr('data')); } })(pos[0],pos[1],data[j]) ) .on('mouseout',(function(x,y){ return function(){ this.setOpacity(1); self.hideTooltip(); } })(x,y)) .appendTo(colGroup); } }); return this; }, drawPie : function(){ var self = this, opts = this.opts, area = opts.area, rx = area.centerX, ry = area.centerY, inc = 0, total = 0, data = [], cumulative = -0.25, // start at top; circ = 2 * Math.PI, radiu = mathMin(opts.charts.radius,mathMin(area.areaWidth/2,area.areaHeight/2)), fraction, half_fraction; each(opts.chartData,function(i,o){ typeof o.data ==='object' ? (data.push((function(o){ var all =0; for(var i in o) all+=~~o[i] return all })(o.data))) :data.push(mathAbs(o.data)) }); each(data,function(i,o){ total = total + o; }); each(data,function(i,o){ var pieGroup = self.group('chart'+i).appendTo(), s = inc/total*360, e = (inc + o)/total*360, name = opts.chartData[i].name, size = calTextLen(name), dot = angle(radiu,[rx,ry],s+(e-s)/2,2), x = rx + (dot[0]-rx)/2 - size.w/2, y = ry + (dot[1]-ry)/2 - size.h/2, len = Math.sqrt((x-rx)*(x-rx)+(y-ry)*(y-ry)), moveDisplacement = ((x-rx)*8/len)+','+((y-ry)*8/len); inc = inc + o; var value = Number(o); fraction = total ? value / total : 0; half_fraction = total ? (value / 2) / total : 0; var start = cumulative * circ; half_cumulative = cumulative + half_fraction; cumulative += fraction; var end = cumulative * circ; self.baseDraw('pie',{ config : opts.chartData[i], s : start, e : end, r : radiu, innerR : 0 }) .css({zIndex:5,cursor:'pointer'}) .attr({move:moveDisplacement}) .on('mouseover',function(){ this.setOpacity(0.85); }) .on('mouseout',function(){ this.setOpacity(1); }) .on('click',function(){ var m = this.attr('move') if(m.indexOf('+')>0){ hasSVG ? this.attr({ transform: 'translate(0,0)' }) : this.css({ left : '0px', top : '0px' }) this.attr({move:m.replace('+','')}); }else{ var s= m.split(','); hasSVG ? this.attr({ transform: 'translate('+m+')' }) : this.css({ left : s[0]+'px', top : s[1]+'px' }) this.attr({move:m+'+'}); } }) .appendTo(pieGroup); self.mainGroup['chart'+i] = {chart:pieGroup,show:true}; self.baseDraw('span',{ x : x, y : y, fill : '#fff', 'text-anchor':'left' }) .css({ fontFamily : 'Verdana,Arial,Helvetica,sans-serif', fontSize : '12px', position : 'absolute', color : '#fff', zIndex : 10 }) .addText(name) .appendTo(pieGroup); }); }, drawPies : function(){ var self = this, opts = this.opts, area = opts.area, rx = area.centerX, ry = area.centerY, total = 0, data = [], chartData = opts.chartData, cumulative = -0.25, // start at top; circ = 2 * Math.PI, radiu = mathMin(opts.charts.radius,mathMin(area.areaWidth/2,area.areaHeight/2)), fraction, half_cumulative, half_fraction; each(chartData,function(i,o){ each(o.data,function(j,d){ data[j] ? data[j] +=mathAbs(d) : data[j] =mathAbs(d) }); }); //看有多少个数据来生成来生成内半径 var len = data.length, innerSpace = radiu / 10; Rpice = (radiu - innerSpace) / len, piesGroup = this.group('pies').appendTo(); each(data,function(i,d){ var inc = 0 ; if(d===0) return; each(chartData,function(j,o){ if(~~o.data[i]===0)return; var outR = radiu - Rpice * i, innerR = radiu - Rpice * (i + 1), value = ~~o.data[i], fraction = value / d; half_fraction = (value/2)/d , start = cumulative * circ, s = inc/d*360, e = (inc + value)/d*360; inc = inc + value; var name = o.name, size = calTextLen(name), dot = angle(radiu,[rx,ry],s+(e-s)/2,2), px = dot[0]>rx?1:-1, py = dot[1]>ry?1:-1; var x = rx + px*innerSpace + ((dot[0]-rx-px*innerSpace)/len)*(len-i-1)+((dot[0]-rx-px*innerSpace)/len)/2- size.w/2, y = ry + py*innerSpace +((dot[1]-ry-py*innerSpace)/len)*(len-i-1)+((dot[1]-ry-py*innerSpace)/len)/2- size.h/2; half_cumulative = cumulative + half_fraction, cumulative += fraction, end = cumulative * circ; self.baseDraw('pie',{ config : o, s : start, e : end, r : outR, innerR : innerR }) .css({zIndex:5,cursor:'pointer'}) .on('mouseover',function(){ this.setOpacity(0.85); }) .on('mouseout',function(){ this.setOpacity(1); }) .appendTo(piesGroup); self.baseDraw('span',{ x : x, y : y, fill : '#fff', 'text-anchor':'left' }) .css({ fontFamily : 'Verdana,Arial,Helvetica,sans-serif', fontSize : '12px', position : 'absolute', color : '#fff', zIndex : 10 }) .addText(name) .appendTo(piesGroup); }) }); }, drawSegment : function(){ var self = this, opts = this.opts, draw = opts.draw, chartData = opts.chartData, columnPad = 5, columnWidth = draw.xPices - columnPad * 2; each(chartData,function(i,c){ }); }, baseDraw : function(type,config){ var self = this arg = arguments; return { rect : function(){ var set = {}; set.rx = set.ry = config.arc*30 || 5; set.width = config.width || 50; set.height = config.height || 50; set.fill = config.fill || '#fff'; set['fill-opacity'] = config.opacity || 0.85; set['stroke-width'] = config.border || 2; set.stroke = config.borderColor || '#606060'; set.transform = 'translate('+(config.left||0)+','+(config.top||0)+')'; return self.createElement('rect') .attr(set) }, text : function(){ return self.createElement('text') .attr(config) }, span : function(){ var elem= self.createElement('text') .attr(config) .attr({ y : config.y+(arg[2]||15) }) return elem; }, path : function(){ var set = {}; set['stroke-width'] = config.border; set.stroke = config.borderColor || '#C0C0C0'; set.fill = config.isfill?config.fillColor:'none'; set.d = config.path; config.opacity &&(set['fill-opacity'] = config.opacity); return self.createElement('path') .attr(set); }, circle : function(){ var set = {}; set.cx = config.x; set.cy = config.y; set.r = config.r; set.fill = config.fillColor; return self.createElement('circle') .attr(set); }, pie : function(){ //config,s,e,r,index var opts = self.opts, s = config.s, r = config.r, e = config.e - 0.000001, id = 'chart'+config.index, area = opts.area, rx = area.centerX, ry = area.centerY, cosStart = mathCos(s), sinStart = mathSin(s), cosEnd = mathCos(e), sinEnd = mathSin(e), color = config.config.color, innerR = config.innerR, longArc = e - s < Math.PI ? 0 : 1, path = [ M, rx + r * cosStart, ry + r * sinStart, 'A', r, r, 0, longArc, 1, rx + r * cosEnd, ry + r * sinEnd, L, rx + innerR * cosEnd, ry + innerR * sinEnd, 'A', // arcTo innerR, // x radius innerR, // y radius 0, // slanting longArc, // long or short arc 0, // clockwise rx + innerR * cosStart, ry + innerR * sinStart, 'Z' ]; return self.baseDraw('path',{ border : 1, border : '#fff', isfill : true, fillColor : color, opacity : 1, path : path }) } }[type](); } }; //--------------------------------------------------------------------------------------------------- //如果是vml 修改smipleChart.prototype中的一些方法 !hasSVG &&extend(smipleChart.prototype,{ baseDraw : function(type,config){ var self = this, width = this.width, height = this.height, name = arguments[2]; return { rect : function(){ var attr = {}, css = {}; attr.arcsize = config.arc || 0.2 +''; if(config.fill==='none'){ attr.filled = 'f' }else{ attr.filled = 't'; attr.fillcolor = config.fill || '#fff'; } attr.strokeWeight = config.border || 2; attr.strokeColor = config.borderColor || '#606060'; css.width = config.width || 50 +'px'; css.height = config.height || 50 +'px'; css.zIndex = 10; css.left = config.left||0+'px'; css.top = config.top ||0+'px'; return self.createElement('roundrect') .attr(attr) .css(css) }, text : function(){ return self.createElement('TextBox') .attr({inset : "2px,2px,2px,2px" }) }, span : function(){ return self.createElement('span'). css({ position:'absolute', left : config.x+'px', top : config.y+'px' }) }, path : function(){ var attr = {}, css = { width : width+'px', height : height+'px' }; if(config.border===0){ attr.Stroked = 'f'; attr.strokeWeight =0; }else{ attr.strokeWeight = config.border||1 ; } attr.strokeColor = config.borderColor || "#C0C0C0"; attr.filled = config.isfill?'t':'f'; attr.filled==='t' &&(attr.fillcolor=config.fillColor||"#C0C0C0"); attr.coordsize = width+','+height; attr.path = config.path; var elem = self.createElement() .attr(attr) .css(css); if(config.opacity){ var fill = self.createElement('fill') .attr({ type : 'fill', color : config.fillColor||"#C0C0C0", opacity : config.opacity }) .appendTo(elem); //那这个对象的一个属性引用设置透明的元素 以后会用到 elem.opacity = fill[0]; } return elem; }, circle : function(){ var attr ={ strokeWeight : 1, coordsize : width+','+height, filled : 't' }, css ={ width : width+'px', height : height+'px' } x = config.x, y = config.y, r = config.r; attr.strokeColor=attr.fillcolor = config.fillColor attr.path =[ 'wa', // clockwisearcto x - r, // left y - r, // top x + r, // right y + r, // bottom x + r, // start x y, // start y x + r, // end x y, // end y 'e' // close ]; return self.createElement() .attr(attr) .css(css) }, pie : function(){ ////config,s,e,r,index var opts = self.opts, area = opts.area, r = config.r, rx = area.centerX, ry = area.centerY, innerR= config.innerR||0, sDot = angle(r,[rx,ry],s,2), eDot = angle(r,[rx,ry],e,2), color = config.config.color, s = config.s, e = config.e, e = e - s == 2 * Math.PI ? e - 0.001 : e, cosStart = mathCos(s), sinStart = mathSin(s), cosEnd = mathCos(e), sinEnd = mathSin(e), path = [ 'wa', // clockwisearcto (rx - r).toFixed(0), // left (ry - r).toFixed(0), // top (rx + r).toFixed(0), // right (ry + r).toFixed(0), // bottom (rx + r * cosStart).toFixed(0), // start x (ry + r * sinStart).toFixed(0), // start y (rx + r * cosEnd).toFixed(0), // end x (ry + r * sinEnd).toFixed(0), // end y 'at', // clockwisearcto (rx - innerR).toFixed(0), // left (ry - innerR).toFixed(0), // top (rx + innerR).toFixed(0), // right (ry + innerR).toFixed(0), // bottom (rx + innerR * cosEnd).toFixed(0), // start x (ry + innerR * sinEnd).toFixed(0), // start y (rx + innerR * cosStart).toFixed(0), // end x (ry + innerR * sinStart).toFixed(0), // end y 'x', // finish path 'e' // close ]; return self.baseDraw('path',{ border : 1, border : '#fff', isfill : true, fillColor : color, opacity : 1, path : path }) } }[type](); } }); //--------------------------------------------------------------------------------------------------- })(document); window.onload = function(){ var config = { charts : { type : 'line', radius : 150, panel : 'x', style: { fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', // default font fontSize: '12px' } }, title : { text : '线性图标' , y : 10, style : { color: 'black', fontSize: '16px' } }, subTitle : { text : '线性图标副标题', y : 35, style: { color: '#111', fontSize: '12px' } }, legend : { enable : true, //type : 'lateral', // lateral 横向 或 lengthwise 纵向 type : 'lateral', pos : [10,10], style:{ fontFamily: '"Lucida Grande", "Lucida Sans Unicode", Verdana, Arial, Helvetica, sans-serif', // default font fontSize: '12px', magin:'0px' } }, yUnit : { text : '线性图标侧标题', x : 20, style: { color : '#111', fontSize : '12px' } }, xUnit : { units: [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月' ] }, chartData : [ { name : 'xcv', color : '#4572A7', data : [-18,45,-38,29,95,-45,77] }, { name: 'frfr', color: '#AA4643', data: [-44,12,78,-100,13,4,-56,-34] }, { name: 'AAAAA', color: '#89A54E', data: [null,78,83,null,22,-78,2,44,78] }, { name: 'BBBB', color: '#80699B', data: [null, 58, 35, null, 52, 47, 26, -55, 39, 123,15,66] } ] }; new smipleChart($('line'),config); config.charts.type ='area'; config.title.text ='区域图标' config.subTitle.text='区域图标副标题' config.yUnit.text ='区域图标侧标题' new smipleChart($('area'),config); config.title.text ='柱状图标' config.subTitle.text='柱状图标副标题' config.yUnit.text ='柱状图标侧标题' config.charts.type ='columns'; config.chartData =[ { name : 'xcv', color : '#4572A7', data : [-0.01,-0.62,0,0.55,null,0.78,-0.63,-0.82,null,null,0.33] }, { name: 'frfr', color: '#AA4643', data: [-0.22,0.82,0.55,1.32,0.33,0.95,null,1,0.65,null,0.78] }, { name: 'AAAAA', color: '#89A54E', data: [null,0.62,0.34,null,0.63,0,-0.23,-1,0.62,0.45,null,-0.56] } ] new smipleChart($('zhu'),config); config.charts.panel='y' new smipleChart($('zhu1'),config); config.charts.type ='pie'; config.title.text ='饼图图标' config.subTitle.text='饼图图标副标题' config.yUnit.text ='' config.legend.type='lengthwise'; config.chartData =[ { name : 'aaa', color : '#4572A7', data : [433,123,null,66] }, { name: 'bbb', color: '#AA4643', data: [45,33,33,511] }, { name: 'ccc', color: '#89A54E', data: [55,null,75,333] } ] config.legend.pos= [680,30] new smipleChart($('pie'),config); config.charts.type ='pies'; config.title.text ='多层饼图图标' config.subTitle.text='多层饼图图标副标题' config.legend.type='lateral'; config.legend.pos= [330,400] new smipleChart($('pies'),config); config.chartData =[ { name : 'xcv', color : '#4572A7', data : [433,355,275,null,588,323,576,32,99] }, { name: 'frfr', color: '#AA4643', data: [45,666,100,null,677,56,88,633,55,64] }, { name: 'AAAAA', color: '#89A54E', data: [55,162,75,null,364,0,637,112,163,615] } ] config.yUnit.lineNum = 2; config.title.text ='比较小的' config.subTitle.text='只设置了2条线' config.yUnit.text ='小测标题' ; config.charts.type ='line'; config.legend.pos= [10,10] new smipleChart($('vv'),config); config.yUnit.lineNum = 10; config.charts.panel = 'x'; config.charts.type ='segment'; //new smipleChart($('segment'),config); } </script> </body> </html>
javascript smipleChart 简单图标类
声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Reply on: @reply_date@
@reply_contents@