2012-05-02 94 views
5

我正在做TScrollingWinControl(和從TScrollBox複製的代碼)與TImage控制基於簡單的控制。我有些縮放工作,但它不一定縮小到一個焦點 - 滾動條不會相應地改變,以保持中心點的焦點。將TScrollBox中的TImage放大/縮小到特定焦點?

我希望能夠告訴這個控制ZoomTo(const X, Y, ZoomBy: Integer);告訴它的焦點縮放。所以當它放大時,我通過的座標將保持「居中」。同時,我還需要有一個ZoomBy(const ZoomBy: Integer);,它告訴它保持它在當前視圖中居中。

例如,將有一個場景,其中鼠標指向在圖像的特定點,並保持控制和滾動鼠標向上時,應該在聚焦在鼠標指針放大。另一方面,另一種情況是滑動控件來調整縮放級別,在這種情況下,它只需要保持當前視圖的中心(不一定是圖像的中心)。

問題是我的數學丟失在這一點上,我想不出合適的辦法來調整這些滾動條。我嘗試了幾種不同的計算方法,似乎沒有什麼工作正確。

這裏是我的控件的剝離版本。我刪除了最多隻有相關的東西,原單位是超過600行代碼。下面最重要的程序是SetZoom(const Value: Integer);

unit JD.Imaging; 

interface 

uses 
    Windows, Classes, SysUtils, Graphics, Jpeg, PngImage, Controls, Forms, 
    ExtCtrls, Messages; 

type 
    TJDImageBox = class; 

    TJDImageZoomEvent = procedure(Sender: TObject; const Zoom: Integer) of object; 

    TJDImageBox = class(TScrollingWinControl) 
    private 
    FZoom: Integer; //level of zoom by percentage 
    FPicture: TImage; //displays image within scroll box 
    FOnZoom: TJDImageZoomEvent; //called when zoom occurs 
    FZoomBy: Integer; //amount to zoom by (in pixels) 
    procedure MouseWheel(Sender: TObject; Shift: TShiftState; 
     WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); 
    procedure SetZoom(const Value: Integer); 
    procedure SetZoomBy(const Value: Integer); 
    public 
    constructor Create(AOwner: TComponent); override; 
    published 
    property Zoom: Integer read FZoom write SetZoom; 
    property ZoomBy: Integer read FZoomBy write SetZoomBy; 
    property OnZoom: TJDImageZoomEvent read FOnZoom write FOnZoom; 
    end; 

implementation 

{ TJDImageBox } 

constructor TJDImageBox.Create(AOwner: TComponent); 
begin 
    inherited Create(AOwner); 
    OnMouseWheel:= MouseWheel; 
    ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents, 
    csSetCaption, csDoubleClicks, csPannable, csGestures]; 
    AutoScroll := True; 
    TabStop:= True; 
    VertScrollBar.Tracking:= True; 
    HorzScrollBar.Tracking:= True; 
    Width:= 100; 
    Height:= 100; 
    FPicture:= TImage.Create(nil); 
    FPicture.Parent:= Self; 
    FPicture.AutoSize:= False; 
    FPicture.Stretch:= True; 
    FPicture.Proportional:= True; 
    FPicture.Left:= 0; 
    FPicture.Top:= 0; 
    FPicture.Width:= 1; 
    FPicture.Height:= 1; 
    FPicture.Visible:= False; 
    FZoom:= 100; 
    FZoomBy:= 10; 
end; 

destructor TJDImageBox.Destroy; 
begin 
    FImage.Free; 
    FPicture.Free; 
    inherited; 
end; 

procedure TJDImageBox.MouseWheel(Sender: TObject; Shift: TShiftState; 
    WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean); 
var 
    NewScrollPos: Integer; 
begin 
    if ssCtrl in Shift then begin 
    if WheelDelta > 0 then 
     NewScrollPos := Zoom + 5 
    else 
     NewScrollPos:= Zoom - 5; 
    if NewScrollPos >= 5 then 
     Zoom:= NewScrollPos; 
    end else 
    if ssShift in Shift then begin 
    NewScrollPos := HorzScrollBar.Position - WheelDelta; 
    HorzScrollBar.Position := NewScrollPos; 
    end else begin 
    NewScrollPos := VertScrollBar.Position - WheelDelta; 
    VertScrollBar.Position := NewScrollPos; 
    end; 
    Handled := True; 
