2012-05-02 49 views
5

私は、TScrollingWinControl(およびTScrollBoxからコピーされたコード)に基づいて、TImageコントロールを使って簡単なコントロールを作成しています。私は幾分ズームが機能するようにしましたが、必ずしもフォーカスされたポイントにズームする必要はありません - スクロールバーはそれに応じて変更されず、中心ポイントにフォーカスが置かれます。TScrollBox内のTImageを特定のフォーカスにズームイン/ズームアウトしますか?

私は、このコントロールZoomTo(const X, Y, ZoomBy: Integer);にフォーカスをどこにズームするかを教えてもらいたいと思います。だからそれがズームするとき、私が渡した座標は「中央に」留まります。同時に、現在のビューの中央に置くように指示するZoomBy(const ZoomBy: Integer);も必要です。

例えば、マウスが画像の特定のポイントを指し示すシナリオがあり、コントロールを保持してマウスをスクロールすると、マウスポインタに注目してズームインする必要があります。一方、別のシナリオでは、現在のビューの中心(必ずしも画像の中心ではない)に焦点を合わせるだけで、ズームレベルを調整するコントロールをスライドさせることがあります。

問題は私の数学がこの時点で失われてしまい、これらのスクロールバーを調整する正しい数式がわからないということです。私はいくつかの計算方法を試しましたが、何も正しく動作しないようです。

私のコントロールは削除されています。私は関連するものだけにほとんどを削除し、元のユニットは600行以上のコードです。以下の最も重要な手順は、それはあなたが「ZoomBy()」に渡すときにX、Yのために参照してくださいしたいと思いますどのようなはっきりしていない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複数人の写真が表示されているとします。マウスは人の顔の中央に向けられています。ユーザーがコントロールキーを押したままマウスホイールをスクロールすると、マウスポインタが画像の同じ位置にあるまま、その人の顔が拡大されます。これが私が「Rect」ではなく「Point」にズームしている理由です。私は、マウスイベントをどのように処理するかを示すために上記の関連するコードをすべて含んでいることは確かです。 –

答えて

4

です。イメージの「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; 

はもちろん、あなたが1行にそれらをリファクタリングしたいあなたは一度だけラウンドを()を呼び出すことを丸め誤差を低減するようにします。

ここから自分でトレーニングを受けることができます。

+0

ありがとう、私は家に帰るときにそれをスピンします。 –