以下示例将Timage放置在表单中;它会在其中创建一个图形,然后通过图像上的两个鼠标事件(MouseDown和MouseMove),用鼠标在屏幕上正确移动图形;
现在,如果我们从代码中删除注释{MyImage.RotationAngle:= 120;}并激活图形先前的旋转加90度,则移动不正确。我看不出问题是什么。 [在我们可以通过鼠标在屏幕上移动结果的同时,必须能够多次旋转屏幕上的图形。]
unit UMoveShape;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Objects, FMX.StdCtrls, FMX.Controls.Presentation,
FireDAC.UI.Intf, FireDAC.FMXUI.Async, FireDAC.Stan.Intf,
FMX.DialogService.Async,System.UIConsts,System.Math.Vectors, FireDAC.Comp.UI;
type
TForm16 = class(TForm)
MyImage: TImage;
procedure FormCreate(Sender: TObject);
procedure MyImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
procedure MyImageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
private
{ Private declarations }
public
Xdiff,Ydiff: single;
{ Public declarations }
end;
var
Form16: TForm16;
implementation
{$R *.fmx}
procedure TForm16.FormCreate(Sender: TObject);
Var
MyRect1, MyRect2: TRectF;
Path: TPathData;
begin
MyImage.Width := 500;
MyImage.Height := 500;
MyImage.Bitmap.SetSize(Round(MyImage.Width), Round(MyImage.Height));
MyRect1 := TRectF.Create(98, 100, 200, 200);
MyRect2 := TRectF.Create(70, 90, 225, 210);
Path := TPathData.Create;
Path.AddEllipse(MyRect1);
Path.AddRectangle(MyRect2, 0, 0, AllCorners);
MyImage.Bitmap.Canvas.BeginScene;
MyImage.Bitmap.Canvas.DrawPath(path, 200);
MyImage.Bitmap.Canvas.EndScene;
{If we rotate the image more than 90 degrees for example, the movement of the
image with the cursor is erratic using the mouseDown and MouseMove routines.
On the other way, if we don't rotate the movement is perfect.}
// MyImage.RotationAngle := 120;
end;
procedure TForm16.MyImageMouseDown (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
begin
if (ssleft in shift) then
begin
Xdiff := X;
Ydiff := y;
end;
end;
procedure TForm16.MyImageMouseMove (Sender: TObject; Shift: TShiftState; X, Y: Single);
begin
if (ssleft in shift) then
begin
MyImage.Position.X := MyImage.Position.X + X - Xdiff;
MyImage.Position.y := MyImage.Position.Y + Y - YDiff;
end;
end;
end.
答案 0 :(得分:2)
旋转的图像无法按照您想要的方式移动的原因是,OnMouseMove事件返回的X和Y值与现在旋转的图像客户端有关。因此,例如,如果您有一幅宽度为100像素的图像,请将其旋转180度,然后将鼠标光标移动到图像的左侧,则X值将为100而不是0,就好像该图像不会旋转一样。
因此,为了正确地旋转旋转的图像,您需要使用从图像旋转点开始的矢量旋转来相应地转换X,Y值以适应图像旋转。
编辑:为了避免自己对X和Y进行矢量变换,可以让Delphi替您完成。为此,您可以使用ClientToScreen方法将所有位置值从本地客户位置转换为Screen位置,然后最后使用ScreenToClient方法将新计算出的位置从Screen位置转换回客户位置。
以下是此类代码的示例:
var
Form1: TForm1;
StartDragPos: TPointF;
Dragging: Boolean;
implementation
{$R *.fmx}
procedure TForm1.Image1MouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
if (ssleft in shift) then
begin
StartDragPos := ClientToScreen(Point(Round(X),Round(Y)));
Dragging := True;
end;
end;
procedure TForm1.Image1MouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Single);
begin
if Dragging then
begin
Image1.Position.Point := ScreenToClient(ClientToScreen(Image1.Position.Point + ClientToScreen(Point(Round(X),Round(Y))) - StartDragPos));
end;
end;
procedure TForm1.Image1MouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Single);
begin
Dragging := False;
end;
PS:同样,在编写此示例代码时,我发现您实际上可以直接使用Points进行数学运算,而不是分别计算X和Y值。至少您可以在Delphi 10.2 Tokyo中做到这一点。我不确定使用较旧的Delphi版本。
因此,我的示例为什么要自己加上和减去点,而不是单独的X和Y值。
答案 1 :(得分:1)
最后,我与创建的解决方案共享代码。
测试视频:https://1drv.ms/v/s!AqdWVn6k-HLbgqRw352kQ1HjuIJ5Hw
旋转图形移动问题的解决方案 通过鼠标,无需向量即可解析,从而创建与原始图像重叠的透明蒙版。如评论中所示,存在一些限制。
鼠标移动例程将基于蒙版Timage中的事件,该事件始终保持不旋转0度,而旋转的图像会在移动蒙版时复制蒙版的位置。实际上,这是另外两行代码,并将掩码声明为新的空Tmage,复制了原始大小和位置。我让完整的代码正常工作。
unit UMoveShape;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
FMX.Objects, FMX.StdCtrls, FMX.Controls.Presentation,
FireDAC.UI.Intf, FireDAC.FMXUI.Async, FireDAC.Stan.Intf,
FMX.DialogService.Async, System.UIConsts, System.Math.Vectors, FireDAC.Comp.UI, FMX.Edit,
FMX.ScrollBox, FMX.Memo;
type
TForm16 = class (TForm)
MyImage: TImage;
MyImageMask: TImage;
EditDegrees: TEdit;
ButtonRotate: TButton;
procedure FormCreate (Sender: TObject);
procedure MyImageMaskMouseMove (Sender: TObject; Shift: TShiftState; X, Y: Single);
procedure MyImageMaskMouseDown (Sender: TObject; Button: TMouseButton; Shift: TShiftState; X,
Y: Single);
procedure ButtonRotateClick (Sender: TObject);
private
{ Private declarations }
public
Xdiff, Ydiff: single;
MyRect1, MyRect2: TRectF;
Path: TPathData;
{ Public declarations }
end;
var
Form16 : TForm16;
implementation
{$R *.fmx}
procedure TForm16.ButtonRotateClick (Sender: TObject);
begin
MyImage.RotationAngle := EditDegrees.Text.ToSingle;
end;
procedure TForm16.FormCreate (Sender: TObject);
begin
// Original Image
MyImage.Width := 300;
MyImage.Height := 300;
MyImage.Bitmap.SetSize (Round (MyImage.Width), Round (MyImage.Height));
MyRect1 := TRectF.Create (98, 100, 200, 200);
MyRect2 := TRectF.Create (70, 90, 225, 210);
Path := TPathData.Create;
Path.AddEllipse (MyRect1);
Path.AddRectangle (MyRect2, 0, 0, AllCorners);
MyImage.Bitmap.Canvas.BeginScene;
MyImage.Bitmap.Canvas.DrawPath (path, 200);
MyImage.Bitmap.Canvas.EndScene;
MyImageMask.Width := MyImage.Width;
MyImageMask.Height := MyImage.Height;
MyImageMask.Position := MyImage.Position;
end;
procedure TForm16.MyImageMaskMouseDown (Sender: TObject; Button: TMouseButton; Shift: TShiftState;
X,
Y: Single);
begin
if (ssleft in shift) then
begin
Xdiff := X;
Ydiff := y;
end;
end;
procedure TForm16.MyImageMaskMouseMove (Sender: TObject; Shift: TShiftState; X, Y: Single);
begin
if (ssleft in shift) then
begin
MyImageMask.Position.X := MyImage.Position.X + X - Xdiff;
MyImageMask.Position.y := MyImage.Position.Y + Y - YDiff;
MyImage.Position.X := MyImageMask.Position.X;
MyImage.Position.y := MyImageMask.Position.Y;
Form16.Caption := 'X:' + MyImage.Position.X.ToString + ' Y: ' + MyImage.Position.y.ToString;
end;
end;
end.
答案 2 :(得分:0)
//First of all load a simple Bitmap in your MyImage.
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls;
type
TRGB = packed record
b: byte;
g: byte;
r: byte;
end;
PRGB = ^TRGB;
type
TForm1 = class(TForm)
MyImage: TImage;
procedure FormCreate(Sender: TObject);
procedure MyImageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
procedure MyImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
private
{ Private declarations }
procedure Rotate( bmp:TBitmap; Angle: double;fillColor: TColor);
public
{ Public declarations }
Xdiff,Ydiff: integer;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
MyImage.Width := 500;
MyImage.Height := 500;
Rotate ( MyImage.Picture.Bitmap, 120, form1.Color);
end;
procedure TForm1.MyImageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
begin
if (ssleft in shift) then
begin
Xdiff := X;
Ydiff := y;
end;
end;
procedure TForm1.MyImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer);
begin
if (ssleft in shift) then
begin
MyImage.left := MyImage.left + X - Xdiff;
MyImage.top := MyImage.top + Y - YDiff;
end;
end;
procedure TForm1.Rotate( bmp:TBitmap; Angle: double; fillColor: TColor);
var
dst: TBitmap;
parx1, parx2: pinteger;
a, tsin, tcos, cxSrc, cySrc, cxDest, cyDest: Double;
fx, fy: Integer;
dw, dh, x, y: Integer;
px: pbyte;
arx1, arx2: pintegerarray;
ary1, ary2: Integer;
ps, pd: pbyte;
dw1, dh1: Integer;
prgb_s, prgb_d: PRGB;
srcrows: ppointerarray;
iangle: Integer;
prog, lprog: Integer;
aTRGB:trgb;
procedure Rot90(inv: Boolean);
var
x, y: Integer;
mulx, muly, addx, addy: Integer;
begin
dw := bmp.height; dw1 := dw-1;
dh := bmp.Width; dh1 := dh-1;
dst:= TBitmap.Create;
dst.Width := dw;
dst.height := dw;
dst.PixelFormat:= pf24bit;
dst.Canvas.Brush.Color := fillColor;
dst.Canvas.FillRect(Rect (0,0,dw ,dh) );
if inv then
begin
mulx := -1;
muly := 1;
addx := dw1;
addy := 0;
end
else
begin
mulx := 1;
muly := -1;
addx := 0;
addy := dh1;
end;
for x := 0 to dw1 do
begin
ps := bmp.ScanLine[addx+x*mulx];
prgb_s := PRGB(ps);
for y := 0 to dh1 do
begin
prgb_d := dst.Scanline[addy+y*muly];
inc(prgb_d, x);
prgb_d^ := prgb_s^;
inc(prgb_s);
end;
end;
end;
procedure Rot180;
var
x, y: Integer;
begin
dw := bmp.width; dw1 := dw-1;
dh := bmp.height; dh1 := dh-1;
dst:= TBitmap.Create;
dst.Width := dw;
dst.height := dw;
dst.PixelFormat:= pf24bit;
dst.Canvas.Brush.Color := fillColor;
dst.Canvas.FillRect(Rect (0,0,dw ,dh) );
for y := 0 to dh1 do
begin
pd := dst.ScanLine[dh1 - y];
ps := bmp.Scanline[y];
prgb_d := PRGB(pd);
prgb_s := PRGB(ps);
inc(prgb_s, dw1);
for x := 0 to dw1 do
begin
prgb_d^ := prgb_s^;
inc(prgb_d);
dec(prgb_s);
end;
end;
end;
begin
if (Frac(angle) = 0) and ((trunc(angle) mod 90) = 0) then
begin
iangle := trunc(angle) mod 360;
case iangle of
90 : Rot90(false);
180 : Rot180;
270 : Rot90(true);
-90 : Rot90(true);
-180 : Rot180;
-270 : Rot90(false);
end;
bmp.Assign( dst );
FreeAndNil(dst);
exit;
end;
a := angle * pi / 180;
dw := round(abs(bmp.width * cos(a)) + abs(bmp.height * sin(a)));
dh := round(abs(bmp.width * sin(a)) + abs(bmp.height * cos(a)));
dw1 := dw-1;
dh1 := dh-1;
dst:= TBitmap.Create;
dst.Width := dw;
dst.height := dw;
dst.PixelFormat:= pf24bit;
dst.Canvas.Brush.Color := fillColor;
dst.Canvas.FillRect(Rect (0,0,dw ,dh) );
tsin := sin(a);
tcos := cos(a);
cxSrc := (bmp.Width - 1) / 2;
cySrc := (bmp.Height - 1) / 2;
cxDest := (dst.Width - 1) / 2;
cyDest := (dst.Height - 1) / 2;
getmem(arx1, sizeof(integer) * dst.Width);
getmem(arx2, sizeof(integer) * dst.Width);
for x := 0 to dst.Width - 1 do
begin
arx1[x] := round( cxSrc + (x - cxDest) * tcos );
arx2[x] := round( cySrc + (x - cxDest) * tsin );
end;
getmem(srcrows, bmp.height*sizeof(pointer));
for y := 0 to bmp.height-1 do
srcrows[y] := bmp.ScanLine[y];
for y := 0 to dh1 do
begin
px := dst.Scanline[y];
ary1 := round( (y - cyDest) * tsin );
ary2 := round( (y - cyDest) * tcos );
parx1 := @arx1[0];
parx2 := @arx2[0];
prgb_d := prgb(px);
for x := 0 to dw1 do
begin
fx := parx1^ - ary1;
if (fx >= 0) and (fx < bmp.width )then
begin
fy := parx2^ + ary2;
if (fy >= 0) and (fy < bmp.height) then
begin
prgb_s := srcrows[fy];
inc(prgb_s, fx);
prgb_d^ := prgb_s^;
end;
end;
inc(prgb_d);
inc(parx1);
inc(parx2);
end;
end;
freemem(srcrows);
freemem(arx1);
freemem(arx2);
bmp.Assign( dst );
FreeAndNil(dst);
end;
end.