end; 

procedure TJDImageBox.SetZoom(const Value: Integer); 
var 
    Perc: Single; 
begin 
    FZoom := Value; 
    if FZoom < FZoomBy then 
    FZoom:= FZoomBy; 
    Perc:= FZoom/100; 
    //Resize picture to new zoom level 
    FPicture.Width:= Trunc(FImage.Width * Perc); 
    FPicture.Height:= Trunc(FImage.Height * Perc); 
    //Move scroll bars to properly position the center of the view 
    //This is where I don't know how to calculate the 'center' 
    //or by how much I need to move the scroll bars. 
    HorzScrollBar.Position:= HorzScrollBar.Position - (FZoomBy div 2); 
    VertScrollBar.Position:= VertScrollBar.Position - (FZoomBy div 2); 
    if assigned(FOnZoom) then 
    FOnZoom(Self, FZoom); 
end; 

procedure TJDImageBox.SetZoomBy(const Value: Integer); 
begin 
    if FZoomBy <> Value then begin 
    FZoomBy := EnsureRange(Value, 1, 100); 
    Paint; 
    end; 
end; 

end. 
+1

我甚至無法想象「縮放到」點會做什麼。我會「縮放」到一個矩形,而不是一個點。我無法猜測你的班級的實施情況,因此我無法猜測你需要什麼數學,也沒有其他人。 –

+0

@WarrenP假設顯示了多人的照片,鼠標指向了人臉的中心。當用戶拿着控制鍵並向上滾動鼠標滾輪時,它將放大到該人的臉部,鼠標指針仍處於圖片的相同位置。這就是爲什麼我要縮放到一個「Point」而不是「Rect」。我敢肯定,我在上面列出了所有相關代碼,以演示如何處理鼠標事件。 –

回答

4

目前尚不清楚你想要什麼參考的X,通過爲Y,當「ZoomBy()」。我假設你已經爲圖像放置了一個'OnMouseDown'處理程序,座標指向你點擊圖像的位置,即它們不是相對於滾動框座標。如果不是這樣,你可以自己調整它。

讓我們忘掉縮放一分鐘,讓我們的任務是爲中心,我們單擊滾動框圖像上的點。很容易,我們知道滾動框的中心位於(ScrollBox.ClientWidth/2,ScrollBox.ClientHeight/2)。想想水平,我們要達到滾動到一個點,這樣,如果我們ClientWidth/2添加到它,這將是我們的點擊點:

procedure ScrollTo(CenterX, CenterY: Integer); 
begin 
    ScrollBox.HorzScrollBar.Position := CenterX - Round(ScrollBox.ClientWidth/2); 
    ScrollBox.VertScrollBar.Position := CenterY - Round(ScrollBox.ClientHeight/2); 
end; 


現在考慮縮放。我們所要做的就是相應地計算X,Y位置,滾動框的大小不會改變。 CenterX := Center.X * ZoomFactor。但要小心,'ZoomFactor'在這裏不是有效的縮放,它是當我們點擊圖像時將應用的縮放。我將使用圖像的之前和之後的尺寸來確定:

procedure ZoomTo(CenterX, CenterY, ZoomBy: Integer); 
var 
    OldWidth, OldHeight: Integer; 
begin 
    OldWidth := FImage.Width; 
    OldHeight := FImage.Height; 

    // zoom the image, we have new image size and scroll range 

    CenterX := Round(CenterX * FImage.Width/OldWidth); 
    ScrollBox.HorzScrollBar.Position := CenterX - Round(ScrollBox.ClientWidth/2); 

    CenterY := Round(CenterY * FImage.Height/OldHeight); 
    ScrollBox.VertScrollBar.Position := CenterY - Round(ScrollBox.ClientHeight/2); 
end; 

當然,你會重構它們變成一條線,讓你調用ROUND()只有一次,以減少舍入誤差。

我相信你可以從這裏自己鍛鍊。

+0

謝謝,當我回家時我會給它一個旋轉。 –