gpt4 book ai didi

delphi - 放大/缩小 TScrollBox 内的 TImage 到特定焦点?

转载 作者:行者123 更新时间:2023-12-03 15:01:22 30 4
gpt4 key购买 nike

我正在使用 TImage 控件制作一个基于 TScrollingWinControl(以及从 TScrollBox 复制的代码)的简单控件。我在某种程度上可以进行缩放,但它不一定会缩放到焦点 - 滚动条不会相应地改变以保持中心点处于焦点。

我希望能够告诉这个控件 ZoomTo(const X, Y, ZoomBy: Integer); 告诉它将焦点缩放到哪里。因此,当它缩放时,我传递的坐标将保持“居中”。同时,我还需要一个 ZoomBy(const ZoomBy: Integer); 告诉它保持在当前 View 的中心。

例如,有一种情况,鼠标指向图像的特定点,当按住 control 并向上滚动鼠标时,它应该放大并聚焦于鼠标指针。另一方面,另一种情况是滑动控件来调整缩放级别,在这种情况下,它只需要保持当前 View 的中心(不一定是图像的中心)聚焦。

问题是我的数学在这一点上迷失了,我无法找出调整这些滚动条的正确公式。我尝试了几种不同的计算方法,但似乎都不起作用。

这是我的控件的精简版本。我删除了大部分内容,仅保留相关内容,原始单元有 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.

最佳答案

在传递给“ZoomBy()”时,尚不清楚您希望为 X、Y 引用什么。我假设您已经为图像放置了一个“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() 一次以减少舍入误差。

我相信您可以自己在这里锻炼。

关于delphi - 放大/缩小 TScrollBox 内的 TImage 到特定焦点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10406584/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com