5

我有一个带有漫画书布局的 .bmp 图像。目前我的代码是这样工作的。如果我右键单击并按住鼠标按钮,我可以在漫画书页面上的一个框架周围绘制一个选取框类型框。当我释放按钮时,它将放大该帧。但它的瞬间。我希望它具有动画效果。

因此,不要将 PicRect 的值设置为“END VALUE”

PicRect.Left
PicRect.right
PicRect.top
PicRect.bottom

如下面的代码所示,我需要一种方法来慢慢到达那里,某种 while 循环可以一次设置一些值,直到它达到“最终值”但我不能 100% 确定这个数学是如何计算的正在工作中。我的任何 while 循环尝试都不会做任何事情,只是放大得太远。这是程序。

procedure TZImage.MouseUp(Button: TMouseButton; Shift: TShiftState;
                      X, Y: Integer);
    var coef:Double;
    t:integer;
begin
   if FMouse=mNone then Exit;
   if x>ShowRect.Right then x:=ShowRect.Right;
   if y>ShowRect.Bottom then y:=ShowRect.Bottom;
   if FMouse=mZoom then begin  //calculate new PicRect
     t:=startx;
     startx:=Min(startx,x);
     x:=Max(t,x);
     t:=starty;
     starty:=Min(starty,y);
     y:=Max(t,y);
     FMouse:=mNone;
     MouseCapture:=False;
//enable the following if you want to zoom-out by dragging in the opposite direction}
    {     if Startx>x then begin
            DblClick;
            Exit;
         end;}
         if Abs(x-startx)<5 then Exit;
         if (x - startx < y - starty) then
         begin
           while (x - startx < y - starty) do
           begin
              x := x + 100;
              startx := startx - 100;
           end;
         end
         else if (x - startx > y - starty) then
         begin
            while (x - startx > y - starty) do
            begin
                y := y + 100;
                starty := starty - 100;
            end;
         end;


    //This is were it sets the zoom info. This is were
    //I have to change to slowly get the PICRECT.Left/right/top/bottom
         if (PicRect.Right=PicRect.Left)
         then
            coef := 100000
         else
            coef:=ShowRect.Right/(PicRect.Right-PicRect.Left);
         PicRect.Left:=Round(PicRect.Left+startx/coef);
         PicRect.Right:=PicRect.Left+Round((x-startx)/coef);
         if (PicRect.Bottom=PicRect.Top)
         then
            coef := 100000
         else
            coef:=ShowRect.Bottom/(PicRect.Bottom-PicRect.Top);
         PicRect.Top:=Round(PicRect.Top+starty/coef);
         PicRect.Bottom:=PicRect.Top+Round((y-starty)/coef);
       end;
       if FMouse=mDrag then begin
         FMouse:=mNone;
         Canvas.Pen.Mode:=pmCopy;
         Screen.Cursor:=crDefault;
       end;
       Invalidate;
    end;

我相信这可以在上面的代码中完成。但也想添加这个以防万一它有帮助。

type
    TZImage = class(TGraphicControl)
  private
    FBitmap        : TBitmap;
    PicRect        : TRect;
    ShowRect       : TRect;
    FShowBorder    : boolean;
    FBorderWidth   : integer;
    FForceRepaint  : boolean;
    FMouse         : (mNone, mDrag, mZoom);
    FProportional  : boolean;
    FDblClkEnable  : boolean;
    startx, starty,
    oldx, oldy     : integer;

感谢您为使其正常工作提供的任何帮助。

4

4 回答 4

6

我有一些建议;我不确定它们是否足以解决您的问题,但我希望它可以帮助您实现目标。

首先,你的while循环做了很多有趣的摆弄:

if (x - startx < y - starty) then
     begin
       while (x - startx < y - starty) do
       begin
          x := x + 100;
          startx := startx - 100;
       end;
     end
else if (x - startx > y - starty) then
     /* similar code */

请注意,x - start == y - starty案例被完全忽略了。我不知道这是否重要。

其次,这可能会在没有循环的情况下重写。我在这里猜测,需要进行一些测试才能确定这是否正确,但这感觉像是正确的道路:

foo := (x - startx) - (y - starty)
if (foo > 200 || foo < -200)
    bar = foo / 200  # I assume integer truncation
    x += bar * 100
    startx += bar * 100

我不完全确定您为什么要尝试(x-startx) - (y-starty)彼此相距 200 以内;可能还有更好的东西。

这部分代码有点混乱:

if (PicRect.Right=PicRect.Left)
     then
        coef := 100000
     else
        coef:=ShowRect.Right/(PicRect.Right-PicRect.Left);
     PicRect.Left:=Round(PicRect.Left+startx/coef);
     PicRect.Right:=PicRect.Left+Round((x-startx)/coef);
     if (PicRect.Bottom=PicRect.Top)
     then
        coef := 100000
     else
        coef:=ShowRect.Bottom/(PicRect.Bottom-PicRect.Top);
     PicRect.Top:=Round(PicRect.Top+starty/coef);
     PicRect.Bottom:=PicRect.Top+Round((y-starty)/coef);
   end;

coef应该从前面的内容覆盖吗?或者,您是否应该计算 a coefxcoefy然后选择(更大?更小?接近 100000?)值来为.Left.Right.Top.Bottom计算服务?我不得不认为,就目前而言,这段代码更有可能导致内容的尴尬拉伸,从而可能会惹恼用户和作者。

现在,要解决你在这里的真正原因,动画缩放 - 你可能需要彻底改变一些东西。我觉得你的while循环可能是为了进行缩放,但它们是计算之后出现的coef,所以我认为它们是为了其他目的。但是,一旦您确定了将循环精确放置在何处以计算coef从“无缩放”到“最终缩放”范围内的不同值,您还需要添加调用以重新绘制显示 - 或者,取决于您的环境中,可能需要添加一些由计时器每 50 毫秒触发一次的回调代码,或者使用更新的coef值重新绘制。

于 2012-06-09T22:31:38.400 回答
6

不要使用 while 循环来更新缩放级别,因为有两个问题:

  1. 不同的动画时长,因为缩放操作的速度取决于代码的速度、当前的CPU使用率、CPU型号等...
  2. 阻塞 te GUI,因为即使您使用延迟(例如使用Sleep),代码也在主线程中运行并且程序结果是无响应的。

就像sarnoldElling已经说过的:使用计时设备(例如 a TTimer)在每个间隔上执行总缩放操作的一部分。现在,有两种方法可以计算这些部分

  1. 将要桥接的总距离划分为固定数量的小距离,将计时器的间隔设置为总持续时间除以该数字,并处理每个间隔上所有已处理距离的总和。这种方法的缺点是双重的:
    • 计时器的设置间隔是一个近似值,由于各种原因并不准确,其中一个原因是依赖于 Windows 消息传递系统,
    • 因此可能会出现粗糙或不流畅的动画。
  2. 在每个间隔重新计算要桥接的部分距离。这样,无论下一个间隔是否需要两倍以上,动画都会始终显得流畅。

我在您的相关问题的这个答案中使用了第二个解决方案,从中提取了以下相关片段:

procedure TZImage.Animate(Sender: TObject); 
var 
  Done: Single; 
begin 
  Done := (GetTickCount - FAnimStartTick) / FAnimDuration; 
  if Done >= 1.0 then 
  begin 
    FAnimTimer.Enabled := False; 
    FAnimRect := FCropRect; 
  end 
  else 
    with FPrevCropRect do 
      FAnimRect := Rect( 
        Left + Round(Done * (FCropRect.Left - Left)), 
        Top + Round(Done * (FCropRect.Top - Top)), 
        Right + Round(Done * (FCropRect.Right - Right)), 
        Bottom + Round(Done * (FCropRect.Bottom - Bottom))); 
  Invalidate; 
end; 

procedure TZImage.Zoom(const ACropRect: TRect); 
begin 
  FPrevCropRect := FCropRect; 
  FAnimRect := FPrevCropRect; 
  FCropRect := ACropRect; 
  FAnimStartTick := GetTickCount; 
  FAnimTimer.Enabled := True; 
end; 

