HTML5 虽然还在起草阶段,但这不妨碍 Html5 Canvas 的流行。不管是简单游戏还是绘制图表,Html5 Canvas 完全都可以胜任,各种主流浏览器都对 Html5 Canvas 支持得很好,低版本的 IE 也可以使用 Excanvas 来兼容。我们可以预见:Html5 Canvas 将代替 Flash 绘制交互性很强的股票图。本文推荐使用 Flotr,它是目前我见过的功能最全、性能最好、最适合绘制股票的 HTML5 JavaScript 图表库。不仅支持各种类型的图表(包括蜡烛图),还支持各种交互操作,如鼠标跟踪、选择缩放等。适当扩展,完全可以做出Google Finance) 那样的效果。本文的实例这么复杂,主要介绍一下绘制股票图基本流程和注意事项。
以 Yahoo 股票为例,页面加载完成后会绘制当天的收盘价格图。切换到不同的时间范围会重新绘制相应的图表。下面说一下基本流程。
知识准备
- 对 Flotr 有所了解,可以参看我上一篇文章:Flotr 快速入门指南:
- 对 prototype 框架有所了解,尤其是 Ajax 和事件部分。Flotr 整个库就是基于 prototype 框架的,本实例也会用到它。
编写后端服务
以 C#为例,编写 stock.aspx 股票 Ajax 服务
private int gmtoffset;
protected void Page_Load(object sender, EventArgs e) {
try {
string symbol, range, ys, yz;
symbol = Request["symbol"] == null ? "YHOO" : Request["symbol"].ToString();
range = Request["range"] == null ? "3y" : Request["range"].ToString();
ys = Request["ys"] == null ? "" : Request["ys"].ToString();
yz = Request["yz"] == null ? "" : Request["yz"].ToString();
StringBuilder sb = new StringBuilder("http://chartapi.finance.yahoo.com/instrument/1.0/" +
symbol + "/chartdata;type=quote");
if (!String.IsNullOrEmpty(range))
sb.Append(";range=" + range);
if (!String.IsNullOrEmpty(ys))
sb.Append(";ys=" + ys);
if (!String.IsNullOrEmpty(yz))
sb.Append(";yz=" + yz);
sb.Append("/csv");
WebClient client = new WebClient();
Stream stream = client.OpenRead(sb.ToString());
using (StreamReader reader = new StreamReader(stream)) {
int i = 0;
string row;
Response.Write("{\"data\":[");
while ((row = reader.ReadLine()) != null) {
if (row.IndexOf("gmtoffset") != -1) {
gmtoffset = Int16.Parse(row.Substring(10));
}
if (range == "1d") {
if (i > 16) {
writeLine(reader, row, range);
}
} else if (range == "5d") {
if (i > 21) {
writeLine(reader, row, range);
}
} else {
if (i > 17) {
writeLine(reader, row, range);
}
}
i++;
}
if (i < 4)
{
Response.Write("The chart is unavailable!");
}
Response.Write("]}");
}
} catch (Exception error) {
Response.Write(error.Message);
}
}
writeLine 方法的实现:
private void writeLine(StreamReader reader, string row, string range) {
Response.Write("[");
string[] arr = row.Split(',');
int len = arr.Length;
double s = 0;
if (gmtoffset == 0)
{
string value = arr[0];
DateTime dt1 = new DateTime(
int.Parse(value.Substring(0,4)),
int.Parse(value.Substring(4,2)),
int.Parse(value.Substring(6,2))
);
DateTime dt2 = Convert.ToDateTime("1970-01-01");
TimeSpan span = dt1 - dt2;
s = span.TotalSeconds;
}
else
{
s = int.Parse(arr[0]) + gmtoffset;
}
Response.Write((s * 1000).ToString() + ",");
Response.Write(arr[1].ToString());
if (!reader.EndOfStream)
{
Response.Write("],");
}
else
{
Response.Write("]");
}
}
简单调用:stock.aspx?symbol=YHOO&&range=' + range
数据准备
本实例是使用 Ajax 从 http://chartapi.finance.yahoo.com/instrument/1.0/抓取的 Json 数据,数据格式如下:
{"data":[[[1335346249000,603.2600],
[1335346317000,604.6700],
...[1335369540000,609.7100]]
}
本实例只绘制一个数据系列,组成数据系列每个数据点对应一个 x
和 y
坐标的数组。其中,x
坐标表示自 1970 年 1 月 1 日午夜(通用时间)以来的毫秒数,y
坐标表示该时间的收盘价。
绘制图表
使用 document 的 observe()
方法侦听 dom:loaded
事件,以便加载完 DOM 后调用 updateChart
方法绘制当天的收盘价格图。代码好下:
var f = null;
document.observe('dom:loaded', function() {
updateChart('1d', 10);
});
updateChart 有两个参数。第一个参数表示当前的时间范围,如 1d
、5d
、1y
等。另一个参数表示刻度线数量。
function updateChart(range, noTicks) {
new Ajax.Request('stock.aspx?symbol=YHOO && range=' + range, {
method: 'get',
onSuccess: function(transport) {
var json = transport.responseText.evalJSON();
if (json.data) {
...
f = Flotr.draw($('container'), [json.data], options);
}
else {
$('container').update('The data could not be retrieved.')
}
}
});
}
代码中选项设置比较多,如设置折线图填充为渐变色 ['#618192', '#749AAF']
,其实这就和 Flex Chart 组件的区域图差不多。
更新图表
最后我们在时间范围按钮的 onclick
事件中调用 updateChart()
方法,通过 Ajax 重新获取数据,重绘相应的图表。其实,也可以使用按钮的 observe()
方法,在侦听 click
事件中回调 updateChart()
方法来实现图表更新。
注意:
- 本实例中
x
轴使用了时间轴,从而简化了应用。但是当时间范围为五天时,会在当天与下一天之间绘制一大段多余的连接线。这不是什么 Bug,与时间轴的算法有关。Flex Chart 组件的时间轴也有这个问题。可以用线性轴或通过后台计算显示设置刻度线来修正这个问题,下次再聊这个话题吧。- 在显示数据点提示时,时间应该是本地时间。
了解更多
更多了解 Html5 JavaScript 图表库:
评论 (0)