2012-05-02 14 views
5

(ve TScrollBox'dan kopyalanan kod) TImage denetimi ile basit bir denetim yapıyorum. Çalışmak için biraz zum yaptım, ancak odaklanmış bir noktaya zıt bir görüntü oluşturmuyor - kaydırma noktası merkez noktasını odakta tutmak için buna göre değişmiyor.Belirli bir odağa TScrollBox içinde bir TImage in/in yakınlaştırma mı?

Bu denetimi ZoomTo(const X, Y, ZoomBy: Integer); anlatıp odağı yakınlaştırmak istediğinizi söyleyebilmeyi isterim. Bu yüzden, yakınlaştırdığımda, geçtiğim koordinatlar “ortalanmış” kalacaktır. Aynı zamanda, mevcut görünümde ortalamasını tutmasını söyleyen ZoomBy(const ZoomBy: Integer);'a da ihtiyacım var. Örneğin, farenin görüntünün belirli bir noktasında işaret edildiği tek bir senaryo olacak ve denetimi tutarken ve fareyi yukarı kaydırırken, fare imlecine odaklanmalı zum yapmalıdır. Diğer yandan, bir başka senaryo, yakınlaştırma seviyesini ayarlamak için bir kontrol kayarken, bu durumda sadece mevcut görüntünün merkezini (görüntünün merkezi değil) odaklanmış olması gerekir.

Sorun şu ki, matematik bu noktada kayboluyor ve bu kaydırma çubuklarını ayarlamak için doğru formülü anlayamıyorum. Birkaç farklı hesaplama yöntemi denedim, hiçbir şey doğru çalışmıyor gibi görünüyor.

Kontrolümün soyulmuş bir sürümü. En çok ilgili şeyleri kaldırdım, orijinal birim 600'den fazla kod satırı. En önemli prosedür aşağıda 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

"Yakınlaştır" noktasının ne yapacağını hayal bile edemiyorum bile. Bir dikdörtgeni "yakınlaştırır", bir noktaya değil. Sınıfının uygulanmasının neye benzediğini tahmin edemem, o yüzden hangi matematiğe ihtiyacın olduğunu, başka kimseyi de tahmin edemiyorum. –

+0

@WarrenP Birden çok kişinin fotoğrafını görüntülediğini farz edelim, fare bir kişinin yüzüne işaret ediyor. Kullanıcı kontrol tuşunu basılı tuttuğunda ve fare tekerleğini yukarı kaydırdığında, fare imlecinin hala resmin aynı pozisyonunda olduğu o kişinin yüzüne zum yapar. İşte bu yüzden bir “Nokta” ya da “Rect” e değil. Fare olaylarını nasıl ele aldığımı göstermek için yukarıdaki ilgili tüm kodu eklediğime eminim. –

cevap

4

Size X için başvurmak istiyorum ne olduğu belli değil, Y ') (ZoomBy' için geçerken olduğunu. Resim için bir 'OnMouseDown' işleyicisi koyduğunuzu ve koordinatların resmin üzerine tıkladığınız yere attığınızı, yani kaydırma kutusu koordinatlarına göre olmadıklarını varsayacağım. Bu böyle değilse, kendiniz değiştirebilirsiniz.

Bir dakikalığına yakınlaştırmayı unutalım, görevimizin kaydırma kutusundaki görüntüyü tıklattığımız noktayı merkezileştirmesine izin verin. Kolay, kaydırma kutusunun ortasının (ScrollBox.ClientWidth/2, ScrollBox.ClientHeight/2) olduğunu biliyoruz.

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


Şimdi yakınlaştırma düşünün: Yatay düşünün, biz, bizim tıklama noktası olacak biz buna clientWidth/2 eklerseniz, böylece bir noktaya kadar ilerlemek istiyorum. Tek yapmamız gereken X, Y konumlarını buna göre hesaplamak, kaydırma kutusunun boyutu değişmeyecek. CenterX := Center.X * ZoomFactor. Fakat dikkatli olun, 'ZoomFactor' burada etkili zum değil, görüntüye tıkladığımızda uygulanacak olan zum. Bunu belirlemek için resmin önce ve sonra boyutlarını kullanırız:

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; 

Tabii ki, bir satır içine planı ayrı ediyorum sadece bir kez Round() dediğimiz yuvarlama hatası azaltmak için.

Buradan kendiniz egzersiz yapabileceğinize eminim.

+0

Teşekkürler, eve döndüğümde bir spin vereceğim. –

İlgili konular