解释:

  • FCropRect是新的缩放矩形,FPrevCropRect是前一个,
  • FAnimRect是两者之间的矩形,取决于动画的进度,
  • FAnimStartTick是通过调用 开始缩放操作的时间Zoom
  • 在每个计时器间隔(设置为 15 毫秒,~67Hz 刷新率),Animate被调用,
  • Done是动画进度的百分比,
  • Invalidate触发将图形绘制到FAnimRect.
于 2012-06-16T17:58:50.593 回答
4

我写了一个回答你之前的问题(被一些可能是好意的stackoverflow版主永久删除)。

我之前的回答中的建议是您查看 glflow 代码示例:

http://code.google.com/p/glflow/

该示例使用 GLScene 库,并说明了图像加载、图像缩放和动画。

即使你不使用 GLScene 库,我想你也可以通过查看代码示例对动画部分有所启发。

它的本质是您需要使用计时器来进行重绘。

首先将开始缩放级别和结束缩放级别之间的距离分成离散的步骤。然后使用计时器循环执行这些步骤并重绘每一步。

于 2012-06-10T10:01:55.827 回答
0

我实际上正在与 Glen 合作开展这个项目。我写了一些有问题的代码,所以我想澄清一下它的作用。我留下了评论,因为我很快就把它放在一起了。这里的大部分代码都是通过我们找到的开放许可证使用的。代码最初没有循环:

if (x - startx < y - starty) then
       begin
         while (x - startx < y - starty) do
         begin
            x := x + 100;
            startx := startx - 100;
         end;
       end
       else if (x - startx > y - starty) then
       begin
          while (x - startx > y - starty) do
          begin
              y := y + 100;
              starty := starty - 100;
          end;
       end;

这就是我添加的内容,之所以添加它是因为原始代码没有按照我们认为的方式运行。基本上,您可以通过拖放选择区域。选定区域被放大,但不是显示选定的整个区域,而是适合查看区域的 x-startx 或 y-starty 中的较小者。因此,为了澄清这一点,如果您选择了一个 50pix 高和 100pix 宽的区域,它将缩放并从上到下填充 50pix 填充视图区域,但 100pix 会被剪裁。侧面从观看区域脱落。因此,此处添加的代码是通过使两者中较小的部分成为两者中的较大部分来解决问题。通过这样做,它实际上将适合原来的两者中较大的视图,现在是两者中的较小者。这是一个令人难以置信的草率修复,但它确实有效。您的方法也可以为我们解决这个问题,并且可能会以更好的方式解决。这一切的最大问题是,如果该区域是一个非常大的区域,那么 200pix 实际上可能不足以解决差异。出于我们的目的,它可能适用于 85% 以上,但不是全部,所以这段代码仍然需要工作。

您质疑的另一个代码实际上是之前让我们发疯的原因。整个文档完全没有评论,我们仍在努力拼凑出这一切的确切含义。coef实际上是让我发疯的原因。我什至不完全确定它首先意味着什么。我确实尝试了一个单独的 coefx 和 coefy ,这实际上打破了它。缩放框与想象的大不相同。据我所知,目前的方法没有增加奇怪的拉伸,至于我不确定的原因。

这是我们正在使用的代码的链接,但如果您想全面了解它。http://www.torry.net/authorsmore.php?id=986 Zimage 在那个页面上。

至于手头的实际问题,我们并不清楚 coef 究竟做了什么,因此对其进行更改会导致我们只是破坏事物而不是以试错的方式工作。如果您不介意看一下它,以便我们弄清楚它到底做了什么,这将使我们能够将其更改为正确的值,并在此过程中摆脱我的 slop 代码。这将允许我们前进到缩放动画。

添加关于动画的另一个问题。在这样做时,这是否也允许我们在从图像上的一个缩放点移动到另一个缩放点时添加动画。对于我们的应用程序,它将从一个漫画面板到另一个漫画面板,在下方或侧面,并且在大多数情况下也是不同的尺寸。在 left、right、top 和 bottom 的值之间加载是显示该类型动画的最佳方式吗?如果是这样,我认为这也适用于从完整图像移动到第一个缩放面板。

于 2012-06-10T05:07:35.967 回答