[原创]利用javascript + VML在网页中实现折线图

本文介绍了一种使用javascript结合VML在仅支持IE6.0的环境中实现网页折线图的方法。通过创建坐标系统,计算逻辑单位与实际像素的比例因子,然后利用javascript动态绘制折线和坐标轴。作者提供了详细的代码示例,展示了如何生成折线图,包括颜色配置、坐标轴的绘制和数据标签的展示。

作者:ybbqy
欢迎访问ybbqy的BLOG :http://blog.youkuaiyun.com/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.youkuaiyun.com/ybbqy的访问量';
data_title[1] = 'blog.youkuaiyun.com/ybbqy的访问量-2';
data_title[2] = 'blog.youkuaiyun.com/ybbqy的访问量-3';
data_title[3] = 'blog.youkuaiyun.com/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.youkuaiyun.com/ybbqy的访问量';
data_title[1] = 'blog.youkuaiyun.com/ybbqy的访问量-2';
data_title[2] = 'blog.youkuaiyun.com/ybbqy的访问量-3';
data_title[3] = 'blog.youkuaiyun.com/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>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值