Estoy haciendo un control simple basado en un TScrollingWinControl
(y un código copiado de un TScrollBox
) con un control TImage
. De alguna manera hice que el zoom funcione, pero no necesariamente se acerca a un punto enfocado: las barras de desplazamiento no cambian en consecuencia para mantener enfocado el punto central.¿Acercar/alejar un TImage dentro de un TScrollBox a un foco particular?
Me gustaría poder decirle a este control ZoomTo(const X, Y, ZoomBy: Integer);
para decirle dónde enfocar el zoom. Entonces, cuando se amplía, las coordenadas que pasé quedarán 'centradas'. Al mismo tiempo, también necesito tener un ZoomBy(const ZoomBy: Integer);
que le indique que lo mantenga centrado en la vista actual.
Por ejemplo, habrá un escenario en el que el mouse apunta a un punto particular de la imagen, y cuando mantiene el control y desplaza el mouse hacia arriba, debe acercarse enfocando el puntero del mouse. Por otro lado, otro escenario sería deslizar un control para ajustar el nivel de zoom, en cuyo caso solo tiene que mantener enfocado el centro de la vista actual (no necesariamente el centro de la imagen).
El problema es que mi matemática se pierde en este momento, y no puedo encontrar la fórmula correcta para ajustar estas barras de desplazamiento. He intentado algunas formas diferentes de calcular, nada parece funcionar bien.
Aquí hay una versión reducida de mi control. Quité la mayoría a solo las cosas relevantes, la unidad original tiene más de 600 líneas de código. El procedimiento más importante a continuación es 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.
Ni siquiera puedo empezar a imaginarme qué haría el "zoom to". Me gustaría "acercarme" a un rectángulo, no a un punto. No puedo adivinar cómo se ve la implementación de tu clase, así que no puedo adivinar qué matemática necesitas, ni nadie más. –
@WarrenP Supongamos que se muestra una foto de varias personas, el mouse apunta al centro de la cara de una persona. Cuando el usuario sostiene la tecla de control y desplaza la rueda del mouse hacia arriba, se acerca a la cara de esa persona, con el puntero del mouse aún en la misma posición de la imagen. Es por eso que estoy acercando a un 'Punto' y no a un 'Rect'. Estoy bastante seguro de haber incluido todo el código relevante anterior para demostrar cómo manejo los eventos del mouse. –