2012-06-09 36 views
5

我有一本漫畫書佈局的.BMP圖像。目前我的代碼是這樣工作的。如果我右鍵單擊並按住鼠標按鈕,我可以在漫畫書頁面的其中一個框架周圍繪製一個選框類型框。當我釋放按鈕時,它將放大到該框架。但它的瞬間。我希望它具有動畫效果。數學慢的圖像變焦

而不是去和PicRect的值設置爲「終值」

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; 

感謝您的任何幫助,使其工作。

回答

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應該是從早期的覆蓋?或者,您應該計算一個coefxcoefy,然後選擇(更大?更小?更接近100000?)值來同時用於.Left,.Right,.Top.Bottom計算?我認爲這個代碼,因爲它的立場,更可能導致的方式,可能會惹惱用戶和作者都尷尬伸展你的內容。

現在,爲解決真正原因,你爲什麼在這裏,動畫變焦 - 你可能需要徹底改變東西。我覺得你的while循環很可能是打算做變焦,但他們之後的coef計算來,所以我認爲他們的意思別的東西。但是,一旦確定了放置迴路的位置,以便在「無縮放」到「最終縮放」範圍內計算不同的coef值,您還需要添加對重繪顯示屏的調用 - 或者,取決於你的環境,也許需要添加一些回調代碼,每50毫秒或者用某個定時器觸發一次,以便用更新的coef值重新繪製。

+0

問題是它會拉伸,增加的循環現在停止拉伸。如果註釋掉了x - start == y - starty,它們的作用相同。我會試着用while循環看看它是如何工作的,謝謝你。 Iam不知道COEF甚至想要做什麼:(我計算了一個while循環會取代你稱之爲有點混淆的代碼段......因爲它上面的while循環是去除了伸展。 –

+0

將接受,因爲這有助於我獲得每一個工作的效果! –

+0

What'd be _awesome_是,如果你用_fixed_代碼添加一個答案 - 別人試圖做一些圖像縮放動畫可能非常想知道什麼特別需要改變 – sarnold

0

我實際上正在與格倫合作這個項目。我寫了一些有問題的代碼,所以我想澄清它的功能。我留下了評論,因爲我很快把它扔在一起。這裏的大部分代碼都是通過我們發現的開放許可證使用的。該代碼本來不具備循環:

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頂部到底部,但是100px會被剪切掉。雙方從觀看區域掉下來。因此,這裏添加的代碼是通過使兩者中的較大者更小來解決問題。通過這樣做,它實際上將適合於兩個中較小的兩個中原來較大的那個的視圖。這是一個令人難以置信的修復,但它的工作原理。你的方法也會爲我們解決這個問題,並且可能會以更好的方式進行。與此相關的一個大問題是,如果該區域選擇的面積較大,那麼200pix可能實際上並不足以解決這個問題。就我們的目的而言,它可能適用於85%以上,但不是全部,所以這段代碼仍然需要工作。

你問的其他代碼實際上是什麼讓我們堅果之前。在整個文件中都缺乏評論,我們仍然試圖將所有的意思拼湊在一起。係數實際上是讓我發瘋的原因。我甚至不完全確定首先要做什麼。我確實嘗試了一個單獨的coefx和coefy,並且實際上打破了它。縮放框與假定的大不相同。據我所知,目前的方法不會增加奇怪的延伸,至於我爲什麼不確定。

下面是我們正在使用的代碼的鏈接,但如果您想全面查看它的話。 http://www.torry.net/authorsmore.php?id=986 Zimage在該頁面上。

至於手頭的實際問題,我們並不清楚coef究竟在做什麼,因此對其進行更改會導致我們違規,而不是以試錯方式工作。如果你不介意看看它,讓我們弄清楚它會做什麼讓我們能夠將它改變爲正確的值,並在這個過程中擺脫我的slop代碼。這將允許我們進入縮放動畫。

添加另一個關於動畫的問題。這樣做,這也可以讓我們在從圖像上的一個縮放點移動到另一個縮放點時添加動畫。對於我們的應用程序,它將從一個漫畫面板到另一個漫畫面板,在下面或側面,在大多數情況下也是不同的大小。加載左邊,右邊,頂部和底部的中間值是展示該類型動畫的最佳方式嗎?如果是這樣,我認爲這也適用於從完整圖像移動到第一個縮放面板。

3

我寫了一個答案給你以前的問題(它被永久刪除了一些,可能是很好的意思,stackoverflow版主)。

在我以前的答案的建議是,你看看glflow代碼示例:

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

示例使用的GLScene庫,並說明這兩個圖像加載,圖像縮放和動畫。

即使您不使用GLScene庫,我認爲您可以通過查看代碼示例獲得動畫部分的一些靈感。

它的實質是你需要使用計時器來重繪。

首先將開始縮放級別和結束縮放級別之間的距離分成不連續的步驟。然後使用計時器循環執行這些步驟,並在每個步驟中重新繪製。

+0

+1提到定時器解決方案但是,我不同意將距離分成定時器間隔的數量是固定的,因爲這些間隔將不準確。 – NGLN

+0

@NGLN是的,我同意。事先劃分距離是一種粗糙的方法,在某些情況下會導致不準確。我想總體上更好的方法是使用確切的經過時間來計算中間幀的位置。 – Elling

5

不要使用while循環更新縮放級別,因爲兩個問題:

  1. 不同的動畫持續時間,因爲變焦操作的速度取決於代碼的速度,目前的CPU使用率,CPU型號等等...
  2. 由於即使使用延遲(例如Sleep),也會阻塞te GUI,因此代碼在主線程中運行,程序變爲無響應。

喜歡sarnoldElling已經說過:使用一個定時裝置(例如TTimer)以在每個間隔執行片的總變焦操作的。現在,有兩種方法來計算這些

  1. 鴻溝的總距離縮小到一個固定數量小的距離,定時器的時間間隔設置爲總的持續時間由數除以和處理的總和所有間隔處理的距離。這種方法的缺點是雙重的:
    • 定時器的設定的時間間隔是近似的,並不會是確切,因爲種種原因,他們是dependend在Windows郵件系統之一,
    • 一個可能粗糙或不平滑的動畫因爲它。
  2. 重新計算在每個間隔橋樑的距離的一部分。這樣動畫將永遠顯得平滑,無論下一個間隔是否需要兩次。

我以前在this answer第二解決您related question,從下面的相關片段採取:

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重繪。
+3

這是一個_superb_答案。優秀的細節,每次時鐘重新計算動畫的好建議,以及非常好的示例代碼。 (_Elegant_示例代碼。) – sarnold

+1

@sarnold哇,賞金!這非常非常慷慨,謝謝你提前。 – NGLN

+2

我偶爾偶爾會遇到一些優雅的東西,只需要一些特別的認可。 :) – sarnold