當大尺寸圖片需在固定大小的視窗中顯示時,「漫游」 (Panning) 是最直覺的交互方式。本文展示純 VCL 控制項組合,無需呼叫 GDI API,即可實現流暢的滑鼠拖曳漫游效果,並解決 TImage 自身座標變更與滑鼠事件衝突導致閃爍的問題。
Panel1 (容器,固定大小,BorderStyle=bsSingle)
└─ Panel2 (Align=alClient,充滿 Panel1)
├─ Image1 (AutoSize=True,載入大圖)
└─ Label1 (Align=alClient,Transparent=True,覆蓋 Image1)
關鍵點:Label1 置於 Image1 上方並填滿 Panel2,攔截所有滑鼠事件。若直接用 Image1.OnMouseDown/Move/Up,滑鼠按下瞬間 Image1 會因座標改變而失去焦點,導致閃爍或拖曳失效。
private
Origin: TPoint; // 滑鼠按下時相對 Label1 的座標
ImgLeft, ImgTop: Integer; // 按下時 Image1 的 Left/Top
Visa1, Visa2: TPoint; // 邊界限制向量
CanMove: Boolean;
procedure TForm1.Label1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button = mbLeft then
begin
Origin := Point(X, Y);
ImgLeft := Image1.Left;
ImgTop := Image1.Top;
// Visa1:滑鼠相對 Image1 右下角的向量 (限制最大拖曳)
Visa1.X := X - (Image1.Width - Panel2.Width + Image1.Left);
Visa1.Y := Y - (Image1.Height - Panel2.Height + Image1.Top);
// Visa2:滑鼠相對 Image1 左上角的向量 (限制最小拖曳)
Visa2.X := X - Image1.Left;
Visa2.Y := Y - Image1.Top;
CanMove := True;
end;
end;
procedure TForm1.Label1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if CanMove then
begin
// 限制 X 軸:不超過 Visa1.X (右邊界) 與 Visa2.X (左邊界)
if X < Visa1.X then X := Visa1.X;
if X > Visa2.X then X := Visa2.X;
// 限制 Y 軸
if Y < Visa1.Y then Y := Visa1.Y;
if Y > Visa2.Y then Y := Visa2.Y;
Image1.Left := ImgLeft + (X - Origin.X);
Image1.Top := ImgTop + (Y - Origin.Y);
end;
end;
procedure TForm1.Label1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
CanMove := False;
end;
Visa2 對應圖片左上角不可超出視窗左上角 (即 Image1.Left <= 0、Image1.Top <= 0)。Visa1 對應圖片右下角不可縮回視窗內部 (即 Image1.Left + Image1.Width >= Panel2.Width)。Label1.OnMouseWheel 中調整 Image1.Width/Height 並重新計算邊界。Panel2.DoubleBuffered := True 減少閃爍。OnMouseUp 啟動 Timer 漸減位移。以 TLabel 作為透明觸控層、TImage.AutoSize 承載大圖、TPanel 限定可視區域,三者配合簡單的座標數學即可實作專業級圖像漫游。此架構易於擴展縮放、旋轉、標註等進階功能,是 Delphi 圖形應用的經典範式。