用JavaScript实现动态图形
1.JavaScript与2D图形
JavaScript的神奇之处远远超过一般程序员的想象。JavaScript可以实现所有的二维图形。看到我的Blog左面,它就是使用JavaScript和Google公司用JavaScript开发的excanvas写出来的。
It's time to reuse!
<shape id="_x0000_s1026" style="MARGIN-TOP: 0px; Z-INDEX: 1; MARGIN-LEFT: 0px; WIDTH: 7.5pt; POSITION: absolute; HEIGHT: 7.5pt" strokeweight="1pt" strokecolor="#c80000" stroked="f" fillcolor="#c80000" path="m95,95r500,l595,595r-500,xe" coordsize="100,100"></shape><shape id="_x0000_s1027" style="MARGIN-TOP: 0px; Z-INDEX: 2; MARGIN-LEFT: 0px; WIDTH: 7.5pt; POSITION: absolute; HEIGHT: 7.5pt" strokeweight="1pt" strokecolor="#0000c8" stroked="f" fillcolor="#0000c8" path="m295,295r500,l795,795r-500,xe" coordsize="100,100"><fill opacity=".5"></fill></shape>
什么是 excanvas 呢? Firefox , Safari 和 Opera9 都支持 canvas tag , canvas 用来绘制 2D 图形。但是 IE 不支持 canvas 。 Google 就开发了 excanvas ,模拟 canvas 在其他浏览器的状态,这样 IE 也可以使用 canvas 了。下面是一组更复杂的图形:
<shapetype id="_x0000_t75" stroked="f" path="m@4@5l@4@11@9@11@9@5xe" coordsize="21600,21600" filled="f" o:preferrelative="t" o:spt="75"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 79.5pt; HEIGHT: 80.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image002.png"></imagedata></shape><shape id="_x0000_i1026" style="WIDTH: 79.5pt; HEIGHT: 79.5pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image004.png"></imagedata></shape><shape id="_x0000_i1027" style="WIDTH: 81pt; HEIGHT: 80.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image006.png"></imagedata></shape><shape id="_x0000_i1028" style="WIDTH: 79.5pt; HEIGHT: 80.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image008.png"></imagedata></shape>
下面是这一组图形的实现。<script type="text/javascript"></script>中,包含了excanvas的内容,因为Blog里不能放.js文件,我只能将excanvas的内容全面拷贝在HTML文件里。后面的function DrawChart(elname)到function DrawChart5(elname),是绘图的5个方法。这5个方法都是我原文拷贝JavaScript canvas经典例子的源代码。
Html内容中,body tag负责调用这5个方法,其中的参数是canvas tag id。
<body onload="DrawChart('graph');DrawChart2('graph2');DrawChart3('graph3');DrawChart4('graph4');DrawChart5('graph5');">
canvas tag是图形绘制的位置。
<canvas id="graph" width="200" height="200" style="border: 1px solid #eee;"></canvas>
<html>
<script type="text/javascript">
// Copyright 2006 Google Inc.
if (!window.CanvasRenderingContext2D) {
(function () {
// alias some functions to make (compiled) code shorter
var m = Math;
var mr = m.round;
var ms = m.sin;
var mc = m.cos;
var G_vmlCanvasManager_ = {
//可以在http://sourceforge.net/projects/excanvas/下载。
}
function DrawChart(elname){
var element=document.getElementById(elname);
var el = G_vmlCanvasManager.initElement(element);
var ctx = el.getContext("2d");
ctx.fillStyle = "rgb(200,0,0)";
ctx.fillRect (10, 10, 50, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect (30, 30, 50, 50);
};
function DrawChart2(elname) {
var start = new Date;
var element=document.getElementById(elname);
var el = G_vmlCanvasManager.initElement(element);
var ctx = el.getContext("2d");
ctx.translate(75,75);
for (i=1;i<6;i++){
ctx.save();
ctx.fillStyle = 'rgb('+(51*i)+','+(255-51*i)+',255)';
for (j=0;j<i*6;j++){
ctx.rotate(Math.PI*2/(i*6));
ctx.beginPath();
ctx.arc(0,i*12.5,5,0,Math.PI*2,true);
ctx.fill();
}
ctx.restore();
}
}
function DrawChart3(elname) {
var start = new Date;
var element=document.getElementById(elname);
var el = G_vmlCanvasManager.initElement(element);
var ctx = el.getContext("2d");
// Draw background
ctx.fillStyle = 'rgb(255,221,0)';
ctx.fillRect(0,0,150,37.5);
ctx.fillStyle = 'rgb(102,204,0)';
ctx.fillRect(0,37.5,150,37.5);
ctx.fillStyle = 'rgb(0,153,255)';
ctx.fillRect(0,75,150,37.5);
ctx.fillStyle = 'rgb(255,51,0)';
ctx.fillRect(0,112.5,150,37.5);
// Draw semi transparent circles
for (i=0;i<10;i++){
ctx.fillStyle = 'rgba(255,255,255,'+(i+1)/10+')';
for (j=0;j<4;j++){
ctx.fillRect(5+i*14,5+j*37.5,14,27.5);
}
}
}
function DrawChart4(elname) {
var start = new Date;
var element=document.getElementById(elname);
var el = G_vmlCanvasManager.initElement(element);
var ctx = el.getContext("2d");
// draw background
ctx.fillStyle = '#FD0';
ctx.fillRect(0,0,75,75);
ctx.fillStyle = '#<chmetcnv w:st="on" unitname="C" sourcevalue="6" hasspace="False" negative="False" numbertype="1" tcsc="0">6C</chmetcnv>0';
ctx.fillRect(75,0,75,75);
ctx.fillStyle = '#<chmetcnv w:st="on" unitname="F" sourcevalue="9" hasspace="False" negative="False" numbertype="1" tcsc="0">09F</chmetcnv>)';
ctx.fillRect(0,75,75,75);
ctx.fillStyle = '#F30';
ctx.fillRect(75,75,150,150);
ctx.fillStyle = '#FFF';
// set transparency value
ctx.globalAlpha = 0.2;
// Draw semi transparent circles
for (i=0;i<7;i++){
ctx.beginPath();
ctx.arc(75,75,10+10*i,0,Math.PI*2,true);
ctx.fill();
}
}
function DrawChart5(elname) {
var start = new Date;
var element=document.getElementById(elname);
var el = G_vmlCanvasManager.initElement(element);
var ctx = el.getContext("2d");
ctx.fillStyle = "red";
ctx.beginPath();
ctx.moveTo(30, 30);
ctx.lineTo(150, 150);
ctx.quadraticCurveTo(60, 70, 70, 150);
ctx.lineTo(30, 30);
ctx.fill();
}
</script>
<body onload="DrawChart('graph');DrawChart2('graph2');DrawChart3('graph3');DrawChart4('graph4');DrawChart5('graph5');">
<table>
<tbody>
<tr>
<td>
<div >
<canvas id="graph" width="200" height="200" style="border: 1px solid #eee;"></canvas>
</div>
</td>
<td>
<div>
<canvas id="graph2" width="200" height="200" style="border: 1px solid #eee;"></canvas>
</div>
</td>
<td>
<div>
<canvas id="graph3" width="200" height="200" style="border: 1px solid #eee;"></canvas>
</div>
</td>
</tr>
<tr>
<td>
<div>
<canvas id="graph4" width="150" height="150">
</canvas>
</div>
</td>
<td>
<div>
<canvas id="graph5" width="150" height="150">
</canvas>
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>
2.JavaScript 动态2D图形与数据交换
当我第一次接触http://www.liquidx.net/plotkit/时,我才意识到JavaScript的强大。PlotKit是JavaScript Chart Plotting, 既JavaScript图形开发工具,它将JavaScript的绘图功能封装为三种较复杂的具有数据交互功能的图形,饼状图,矩形图和二维曲线图。
我使用二维曲线图和JavaScript Timer模拟简单的动态股票行情。
如下图,该图每1秒更新一次数据。
<shape id="_x0000_i1029" style="WIDTH: 424.5pt; HEIGHT: 162.75pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image010.png"></imagedata></shape>
<shape id="_x0000_i1030" style="WIDTH: 425.25pt; HEIGHT: 162pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5C%E8%B6%99%E7%A3%8A%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image012.png"></imagedata></shape>
<script type="text/javascript" src="js/mochikit/MochiKit.js"></script>等include Plotkit的js文件。
var plotter 为绘图对象
var newRows用来显示动态表数据, 图中的Last列。
var options 变量为绘图参数。
createData方法生成三组随机数据,既右图的3条变化曲线。
DrawChart更新图形和表数据。
Beginning创建图形和启动Timer。
HTML Body中的
<object id="tabledata"
classid="clsid:<chmetcnv w:st="on" unitname="C" sourcevalue="333" hasspace="False" negative="False" numbertype="1" tcsc="0">333C</chmetcnv>7BC4<chmetcnv w:st="on" unitname="F" sourcevalue="460" hasspace="False" negative="True" numbertype="1" tcsc="0">-460F</chmetcnv>-11D0-BC04<chmetcnv w:st="on" unitname="C" sourcevalue="80" hasspace="False" negative="True" numbertype="1" tcsc="0">-0080C</chmetcnv><chmetcnv w:st="on" unitname="a" sourcevalue="7055" hasspace="False" negative="False" numbertype="1" tcsc="0">7055A</chmetcnv>83">
<param name="DataURL" value="table.csv" />
<param name="UseHeader" value="True" />
</object>
是从文件table.csv中导入表中的Symbol列和Name列。
table.csv的内容与格式如下:
Symbol,Name
ADBE,Adobe Systems Incorporated
AIG,American International Group Inc
BA,Boeing Company
<div id="chart" width="500" height="300"></div>是显示图形。
<html>
<script type="text/javascript" src="js/mochikit/MochiKit.js"></script>
<script type="text/javascript" src="js/plotkit/Base.js"></script>
<script type="text/javascript" src="js/plotkit/Layout.js"></script>
<script type="text/javascript" src="js/plotkit/Canvas.js"></script>
<script type="text/javascript" src="js/plotkit/SweetCanvas.js"></script>
<script type="text/javascript" src="js/plotkit/excanvas.js"></script>
<script type="text/javascript" src="js/plotkit/PlotKit_Packed.js"></script>
<script type="text/javascript" src="js/plotkit/EasyPlot.js"></script>
<script type="text/javascript">
var plotter = null;
var newRows = new Array();
var options = {
"IECanvasHTC": "/plotkit/excanvas.htc",
"backgroundColor":Color.blackColor(),
"drawYAxis": true,
"shouldFill" : true,
"xAxis": [13,18],
"yAxis":[20,50]
};
function createData(){
var data3 = [
[13.00, 49.3+Math.random()*5],
[13.1, 48.2+Math.random()*5],
[13.2, 48.4+Math.random()*5],
[13.3, 48.1+Math.random()*5],
[13.4, 48.6+Math.random()*5],
[13.5, 48.9+Math.random()*5],
[14, 50.1+Math.random()*5],
[14.1, 49.1+Math.random()*5],
[14.2, 49.8+Math.random()*5],
[14.2, 50.2+Math.random()*5],
[14.3, 48.1+Math.random()*5],
[14.4, 51.1+Math.random()*5],
[14.5, 52.1+Math.random()*5],
[15, 48.1+Math.random()*5],
[15.1, 48.2+Math.random()*5],
[15.2, 48.4+Math.random()*5],
[15.3, 48.1+Math.random()*5],
[15.4, 48.6+Math.random()*5],
[15.5, 48.9+Math.random()*5],
[15.5, 49.8+Math.random()*5],
[16, 49.9+Math.random()*5],
[16.1, 48.2+Math.random()*5],
[16.2, 48.4+Math.random()*5],
[16.3, 48.1+Math.random()*5],
[16.4, 48.6+Math.random()*5],
[16.5, 48.9+Math.random()*5],
[16.5, 47.1+Math.random()*5],
[17, 48.1+Math.random()*5],
[17.1, 49.1+Math.random()*5],
[17.2, 49.8+Math.random()*5],
[17.3, 48.1+Math.random()*5],
[17.4, 51.1+Math.random()*5],
[17.5, 48.4+Math.random()*5]];
var data1 = [[13.00, 39.3+Math.random()*5],
[13.1, 38.2+Math.random()*5],
[13.2, 38.4+Math.random()*5],
[13.3, 38.1+Math.random()*5],
[13.4, 38.6+Math.random()*5],
[13.5, 38.9+Math.random()*5],
[14, 40.1+Math.random()*5],
[14.1, 39.1+Math.random()*5],
[14.2, 39.8+Math.random()*5],
[14.2, 40.2+Math.random()*5],
[14.3, 38.1+Math.random()*5],
[14.4, 41.1+Math.random()*5],
[14.5, 42.1+Math.random()*5],
[15, 38.1], [15.5, 39.8+Math.random()*5],
[16, 39.9], [16.5, 37.1+Math.random()*5],
[17, 38.1], [17.5, 38.4+Math.random()*5]];
</sp