如何利用javascript+VML在网页中实现折线图?

如何利用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>