Wykonuję prostą kontrolę na podstawie TScrollingWinControl
(i kodu skopiowanego z TScrollBox
) z kontrolką TImage
. Trochę się zbliża do pracy, ale niekoniecznie zbliża się do punktu skupienia - paski przewijania nie zmieniają się odpowiednio, aby zachować punkt środkowy.Powiększanie/zmniejszanie TImage wewnątrz TScrollBox do konkretnego fokusa?
Chciałbym móc powiedzieć tej kontroli ZoomTo(const X, Y, ZoomBy: Integer);
, aby powiedzieć mu, gdzie należy ustawić zoom. Gdy zbliża się, współrzędne, które minęły, pozostaną "wyśrodkowane". W tym samym czasie muszę również mieć ZoomBy(const ZoomBy: Integer);
, który mówi, aby zachować go wyśrodkowany w bieżącym widoku.
Na przykład, będzie jeden scenariusz, w którym mysz zostanie wskazana w określonym punkcie obrazu, a gdy przytrzymamy sterowanie i przewijamy myszką w górę, powinna ona powiększyć skupienie na wskaźniku myszy. Z drugiej strony, kolejnym scenariuszem byłoby przesuwanie kontrolki w celu dostosowania poziomu zoomu, w takim przypadku wystarczy, aby skupić się na środku bieżącego widoku (niekoniecznie na środku obrazu).
Problem polega na tym, że moja matematyka zostaje utracona w tym momencie i nie mogę znaleźć odpowiedniej formuły do dostosowania tych pasków przewijania. Próbowałem kilku różnych sposobów obliczania, nic nie działa poprawnie.
Oto pozbawiona wersji moja kontrola. Usunąłem większość tylko do odpowiednich rzeczy, oryginalna jednostka ma ponad 600 linii kodu. Najważniejszą poniżej procedura jest 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.
Nie mogę sobie nawet wyobrazić, co zrobi punkt "przybliżania". Chciałbym "powiększyć" prostokąta, a nie punkt. Nie potrafię odgadnąć, jak wygląda twoja klasa, więc nie mogę zgadnąć, jakiej matematyki potrzebujesz, ani której nie możesz mieć. –
@WarrenP Załóżmy, że wyświetlane jest zdjęcie wielu osób, mysz jest wycelowana w środek twarzy osoby. Gdy użytkownik przytrzyma klawisz sterujący i przewiń tarczę myszy w górę, przybliża twarz tej osoby, a wskaźnik myszy nadal znajduje się w tej samej pozycji obrazu. Właśnie dlatego przybliżam się do "Point", a nie "Rect". Jestem prawie pewien, że zawarłem wszystkie odpowiednie kody powyżej, aby pokazać, jak radzę sobie ze zdarzeniami myszy. –