您好,登錄后才能下訂單哦!
對于一些內部系統的項目,各種圖表是在所難免的,因為圖表可以更加清晰的表達出想看到的數據。
因為之前從來沒有做過關于圖表的東西,唯一能想到的就是“驗證碼”,所以應該是一個思路,用GDI去搞。
數據懶著去搞了,記得前些日子在亞航官網查機票,就想到這些數據還挺適合做這個DEMO的,所以就先借用一下亞航的數據嘍。
數據大概就是這樣子的,日期及價錢。
我選了其中“9月27日-10月10日”正好兩周的數據作為此次Demo的測試數據。
原理就是跟實現驗證碼一模一樣,通過給<img>標簽修改src屬性來操作柱形圖的變化,src屬性導向的頁面是另外一個頁,非顯示柱形圖本頁。
然后就是如何利用GDI畫柱形圖。
先上一下效果圖:
下面看一下部分代碼片段
html代碼:
<body> <form id="form1" runat="server"> <div> <asp:ImageButton ID="img_last" runat="server" ImageUrl="~/Left.jpg" AlternateText="Left" OnClientClick="return false;" /> <img id="img_bar" src="GenerateImage.aspx?page_num=0&from=北京&to=清邁" alt="" /> <asp:ImageButton ID="img_next" runat="server" ImageUrl="~/Right.jpg" AlternateText="Right" OnClientClick="return false;" /> </div> <asp:HiddenField ID="hf_pagenum" runat="server" Value="0" /> <asp:HiddenField ID="hf_recordnum" runat="server" /> </form> </body>
js代碼:
$(function () { var $pagenum = $("#hf_pagenum"); var $recordnum = $("#hf_recordnum"); $("#img_last").click(function () { if (parseInt($pagenum.val()) - 1 >= 0) { $pagenum.val(parseInt($pagenum.val()) - 1); $("#img_bar").attr("src", "GenerateImage.aspx?page_num=" + $pagenum.val() + "&from=北京&to=清邁"); } }) $("#img_next").click(function () { if (parseInt($pagenum.val()) + 1 < parseInt($recordnum.val())/7) { $pagenum.val(parseInt($pagenum.val()) + 1); $("#img_bar").attr("src", "GenerateImage.aspx?page_num=" + $pagenum.val() + "&from=北京&to=清邁"); } }) })
后臺代碼:
1、一些坐標點的聲明
private readonly int WEEKDAYS = 7;//一周的天數 private readonly int Y_UNIT_NUM = 10; private readonly int CHAR_X_LEFT = 110; private readonly int CHAR_X_TOP = 388; private readonly int CHAR_Y_LEFT = 30; private readonly int CHAR_Y_TOP = 79; private readonly int TITLE_LEFT = 140;//柱形圖標題 起始X坐標 private readonly int TITLE_TOP = 18;//柱形圖標題 起始Y坐標 private readonly int TIME_LEFT = 170;//柱形圖日期 起始X坐標 private readonly int TIME_TOP = 48;//柱形圖日期 起始Y坐標 private readonly int GRID_X_LEFT = 150;//背景網格 X左邊位移 private readonly int GRID_X_TOP = 80;//背景網格 X上邊位移 private readonly int GRID_X_BOTTOM = 380;//背景網格 X下邊位移 private readonly int GRID_Y_TOP = 110; private readonly int GRID_Y_LEFT = 100; private readonly int GRID_Y_RIGHT = 580; private readonly int GRID_UNIT_WIDTH = 50;//網格單元寬度 private readonly int GRID_UNIT_HEIGHT = 30;//網格單元高度 private readonly int DATA_UNIT_WIDTH = 40;//柱單元寬度 private readonly int DATA_UNIT_HEIGHT = 30;//柱單元高度
2、篩選當頁數據方法
為了是通過點擊下一頁,上一頁按鈕,對數據進行篩選,選出當頁顯示數據進行繪制
protected DataSet QueryDisplayRecord(int pagenum) { DataSet ds = new DataSet(); DataTable dt = new DataTable(); dt.Columns.Add(new DataColumn("date")); dt.Columns.Add(new DataColumn("price")); for (int i = 7 * pagenum; i < (goStr.Count >= WEEKDAYS * (pagenum + 1) ? WEEKDAYS * (pagenum + 1) : goStr.Count); i++) { DataRow dr = dt.NewRow(); dr["date"] = goStr.Keys.Take(i + 1).Last(); ; dr["price"] = goStr.Values.Take(i + 1).Last(); ; dt.Rows.Add(dr); } ds.Tables.Add(dt); return ds; }
哦,對了,這里用的是dictionary<string, string>暫時存的模擬數據,正常情況當然要是通過數據庫了。
3、生成圖片
protected void CreateImage(string from, string to, int pagenum, DataSet ds) { Font font_Note = new System.Drawing.Font("Arial", 9, FontStyle.Regular);//x軸,y軸,解釋內容字體 小字 Font font_GraphicName = new System.Drawing.Font("微軟雅黑", 18, FontStyle.Regular);//圖表名稱 字體 大字 Font font_GraphicTime = new System.Drawing.Font("微軟雅黑", 14, FontStyle.Regular);//圖表時間 字體 大字 Brush brush_Blue = new SolidBrush(Color.Black);//藍色線條 if (ds != null) { int height = 620, width = 650; System.Drawing.Bitmap p_w_picpath = new System.Drawing.Bitmap(width, height); Graphics g = Graphics.FromImage(p_w_picpath); g.Clear(Color.White); System.Drawing.Drawing2D.LinearGradientBrush brush = new System.Drawing.Drawing2D.LinearGradientBrush(new Rectangle(0, 0, p_w_picpath.Width, p_w_picpath.Height), Color.LightGray, Color.LightGray, 1.2f, true); //調用FillRectangle方法使用指定顏色填充柱形圖的內部 g.FillRectangle(Brushes.WhiteSmoke, 0, 0, width, height); Brush brush2 = new SolidBrush(Color.Black); //應用DrawString方法輸出文字信息 g.DrawString(from + " 至 " + to + " 機票分析圖 (單位:CNY)", font_GraphicName, brush_Blue, new PointF(TITLE_LEFT, TITLE_TOP)); //畫圖片的邊框線 g.DrawRectangle(new Pen(Color.Black), 0, 0, p_w_picpath.Width - 1, p_w_picpath.Height - 1); Pen mypen = new Pen(brush, 1); //繪制橫向線條 for (int i = 0; i < WEEKDAYS; i++) { g.DrawLine(mypen, GRID_X_LEFT + GRID_UNIT_WIDTH * i, GRID_X_TOP, GRID_X_LEFT + GRID_UNIT_WIDTH * i, GRID_X_BOTTOM); } Pen mypen1 = new Pen(Color.Black, 2); g.DrawLine(mypen1, GRID_X_LEFT - GRID_UNIT_WIDTH, GRID_X_TOP, GRID_X_LEFT - GRID_UNIT_WIDTH, GRID_X_BOTTOM); for (int i = 0; i < Y_UNIT_NUM; i++) { g.DrawLine(mypen, GRID_Y_LEFT, GRID_Y_TOP + GRID_UNIT_HEIGHT * i, GRID_Y_RIGHT, GRID_Y_TOP + GRID_UNIT_HEIGHT * i); } g.DrawLine(mypen1, GRID_Y_LEFT, GRID_Y_TOP + GRID_UNIT_HEIGHT * (Y_UNIT_NUM - 1), GRID_Y_RIGHT, GRID_Y_TOP + GRID_UNIT_HEIGHT * (Y_UNIT_NUM - 1)); //x軸 for (int i = 0; i < WEEKDAYS; i++) { g.DrawString(ds.Tables[0].Rows[i][0].ToString().Substring(ds.Tables[0].Rows[i][0].ToString().LastIndexOf(',') + 1), font_Note, Brushes.Black, CHAR_X_LEFT + GRID_UNIT_WIDTH * i, CHAR_X_TOP); } //y軸 string[] money = { "5000CNY", "4500CNY", "4000CNY", "3500CNY", "3000CNY", "2500CNY", "2000CNY", "1500CNY", "1000CNY", "500CNY" }; for (int i = 0; i < Y_UNIT_NUM; i++) { g.DrawString(money[i].ToString(), font_Note, Brushes.Black, CHAR_Y_LEFT, CHAR_Y_TOP + DATA_UNIT_HEIGHT * i); } int[] Count = new int[WEEKDAYS]; int j = 0; for (j = 0; j < WEEKDAYS; j++) { Count[j] = Convert.ToInt32(ds.Tables[0].Rows[j][1].ToString()); } //顯示柱狀效果 ImageAttributes imgAtt = new ImageAttributes(); imgAtt.SetWrapMode(WrapMode.TileFlipXY);//為了使柱形圖片無漸變色效果 for (int i = 0; i < WEEKDAYS; i++) { g.DrawImage(bitmap, new Rectangle(GRID_Y_LEFT + GRID_UNIT_WIDTH * i + (GRID_UNIT_WIDTH - DATA_UNIT_WIDTH) / 2, (int)(380 - (Count[i] > 5000 ? 5000 : Count[i]) * 3F / 50F), 40, (int)(Count[i] * 3F / 50F)), 0, 0, bitmap.Width, bitmap.Height, GraphicsUnit.Pixel, imgAtt); } //創建其支持存儲區為內存的流 System.IO.MemoryStream ms = new System.IO.MemoryStream(); //將此圖像以指定格式保存到指定流中 p_w_picpath.Save(ms, System.Drawing.Imaging.ImageFormat.Gif); //清除緩沖區流中的所有內容輸出 Response.ClearContent(); //設置輸出MIME類型 Response.ContentType = "p_w_picpath/Gif"; Response.BinaryWrite(ms.ToArray()); } else { int height = 380, width = 570; System.Drawing.Bitmap p_w_picpath = new System.Drawing.Bitmap(width, height); Graphics g = Graphics.FromImage(p_w_picpath); g.Clear(Color.White); //應用DrawString方法輸出文字信息 g.DrawString("無符合搜索條件數據", font_GraphicName, brush_Blue, new PointF(140, 20)); //創建其支持存儲區為內存的流 System.IO.MemoryStream ms = new System.IO.MemoryStream(); //將此圖像以指定格式保存到指定流中 p_w_picpath.Save(ms, System.Drawing.Imaging.ImageFormat.Gif); //清除緩沖區流中的所有內容輸出 Response.ClearContent(); //設置輸出MIME類型 Response.ContentType = "p_w_picpath/Gif"; Response.BinaryWrite(ms.ToArray()); } }
可能大家看的比較暈,因為全是一些坐標的計算,這個大家就不用太多考慮,就知道圖表和柱形的一個思路就行了。
最后總結一下,其實畫圖指定是離不開一些點在圖中位移的計算,這里最好是自己能在草紙上比劃一下,設計一下,這樣會畫的更快一些,同時也不容易被一大堆的數據搞暈。另外一點就是數據這塊,柱形的比例搞好就行了,公式大概就是H(格子的實際高度)/h(單位刻度表示的價錢)=X(柱形的高度,我們所有求的)/price(當前柱的實際表示價錢,數據庫中存的)。最后也就是X= H*price/h 這樣。
想做出更加完美,智能的圖表還有許多地方可以改進:
1、y軸展示的刻度我們可以動態變化,根據所查詢出數據的最大值進行刻度計算,找出合適的單位刻度
2、x軸可以做一個額外的注釋(當然,這里就是星期,也不太用到),但是如果是一些內容的話,可以根據坐標范圍做鼠標移上有提示那種。
3、可以把柱形做的再美一點,更加立體一些,當然,這是需要依靠美工的力量。
Demo地址:http://down.51cto.com/data/1878284
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。