Delphi的TCanvas类可以实现各种复杂的图形输出功能,基于近期项目的需求,利用它实现了一个很炫的动态折线图(模拟了资源管理器中CPU使用率的折线图),可以直观地展现出数值的实时变化情况。
这段代码里边有几个核心的地方:
- 首先是为了缓解刷新时画布闪烁,利用了双缓冲的原理;
- 其次结合队列,保证了数据的顺序压入;
- 还有就是一些简单的数组算法。
最终的效果如下:
单元代码如下:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls,Contnrs; const {* 网格间隔 *} GridSpace = 12; {* 移动步长(能够被间隔整除) *} MoveStep = 3; {* Y轴最大值(最大刻度) *} MaxY = 100; type TForm1 = class(TForm) Timer1: TTimer; Button1: TButton; Image1: TImage; procedure DrawPL(Shower:TImage); procedure Timer1Timer(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); procedure FormShow(Sender: TObject); procedure FormDestroy(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; {* 网格竖线X坐标数组 *} GridXPArr: array of Integer; {* 点坐标数组 *} PointLst: array of TPoint; {* 数值队列 *} YPQueue: TQueue; {* 数值指针 *} PYValue: PInteger; {* 网格偏移量 *} X: Word; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var YValue:Integer; begin Randomize; YValue := Random(100); //新Y坐标点按顺序压入队列 New(PYValue); PYValue^ := YValue; YPQueue.Push(PYValue); end; procedure TForm1.DrawPL(Shower:TImage); var Bit: TBitmap; i: Integer; PW,PH: Integer; YValue:Integer; begin //偏移量计算 Inc(X); if X = GridSpace div MoveStep then X := 0; //初始化画布(双缓冲) Bit := TBitmap.Create; try PW := Shower.Width; PH := Shower.Height; Bit.Width := PW; Bit.Height := PH; //初始化网格竖线X坐标数组长度为宽/间隔+1 SetLength(GridXPArr,PW div GridSpace + 1); with Bit.Canvas do begin Brush.Color := clBlack; Brush.Style := bsSolid; Rectangle(0,0,PW,PH); Pen.Color := $00408000; //画网格,根据偏移量实现动态效果 for i := 0 to PW div GridSpace + 1 do begin GridXPArr[i] := GridSpace * i - X * MoveStep; MoveTo(GridXPArr[i],0); LineTo(GridXPArr[i],PH); end; for i := 0 to PH div GridSpace do begin MoveTo(0,GridSpace * i); LineTo(PW,GridSpace * i); end; //画折线 Pen.Color := clLime; YValue := 0; //如果队列中有新的Y坐标点,则输出 if YPQueue.Count > 0 then begin PYValue := YPQueue.Pop; YValue := PYValue^; Dispose(PYValue); end; //画笔移动到起点位置 MoveTo(0,PH); //每执行一次函数,Y坐标向前移动一位,并连线各个点 for i := 0 to Length(PointLst) - 2 do begin PointLst[i].Y := PointLst[i + 1].Y; LineTo(PointLst[i+1].X,PointLst[i+1].Y); end; //按比例更新最后一位坐标点 PointLst[Length(PointLst)-1].X := PW; PointLst[Length(PointLst)-1].Y := PH - (YValue * PH div MaxY); //打印信息(可根据需要调整显示位置和内容) Brush.Style:=bsClear; Font.Color:=clYellow; TextOut(10,10,'数值:'+inttostr(YValue)); end; Shower.Canvas.Draw(0,0,Bit); finally Bit.Free; end; end; procedure TForm1.FormCreate(Sender: TObject); var i:Integer; begin YPQueue := TQueue.Create; //初始化坐标点个数为宽/步长+1 SetLength(PointLst,Image1.Width div MoveStep + 1); //初始化坐标点为X轴基线位置 for i := 0 to Length(PointLst) - 1 do begin PointLst[i].X := i*MoveStep; PointLst[i].Y := Image1.Height; end; end; procedure TForm1.FormDestroy(Sender: TObject); begin YPQueue.Free; end; procedure TForm1.FormShow(Sender: TObject); begin DrawPL(Image1); end; procedure TForm1.Timer1Timer(Sender: TObject); begin DrawPL(Image1); end; end.