使用 Flotr 绘制股票图

使用 Flotr 绘制股票图

Flying
2013-12-10 / 0 评论 / 178 阅读 / 正在检测是否收录...

HTML5 虽然还在起草阶段,但这不妨碍 Html5 Canvas 的流行。不管是简单游戏还是绘制图表,Html5 Canvas 完全都可以胜任,各种主流浏览器都对 Html5 Canvas 支持得很好,低版本的 IE 也可以使用 Excanvas 来兼容。我们可以预见:Html5 Canvas 将代替 Flash 绘制交互性很强的股票图。本文推荐使用 Flotr,它是目前我见过的功能最全、性能最好、最适合绘制股票的 HTML5 JavaScript 图表库。不仅支持各种类型的图表(包括蜡烛图),还支持各种交互操作,如鼠标跟踪、选择缩放等。适当扩展,完全可以做出Google Finance) 那样的效果。本文的实例这么复杂,主要介绍一下绘制股票图基本流程和注意事项。

chart-stock.svg

以 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]]
}

本实例只绘制一个数据系列,组成数据系列每个数据点对应一个 xy 坐标的数组。其中,x 坐标表示自 1970 年 1 月 1 日午夜(通用时间)以来的毫秒数,y 坐标表示该时间的收盘价。

绘制图表

使用 document 的 observe() 方法侦听 dom:loaded 事件,以便加载完 DOM 后调用 updateChart 方法绘制当天的收盘价格图。代码好下:

var f = null;
document.observe('dom:loaded', function() {
  updateChart('1d', 10);
});

updateChart 有两个参数。第一个参数表示当前的时间范围,如 1d5d1y等。另一个参数表示刻度线数量。

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 图表库:

5

评论 (0)

取消