如何利用javascript+VML在网页中实现折线图?
作者:ybbqy
欢迎访问ybbqy的BLOG :http://blog.csdn.net/ybbqy 言规正传: 今天的主题就是在网页上画图! 网页上画图?文本描述的HTTP浏览感觉很悬吧?其实网上已经有很多相关的技术,我这里就用VML来实现。(注:VML目前只被IE6.0下支持,并且微软也将打算不再支持VML,这里只作抛砖引玉,相信认真的读者会做到举一反三。想实用、兼容性好,建议还是在服务器端生成图片或用JAVA.applet吧。在我的试用下,IE5.0不支持容器的相对坐标,一切坐标都从网页右上角开始,在这里也请达人指教。) 思路: 首先我们画一个坐标,坐标的映射我没有用vml自身的,因为失量的计算会使速度变得非常的慢。 坐标的实际坐标宽为600素,高为200象素,逻辑宽由数据个数决定,逻辑高由最大数据决定。 比例因子是实际象素数除逻辑数。我们知道一个逻辑象素,乘上比例因子可以得到相应的实际象素坐标。 在此基础之上我们利用JAVASCRIPT控制来画出需要的折线图. 首先必须vml要用的东东,我也不清楚干嘛的,听说和xml有关,感兴趣的可以查资料地说~ <html xmlns:v="urn:schemas-microsoft-com:vml"> <STYLE> v/:* { Behavior: url(#default#VML) } </STYLE> 准备一个table(表格)做图的容器,把画的图放里面 折线的颜色 最多可用10组 var tmdColor1 = new Array(); tmdColor1[0] = "#d1ffd1"; tmdColor1[1] = "#ffbbbb"; tmdColor1[2] = "#ffe3bb"; tmdColor1[3] = "#cff4f3"; tmdColor1[4] = "#d9d9e5"; tmdColor1[5] = "#ffc7ab"; tmdColor1[6] = "#ecffb7"; tmdColor1[7] = "#FFE6E7"; tmdColor1[8] = "#FFF5D0"; tmdColor1[9] = "#C4C4FF";var tmdColor2 = new Array();
tmdColor2[0] = "#00ff00"; tmdColor2[1] = "#ff0000"; tmdColor2[2] = "#ff9900"; tmdColor2[3] = "#33cccc"; tmdColor2[4] = "#666699"; tmdColor2[5] = "#993300"; tmdColor2[6] = "#99cc00"; tmdColor2[7] = "#FFAEAD"; tmdColor2[8] = "#FFCB00"; tmdColor2[9] = "#0000FF";函数的声名,这个函数可以画一个坐标,很好看地~~~
// 生成坐标。参数:实际宽,实际高,逻辑宽,逻辑高 function get_coord(container_width, container_height, logic_width, logic_height, org_x, org_y , data_title) 这里是逻辑单位和实际象素比例,vml也有坐标系统,但我推荐不要用,原因是当数值太大时vml不能胜任,因为vml的坐标系统宽和高最大只支持到8位数,用javascript就不会出现这个问题.以前我也是直接看教程用的vml坐标,但发现不实用,也不好控制,所以自己进行映射. 得到的比例因子乘上逻辑单位就是实际的像素数. // 比例因子=容器/逻辑 var x_scale = container_width/logic_width; var y_scale = container_height/logic_height; 然后设置整个坐标, 这里的高因为是以数值最大大小来决定的,所以算法可以由读者来重写. // 坐标大小 var coord_width = logic_width; var coord_height= (parseInt(logic_height.toString().substring(0,1))+1) * Math.pow(10, logic_height.toString().length - 1); 这里是画坐标的刻度 // 画纵 坐标 for (var loop = 0; loop <= coord_height; loop += coord_height/10) { // 映射为象素 var coord_line_x1 = org_x; var coord_line_y1 = y_scale*loop + org_y; var coord_line_x2 = x_scale*coord_width + org_x; var coord_line_y2 = y_scale*loop + org_y;var across_num_x1 = org_x;
var across_num_y1 = coord_line_y1; var across_num_x2 = org_x; var across_num_y2 = coord_line_y2;str +=
// 画纵坐标的虚线 '<v:line from="'+org_x+','+coord_line_y1+'" to="'+coord_line_x2+','+coord_line_y2+'" style="Z-INDEX:0;" strokeweight="1pt">' + '<v:stroke dashstyle="Dot"/>' + '</v:line>' // 画纵坐标数字 + '<v:line from="'+across_num_x1+','+across_num_y1+'" to="'+across_num_x2+','+across_num_y2+'" style="Z-INDEX:0;" strokeweight="1pt">' + '<v:TextBox inset="-50pt,0pt,0pt,50pt" style="font-size:9pt;">' + '<font color="0"><p align="right">'+Math.floor(loop)+'</p></font>' + '</v:TextBox>' + '</v:line>'; } 纵轴的坚线 str += '' + '<v:line from="'+org_x+',0" to="'+(coord_line_x2 )+',0" style="Z-INDEX:8;" strokeweight="1pt">' + '</v:line>' + '<v:line from="'+org_x+',0" to="'+org_x+','+(y_scale*(loop+1))+'" style="Z-INDEX:8;" strokeweight="1pt">' + '</v:line>';说明每条线代表的意义.以样标题显示在坐标间。
// 画各数据标题 for (var i=0; i < data_title.length; i++) { title_left = 500-i*100; title_top = y_scale*coord_height-10; title_width = 10; title_height = 10;str += '<v:Rect style="position:relative;left:'+(title_left)+';top='+(title_top)+';width:'+title_width+'px;height:'+title_height+'px;" StrokeWeight="1pt" fillcolor = "#'+tmdColor2[i]+'" StrokeWeight="0pt";>' +'</v:Rect>'
+'<v:line from="'+(title_left+title_width)+','+(title_top)+'" to="'+(title_left+title_width)+','+(title_top)+'" style="position:relative;">' +'<v:TextBox inset="0pt,-2pt,-'+(50)+'pt,50pt" style="font-size:9pt;">'+data_title[i]+'</v:TextBox>' +'</v:line>' ; } 以上是坐标部份,已经完成一半啦~ 下面来讲真正画折线,感觉比画坐标简单~ 活活 函数声名: // 数据数组,说明数据意义数组,数据周期说明数组,x轴的标题,y轴的标题 function fold_line(data, data_title, bottom_title, x_title, y_title) 这个是容器的宽和高 // 容器作标 var container_width = 600; var container_height= -200; 容器的原点位置var org_x=50;
var org_y=0; 逻辑大小// 逻辑坐标
var logic_width = 0; var logic_height= 0; 最后都放这个变量里再打印到网页上 // 图表生成文本存放变量 var fold_line = '<html xmlns:v="urn:schemas-microsoft-com:vml">' + '<STYLE>' + 'v/:* { Behavior: url(#default#VML) }' + '</STYLE>'; 计算逻辑高和逻辑宽:计算宽简单,就是数据的周期有多少个宽就是多少,高就需要取最大的才能容下了. // 计算逻辑高和逻辑宽 for (var i=0; i < data.length ; i++) { var item = data[i].split(/[',']/); // 最大的列数设置为逻辑宽度 if (logic_width < item.length) { logic_width = item.length; }// 最大数据为逻辑高度
for (var j=0; j < item.length; j++) { if (logic_height < parseInt(item[j])) { logic_height = item[j]; } } } 计算比例因子 // 比例因子=容器/逻辑 var x_scale = container_width/logic_width; var y_scale = container_height/logic_height; 为0 时 // 为0 时 if (logic_height < 1) { logic_height = -container_height; } 画容器和坐标,前面的计算用上了吧~~~ // 画容器 fold_line += ' <v:group ID="group1" style="position:relative;WIDTH:'+container_width+';HEIGHT:'+container_height+';LEFT:0;TOP:10;" CoordSize="'+container_width+', '+container_height+'" >' // 画坐标画面 fold_line += get_coord(container_width, container_height, logic_width, logic_height, org_x, org_y , data_title ); 画x轴线上的标题 // 画横坐标,数据周期 for (var loop=0; loop < logic_width; loop++) {// 映射为象素
var coord_title_x1 = x_scale*loop + org_x; var coord_title_y1 = org_y; var coord_title_x2 = x_scale*loop + org_x; var coord_title_y2 = 10 + org_y;fold_line +=
'<v:line from="'+coord_title_x1+','+coord_title_y1+'" to="'+coord_title_x2+','+coord_title_y2+'" style="Z-INDEX:8;" StrokeWeight="1pt"/>' + '<v:line from="'+(x_scale*(loop)+org_x)+',0" to="'+(x_scale*(loop+1)+org_x)+',0" style="Z-INDEX:8;" StrokeWeight="1pt">' + '<v:TextBox inset="0pt,0pt,50pt,50pt" style="font-size:9pt;">' + '<font color="0"><p align="center">'+ bottom_title[loop]+ '</p></font>' + '</v:TextBox>' + '</v:line>' ;}
循环从左到右,从上到下画折线咯!~~~ // 开始逐一画折线 for (var i=0; i < data.length ; i++) { var item = data[i].split(/[',']/);for (var j=0; j < logic_width - 1; j++)
{var fold_line_x1 = x_scale*j + org_x;
var fold_line_y1 = y_scale*item[j] + org_y; var fold_line_x2 = x_scale*(j+1) + org_x; var fold_line_y2 = y_scale*item[j+1] + org_y;fold_line +='<v:line from="'+fold_line_x1+','+fold_line_y1+'" to="'+fold_line_x2+','+fold_line_y2+'" style="Z-INDEX:8;" StrokeWeight="1pt"; StrokeColor="'+tmdColor2[i]+'">'
+ '<v:shadow on="T" type="single" color="#b3b3b3" offset="1px,1px"/>' + '</v:line>' // 画小方块 + '<v:rect style="position:relative;Z-INDEX:10;left:'+(fold_line_x2-(5/2))+';top:'+(fold_line_y2-(5/2))+';width:'+5+';height:'+5+'" fillcolor = "#'+tmdColor2[i]+'" StrokeWeight="0pt";/>' ; } } 完工~~~ // 容器结束 fold_line += '</v:group>'; 打印~~~ document.write(fold_line); 到此一个完美的折线已经画完~~~ 调用方法也很简单: 在script块下调用fold_line函数即可,具体方法如下:建三个数组
data是数据,data_title是说明每维数据的表述的意义,bottom_title是周期的标题(这也是用bottom的意义,因为周期在横坐标上表示,横坐标在底下的嘛!) var data = new Array(); var data_title = new Array(); var bottom_title = new Array();data[0] = '2,34456565,53434346,7454545,8,45445459,67676774,45656566,26767674,24545678,94456789,26543456'; data[1] = '13434342,33434342,34343435,30454545,56566737,30788980,23345645,34344576,67676786,56567353,28989896,19899467'; data[2] = '22323233,23434341,24545454,55656564,67667676,78787857,56788903,13233452,45566782,23545562,23434344,34343442'; data[3] = '33434343,42323235,33434344,54545456,55656567,76767678,77878788,67676762,56565672,45454552,5656662,56565662';
data_title[0] = 'blog.csdn.net/ybbqy的访问量';
data_title[1] = 'blog.csdn.net/ybbqy的访问量-2'; data_title[2] = 'blog.csdn.net/ybbqy的访问量-3'; data_title[3] = 'blog.csdn.net/ybbqy的访问量-4';bottom_title[0] = '1月';
bottom_title[1] = '2月'; bottom_title[2] = '3月'; bottom_title[3] = '4月'; bottom_title[4] = '5月'; bottom_title[5] = '6月'; bottom_title[6] = '7月'; bottom_title[7] = '8月'; bottom_title[8] = '9月'; bottom_title[9] = '10月'; bottom_title[10] = '11月'; bottom_title[11] = '12月'; bottom_title[12] = '11月'; //*/fold_line(data, data_title, bottom_title);
完整源码(粘贴后存成.htm文件看效果)
<html xmlns:v="urn:schemas-microsoft-com:vml"> <STYLE> v/:* { Behavior: url(#default#VML) } </STYLE> <TABLE> <TR> <TD> <script language="JavaScript">var tmdColor1 = new Array(); tmdColor1[0] = "#d1ffd1"; tmdColor1[1] = "#ffbbbb"; tmdColor1[2] = "#ffe3bb"; tmdColor1[3] = "#cff4f3"; tmdColor1[4] = "#d9d9e5"; tmdColor1[5] = "#ffc7ab"; tmdColor1[6] = "#ecffb7"; tmdColor1[7] = "#FFE6E7"; tmdColor1[8] = "#FFF5D0"; tmdColor1[9] = "#C4C4FF"; var tmdColor2 = new Array(); tmdColor2[0] = "#00ff00"; tmdColor2[1] = "#ff0000"; tmdColor2[2] = "#ff9900"; tmdColor2[3] = "#33cccc"; tmdColor2[4] = "#666699"; tmdColor2[5] = "#993300"; tmdColor2[6] = "#99cc00"; tmdColor2[7] = "#FFAEAD"; tmdColor2[8] = "#FFCB00"; tmdColor2[9] = "#0000FF";// 生成坐标。参数:实际宽,实际高,逻辑宽,逻辑高 function get_coord(container_width, container_height, logic_width, logic_height, org_x, org_y , data_title) { var str = '';// 比例因子=容器/逻辑 var x_scale = container_width/logic_width; var y_scale = container_height/logic_height;// 坐标大小 var coord_width = logic_width; var coord_height= (parseInt(logic_height.toString().substring(0,1))+1) * Math.pow(10, logic_height.toString().length - 1);// 画纵 坐标 for (var loop = 0; loop <= coord_height; loop += coord_height/10) { // 映射为象素 var coord_line_x1 = org_x; var coord_line_y1 = y_scale*loop + org_y; var coord_line_x2 = x_scale*coord_width + org_x; var coord_line_y2 = y_scale*loop + org_y;var across_num_x1 = org_x; var across_num_y1 = coord_line_y1; var across_num_x2 = org_x; var across_num_y2 = coord_line_y2;str += // 画纵坐标的虚线 '<v:line from="'+org_x+','+coord_line_y1+'" to="'+coord_line_x2+','+coord_line_y2+'" style="Z-INDEX:0;" strokeweight="1pt">' + '<v:stroke dashstyle="Dot"/>' + '</v:line>' // 画纵坐标数字 + '<v:line from="'+across_num_x1+','+across_num_y1+'" to="'+across_num_x2+','+across_num_y2+'" style="Z-INDEX:0;" strokeweight="1pt">' + '<v:TextBox inset="-50pt,0pt,0pt,50pt" style="font-size:9pt;">' + '<font color="0"><p align="right">'+Math.floor(loop)+'</p></font>' + '</v:TextBox>' + '</v:line>'; }str += '' + '<v:line from="'+org_x+',0" to="'+(coord_line_x2 )+',0" style="Z-INDEX:8;" strokeweight="1pt">' + '</v:line>' + '<v:line from="'+org_x+',0" to="'+org_x+','+(y_scale*(loop+1))+'" style="Z-INDEX:8;" strokeweight="1pt">' + '</v:line>';// 画各数据标题 for (var i=0; i < data_title.length; i++) { title_left = 500-i*100; title_top = y_scale*coord_height-10; title_width = 10; title_height = 10; str += '<v:Rect style="position:relative;left:'+(title_left)+';top='+(title_top)+';width:'+title_width+'px;height:'+title_height+'px;" StrokeWeight="1pt" fillcolor = "#'+tmdColor2[i]+'" StrokeWeight="0pt";>' +'</v:Rect>' +'<v:line from="'+(title_left+title_width)+','+(title_top)+'" to="'+(title_left+title_width)+','+(title_top)+'" style="position:relative;">' +'<v:TextBox inset="0pt,-2pt,-'+(50)+'pt,50pt" style="font-size:9pt;">'+data_title[i]+'</v:TextBox>' +'</v:line>' ; }return str; }function fold_line(data, data_title, bottom_title, x_title, y_title) {// 容器作标 var container_width = 600; var container_height= -200;var org_x=50; var org_y=0;// 逻辑坐标 var logic_width = 0; var logic_height= 0;// 图表生成文本存放变量 var fold_line = '<html xmlns:v="urn:schemas-microsoft-com:vml">' + '<STYLE>' + 'v/:* { Behavior: url(#default#VML) }' + '</STYLE>'; // 计算逻辑高和逻辑宽 for (var i=0; i < data.length ; i++) { var item = data[i].split(/[',']/); // 最大的列数设置为逻辑宽度 if (logic_width < item.length) { logic_width = item.length; }// 最大数据为逻辑高度 for (var j=0; j < item.length; j++) { if (logic_height < parseInt(item[j])) { logic_height = item[j]; } } }// 比例因子=容器/逻辑 var x_scale = container_width/logic_width; var y_scale = container_height/logic_height;// 为0 时 if (logic_height < 1) { logic_height = -container_height; }// 画容器 fold_line += ' <v:group ID="group1" style="position:relative;WIDTH:'+container_width+';HEIGHT:'+container_height+';LEFT:0;TOP:10;" CoordSize="'+container_width+', '+container_height+'" >' // 画坐标画面 fold_line += get_coord(container_width, container_height, logic_width, logic_height, org_x, org_y , data_title ); // 画横坐标,数据周期 for (var loop=0; loop < logic_width; loop++) {// 映射为象素 var coord_title_x1 = x_scale*loop + org_x; var coord_title_y1 = org_y; var coord_title_x2 = x_scale*loop + org_x; var coord_title_y2 = 10 + org_y;fold_line += '<v:line from="'+coord_title_x1+','+coord_title_y1+'" to="'+coord_title_x2+','+coord_title_y2+'" style="Z-INDEX:8;" StrokeWeight="1pt"/>' + '<v:line from="'+(x_scale*(loop)+org_x)+',0" to="'+(x_scale*(loop+1)+org_x)+',0" style="Z-INDEX:8;" StrokeWeight="1pt">' + '<v:TextBox inset="0pt,0pt,50pt,50pt" style="font-size:9pt;">' + '<font color="0"><p align="center">'+ bottom_title[loop]+ '</p></font>' + '</v:TextBox>' + '</v:line>' ;} // 开始逐一画折线 for (var i=0; i < data.length ; i++) { var item = data[i].split(/[',']/);for (var j=0; j < logic_width - 1; j++) {var fold_line_x1 = x_scale*j + org_x; var fold_line_y1 = y_scale*item[j] + org_y; var fold_line_x2 = x_scale*(j+1) + org_x; var fold_line_y2 = y_scale*item[j+1] + org_y;fold_line +='<v:line from="'+fold_line_x1+','+fold_line_y1+'" to="'+fold_line_x2+','+fold_line_y2+'" style="Z-INDEX:8;" StrokeWeight="1pt"; StrokeColor="'+tmdColor2[i]+'">' + '<v:shadow on="T" type="single" color="#b3b3b3" offset="1px,1px"/>' + '</v:line>' // 画小方块 + '<v:rect style="position:relative;Z-INDEX:10;left:'+(fold_line_x2-(5/2))+';top:'+(fold_line_y2-(5/2))+';width:'+5+';height:'+5+'" fillcolor = "#'+tmdColor2[i]+'" StrokeWeight="0pt";/>' ; } }// 容器结束 fold_line += '</v:group>';document.write(fold_line); } //*/
var data = new Array(); var data_title = new Array(); var bottom_title = new Array();data[0] = '2,34456565,53434346,7454545,8,45445459,67676774,45656566,26767674,24545678,94456789,26543456'; data[1] = '13434342,33434342,34343435,30454545,56566737,30788980,23345645,34344576,67676786,56567353,28989896,19899467'; data[2] = '22323233,23434341,24545454,55656564,67667676,78787857,56788903,13233452,45566782,23545562,23434344,34343442'; data[3] = '33434343,42323235,33434344,54545456,55656567,76767678,77878788,67676762,56565672,45454552,5656662,56565662'; data_title[0] = 'blog.csdn.net/ybbqy的访问量'; data_title[1] = 'blog.csdn.net/ybbqy的访问量-2'; data_title[2] = 'blog.csdn.net/ybbqy的访问量-3'; data_title[3] = 'blog.csdn.net/ybbqy的访问量-4';bottom_title[0] = '1月'; bottom_title[1] = '2月'; bottom_title[2] = '3月'; bottom_title[3] = '4月'; bottom_title[4] = '5月'; bottom_title[5] = '6月'; bottom_title[6] = '7月'; bottom_title[7] = '8月'; bottom_title[8] = '9月'; bottom_title[9] = '10月'; bottom_title[10] = '11月'; bottom_title[11] = '12月'; bottom_title[12] = '11月'; //*/fold_line(data, data_title, bottom_title); </script> </TD> </TR> </TABLE> |