全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:1889
推到 Plurk!
推到 Facebook!

在Canvas畫上隨TIMER變動之動畫出現不連續的問題

尚未結案
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#1 引用回覆 回覆 發表時間:2004-10-23 00:17:16 IP:140.116.xxx.xxx 未訂閱
請問各位高手 我想在Image的Canvas上作隨Timer變動之動畫 就是一條條垂直於螢幕的黑白相間線條, 隨著Timer的變化慢慢向右移 形成週而復始的動畫    我是在Canvas上用LineTo畫出這些線 並一次移動一個pixel 但僅管我把timer的interval調到最快(1), 動畫移動的速度還是很慢    如果把移動間格調成大於一個pixel, 又會出現動畫不連續(線的邊緣出現一條條的裂橫) 請問有方法可以解決嗎? 還是需利用其它方式來撰寫呢? PS:DoubleBuffered也開了... 請各位不吝指教!    
    //---------------------------------------------------------------------------    #include 
#pragma hdrstop
#include "Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int Line_Width,Frequency,ScreenWidth,ScreenHeight,step,index;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{  
   switch (Form1->ComboBox1->ItemIndex)
    {
         case 0 :  Line_Width=100;break;
         case 1 :  Line_Width=200;break;
         case 2 :  Line_Width=300;break;
         case 3 :  Line_Width=400;break;
         case 4 :  Line_Width=500;break;
    }  //線寬       switch (Form1->ComboBox2->ItemIndex)
    {
         case 0 :  Frequency=1;break;
         case 1 :  Frequency=10;break;
         case 2 :  Frequency=20;break;
         case 3 :  Frequency=40;break;
         case 4 :  Frequency=50;break;
         case 5 :  Frequency=100;break;
    }  //TIMER速度調節        switch (Form1->ComboBox3->ItemIndex)
    {
         case 0 :  ScreenWidth=1024;ScreenHeight=768;break;
         case 1 :  ScreenWidth=800;ScreenHeight=600;break;
         case 2 :  ScreenWidth=640;ScreenHeight=560;break;
    }  //螢幕解析度選擇
  // ShowMessage(Frequency);      Form2->Height= ScreenHeight;
  Form2->Width = ScreenWidth;
  Form2->Image1->Height= ScreenHeight;
  Form2->Image1->Width = ScreenWidth;
  Form1->Image1->Height= ScreenHeight;
  Form1->Image1->Width = ScreenWidth;      Form1->Timer1->Interval=Frequency;
  //調整timer的速度
  step = -Line_Width;
  Form1->Timer1->Enabled=true;
  Form2->ShowModal();// Show();
}
//---------------------------------------------------------------------------    void __fastcall TForm1::Button2Click(TObject *Sender)
{
    Form1->Close();
    Form2->Close();    }
//---------------------------------------------------------------------------    void __fastcall TForm1::FormCreate(TObject *Sender)
{
     Form1->Timer1->Enabled = false;
     Form1->DoubleBuffered = true;    }
//---------------------------------------------------------------------------    void __fastcall TForm1::Timer1Timer(TObject *Sender)
{    //開始畫圖
     int temp = ScreenWidth+ScreenWidth*1/2;
     int step2;;
     Form1->Image1->Picture->Assign(NULL);
    //清空圖案
  //----------------------------------------------------
  step2 = step;
  while(step2Image1->Canvas->Pen->Width=Line_Width;
    Form1->Image1->Canvas->Pen->Color=clBlack;
    Form1->Image1->Canvas->MoveTo(step2,0);
    Form1->Image1->Canvas->LineTo(step2,ScreenHeight);
    step2 = step2+Line_Width*2;
    //將整個螢幕畫滿
  }
  step=step+1;
  //每次移動一格,移太多線的兩旁會出現裂痕...
  if(step>Line_Width)
  {
   step=-Line_Width;
  }      Form2->Image1->Picture=Form1->Image1->Picture;
  //將結果重繪製Form2->Image1上,秀出    }
//---------------------------------------------------------------------------    
發表人 - williams8807 於 2004/10/23 00:19:46
arisaka_matsuri
高階會員


發表:25
回覆:205
積分:231
註冊:2003-10-19

發送簡訊給我
#2 引用回覆 回覆 發表時間:2004-10-23 11:17:53 IP:140.113.xxx.xxx 未訂閱
dear williams8807: BCB的Timer元件精確度有限(約55ms),需要較短的時間間隔時,請使用Windows Multimedia Timer(詳見WinSDK說明),或是在下已經包裝成BCB類別,可以像Timer一樣的簡單使用http://delphi.ktop.com.tw/topic.php?TOPIC_ID=51703。 在用TMMTimer試驗的過程中,你原本附的程式碼可以正確執行,但是當Interval調至3ms以下時,會發生不明原因的記憶體違規存取,所以我又幫你修改了一些地方,可以正確使用在Interval = 1ms沒問題。 **** Unit1.h ****
//---------------------------------------------------------------------------
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include 
#include 
#include 
#include <Forms.hpp>
#include     #include "TMMTimerUnit.hpp"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:        // IDE-managed Components
        TButton *Button1;
        TTimer *Timer1;
        TImage *Image1;
        void __fastcall Button1Click(TObject *Sender);
        void __fastcall Timer1Timer(TObject *Sender);
        void __fastcall FormClose(TObject *Sender, TCloseAction &Action);
private:        // User declarations
public:                // User declarations
        __fastcall TForm1(TComponent* Owner);            TMMTimer *MMTimer;
        void __fastcall OnMMTimer(void); //Timer觸發時要執行的函式
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif
**** Unit1.cpp ****
//---------------------------------------------------------------------------
#include 
#pragma hdrstop    #include "Unit1.h"
#include "Unit2.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
int Line_Width,Frequency,ScreenWidth,ScreenHeight,step,index;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
     // Form1 建構時執行
     DoubleBuffered = true;
     MMTimer = new TMMTimer();
     MMTimer->Enabled = false;
     MMTimer->Resolution = 1; // 使用最佳解析度(跟時間間隔不一樣喔)
     MMTimer->OnTimer = OnMMTimer; // 設定Timer觸發時要執行的函式
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
  .
  .
  .      MMTimer->Interval = Frequency;
  //調整timer的速度
  step = -Line_Width;
  MMTimer->Enabled = true;
  Form2->ShowModal();// Show();
}
//---------------------------------------------------------------------------    /* 跟原本 void __fastcall TForm1::Timer1Timer(TObject *Sender) 處理一樣的事情*/    void __fastcall TForm1::OnMMTimer(void)
{
//開始畫圖
     int temp = ScreenWidth ScreenWidth*1/2;
     int step2;
     //Form1->Image1->Picture->Assign(NULL); // 會導致記憶體違規存取(不明原因)
     Form1->Image1->Canvas->Brush->Color = clWhite;
     Form1->Image1->Canvas->FillRect(TRect(0,0,Image1->Width,Image1->Height));
    //清空圖案
  //----------------------------------------------------
  step2 = step;
  while(step2Image1->Canvas->Pen->Width=Line_Width;
    Form1->Image1->Canvas->Pen->Color=clBlack;
    Form1->Image1->Canvas->MoveTo(step2,0);
    Form1->Image1->Canvas->LineTo(step2,ScreenHeight);
    step2 = step2 Line_Width*2;
    //將整個螢幕畫滿
  }
  step=step 1;
  //每次移動一格,移太多線的兩旁會出現裂痕...
  if(step>Line_Width)
  {
   step=-Line_Width;
  }      //下列並不會真正將資料畫到Form2->Image1上
  //Form2->Image1->Picture=Form1->Image1->Picture;
  
  // 下列在interval很小時(約3ms以下)會導致資源分配問題
  //Form2->Image1->Picture->Assign(Form1->Image1->Picture);
  
  
  // 將Form1->Image1的東東畫到Form2->Image1
  Form2->Image1->Canvas->Draw(0, 0, Form1->Image1->Picture->Bitmap);
  
  //將結果重繪製Form2->Image1上,秀出
}

//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
  delete MMTimer; //用完一定要歸還!
}
//---------------------------------------------------------------------------
如果要直接用TMMTimer,還別忘了將TMMTimerUnit.cpp加到專案中(Shift F11)~
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#3 引用回覆 回覆 發表時間:2004-10-24 20:00:45 IP:163.28.xxx.xxx 未訂閱
謝謝arisaka_matsuri您的回答 效果真的變好了! 可是我還是無法到達 src="http://delphi.ktop.com.tw/loadfile.php?TOPICID=18287351&CC=408989">
arisaka_matsuri
高階會員


發表:25
回覆:205
積分:231
註冊:2003-10-19

發送簡訊給我
#4 引用回覆 回覆 發表時間:2004-10-25 19:21:53 IP:140.113.xxx.xxx 未訂閱
dear williams8807: 恩~我猜,應該是程式結束時,沒有delete計時器吧!在FormClose時一定要刪除已經建立的多媒體計時器,不然就會得到那樣的錯誤。順便說明一下,程式碼藍色部分就是我試驗時新加上的,紅色則是刪除的部分。    BTW,多媒體計時器的威力強大,請小心使用。除非特殊情況,不然用到個位數ms時,系統是很吃緊的(而且你還要畫這有的沒的)。在PO這文章之前,為了找出你的問題,忘了關MP3就把計時器朝1ms邁進,結果邊聽邊當........連Windows工作管理員都差點叫不出來
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#5 引用回覆 回覆 發表時間:2004-10-25 20:12:04 IP:140.116.xxx.xxx 未訂閱
謝謝您的回應和幫忙, 還要麻煩您幫我測試程式,真是太感謝了! 可是有個小問題說... 我有記得 > 看來我得在畫動畫時多做點技巧, 可是我想不通的是,為啥我每一步跳 class="code"> void __fastcall TForm1::Timer1Timer(TObject *Sender) { //開始畫圖 int temp = ScreenWidth ScreenWidth*1/2; int step2;; Form1->Image1->Picture->Assign(NULL); //清空圖案 //---------------------------------------------------- step2 = step; while(step2Image1->Canvas->Pen->Width=Line_Width; Form1->Image1->Canvas->Pen->Color=clBlack; Form1->Image1->Canvas->MoveTo(step2,0); Form1->Image1->Canvas->LineTo(step2,ScreenHeight); step2 = step2 Line_Width*2; //將整個螢幕畫滿 } step=step 1;<-這裡 //每次移動一格,移太多線的兩旁會出現裂痕... if(step>Line_Width) { step=-Line_Width; } Form2->Image1->Picture=Form1->Image1->Picture; //將結果重繪製Form2->Image1上,秀出 } 發表人 - williams8807 於 2004/10/25 20:17:04
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#6 引用回覆 回覆 發表時間:2004-10-26 15:24:01 IP:163.28.xxx.xxx 未訂閱
嗚嗚,有大大有建議嗎? 剛老闆又來問了...
arisaka_matsuri
高階會員


發表:25
回覆:205
積分:231
註冊:2003-10-19

發送簡訊給我
#7 引用回覆 回覆 發表時間:2004-10-26 17:01:18 IP:140.113.xxx.xxx 未訂閱
dear williams8807: 關於多媒體計時器的問題,可能沒辦法給你很清楚的答案。尤其你在計時器觸發時以GDI方式繪圖(像是用Timage元件),本來就會慢一些。當計時器觸發間隔很短時,只要來不及把它畫完,我也不知道會發生什麼事~~所以呢,如果真的很在意動畫的表現,建議改用>< face="Verdana, Arial, Helvetica">引言:可是我想不通的是,為啥我每一步跳2個pixel以上時,就會出現雜影呢? 不然只要我跳得越多看起來也越快呀? 當每一步移動的距離越大,帶給視覺的不連續性也會增加。當超過每個限度時,你會覺得畫面是在「閃爍」而看不出來畫面移動。這個「感覺」可以用取樣的觀念來解釋。
brook
資深會員


發表:57
回覆:323
積分:371
註冊:2002-07-12

發送簡訊給我
#8 引用回覆 回覆 發表時間:2004-10-26 18:33:37 IP:218.167.xxx.xxx 未訂閱
我覺得可能要考慮不要使用Timer,直接在Button1Click裡用迴路跑就好了,頂多在迴路裡加個 Application->ProcessMessages();還可以處理其他事情,可能會比較好一點. 致於假如跑得太快,迴路裡加個Sleep(1);或Sleep(?);看看會不會好一點. 但假如跑得太慢,再說吧!
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#9 引用回覆 回覆 發表時間:2004-10-27 14:32:04 IP:140.116.xxx.xxx 未訂閱
謝謝arisaka_matsuri跟brook大大的回答 我都TRY過了 使用while迴圈取代timer,可以直接到系統的最高速 相容性較好,不會與其他程式相衝或是記憶體衝突 但缺點是, 當系統資源不足時, 他會跑得很慢並且無法作速度控制... 這裡先謝謝 > 至於
JerryKuo
版主


發表:42
回覆:571
積分:322
註冊:2003-03-10

發送簡訊給我
#10 引用回覆 回覆 發表時間:2004-10-27 18:14:12 IP:220.135.xxx.xxx 未訂閱
williams8807你好:    我建議不要直接畫在TImage物件上,因為會很慢。或改用buffer,直接畫在buffer 上,再將buffer貼在TImage物件上,以下是將Timer事件裡的原始碼所做的更改。 跑起來很順。 試試看。
int Line_Width,Frequency,ScreenWidth,ScreenHeight,step,index;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------    void __fastcall TForm1::Button1Click(TObject *Sender)
{
   switch (Form1->ComboBox1->ItemIndex)
    {
         case 0 :  Line_Width=100;break;
         case 1 :  Line_Width=200;break;
         case 2 :  Line_Width=300;break;
         case 3 :  Line_Width=400;break;
         case 4 :  Line_Width=500;break;
    }  //線寬       switch (Form1->ComboBox2->ItemIndex)
    {
         case 0 :  Frequency=1;break;
         case 1 :  Frequency=10;break;
         case 2 :  Frequency=20;break;
         case 3 :  Frequency=40;break;
         case 4 :  Frequency=50;break;
         case 5 :  Frequency=100;break;
    }  //TIMER速度調節        switch (Form1->ComboBox3->ItemIndex)
    {
         case 0 :  ScreenWidth=1024;ScreenHeight=768;break;
         case 1 :  ScreenWidth=800;ScreenHeight=600;break;
         case 2 :  ScreenWidth=640;ScreenHeight=560;break;
    }  //螢幕解析度選擇
  // ShowMessage(Frequency);      Form2->Height= ScreenHeight;
  Form2->Width = ScreenWidth;
  Form2->Image1->Height= ScreenHeight;
  Form2->Image1->Width = ScreenWidth;
  Form1->Image1->Height= ScreenHeight;
  Form1->Image1->Width = ScreenWidth;      Form1->Timer1->Interval=Frequency;
  //調整timer的速度
  step = -Line_Width;
  Form1->Timer1->Enabled=true;
//  Form2->ShowModal();// Show();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    Form1->Close();
    Form2->Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
     Form1->Timer1->Enabled = false;
     Form1->DoubleBuffered = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  Graphics::TBitmap *Bmp = new Graphics::TBitmap();
  Bmp->Width = ScreenWidth;
  Bmp->Height= ScreenWidth;
 //開始畫圖
     int temp = ScreenWidth ScreenWidth*1/2;
     int step2;;
     Form1->Image1->Picture->Assign(NULL);
    //清空圖案
  //----------------------------------------------------
  step2 = step;
  while(step2Canvas->Pen->Width=Line_Width;
    Bmp->Canvas->Pen->Color=clBlack;
    Bmp->Canvas->MoveTo(step2,0);
    Bmp->Canvas->LineTo(step2,ScreenHeight);
    step2 = step2 Line_Width*2;
    //將整個螢幕畫滿
  }
  step=step 1;
  //每次移動一格,移太多線的兩旁會出現裂痕...
  if(step>Line_Width)
  {
   step=-Line_Width;
  }      Form2->Image1->Picture->Assign(Bmp);
  Form1->Image1->Picture->Assign(Bmp);
  //將結果重繪製Form2->Image1上,秀出
  delete Bmp;
}
發表人 - jerrykuo 於 2004/10/27 18:17:01
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#11 引用回覆 回覆 發表時間:2004-10-28 13:27:33 IP:163.28.xxx.xxx 未訂閱
謝謝JerryKuo的回應 您的方法我之前就有試過 不過有些人是跟我說, 這跟直接畫在image1在回畫到顯示canvas上是一樣的... 不過這樣的方法應該比較複雜(還要宣告有的沒的) 所以還是照您的方法改了一次 結果速度相差不大 還是謝謝大大的建議,謝謝啦... 我想了很久,我想大概只有用其它的方法吧... 如
brook
資深會員


發表:57
回覆:323
積分:371
註冊:2002-07-12

發送簡訊給我
#12 引用回覆 回覆 發表時間:2004-10-28 13:50:02 IP:218.160.xxx.xxx 未訂閱
引言: 但缺點是, 當系統資源不足時, 他會跑得很慢並且無法作速度控制... < face="Verdana, Arial, Helvetica"> 1.假如Sleep設零時還是會跑得很慢時,那真的繪圖要用其他寫法. 或者不是一次一格而是?格,或者己經經過多少時間要跑到第幾個位置. 2.Sleep()你可改另一種寫法 如: DWORD t = GetTickCount(); //繪圖 while (GetTickCount()
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#13 引用回覆 回覆 發表時間:2004-10-28 14:59:01 IP:163.28.xxx.xxx 未訂閱
引言: 1.假如Sleep設零時還是會跑得很慢時,那真的繪圖要用其他寫法. 或者不是一次一格而是?格,或者己經經過多少時間要跑到第幾個位置. 2.Sleep()你可改另一種寫法 如: DWORD t = GetTickCount(); //繪圖 while (GetTickCount() 1.如果是一次畫一格的話,Sleep(0)還是太慢.... 2.但如果換成一次5到10格的話,就OK啦 可是線的兩旁會晃動,嗚嗚... 3.可以請大大解釋一下您說的 DWORD t = GetTickCount();或是 while (GetTickCount()
brook
資深會員


發表:57
回覆:323
積分:371
註冊:2002-07-12

發送簡訊給我
#14 引用回覆 回覆 發表時間:2004-10-28 18:56:15 IP:218.160.xxx.xxx 未訂閱
引言: 1.如果是一次畫一格的話,Sleep(0)還是太慢....
需考慮繪圖用其他方式,因為你的程式好像是每次繪都是整張重繪, 假如只劃長方形,直接用Canvas->Rectangle()的方式看看.
引言: 3.可以請大大解釋一下您說的 DWORD t = GetTickCount();或是 while (GetTickCount() 假如你繪圖需3秒,再加上sleep5秒,則你跑一圈則需8秒. 但如果你想在5秒內跑一圈,你就可用這個方式
  DWORD t = GetTickCount(); //取得目前時間
//執行繪圖需3秒
  while (GetTickCount()ProcessMessages();  //最好加上這行,還可以做其他事.
  }
因你的程式,沒有sleep還是跟不上,所以從加快你的繪圖速度著手.或者退而求其次解決你一次5,10格的話,線的兩旁會晃動的問題.
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#15 引用回覆 回覆 發表時間:2004-10-29 19:45:30 IP:140.116.xxx.xxx 未訂閱
引言:
引言: 需考慮繪圖用其他方式,因為你的程式好像是每次繪都是整張重繪, 假如只劃長方形,直接用Canvas->Rectangle()的方式看看.
我有改用Rectangle()的方式做過,好像跟lineto的方式相差不大, 尤其是Rectangle()也沒辦法一次畫完一整頁說...
引言: 假如你繪圖需3秒,再加上sleep5秒,則你跑一圈則需8秒. 但如果你想在5秒內跑一圈,你就可用這個方式 DWORD t = GetTickCount(); //取得目前時間 while (GetTickCount()ProcessMessages(); //最好加上這行,還可以做其他事. }
嗯,謝謝您了, 這樣的確可以設定時間了,但當我設太小還是會有問題
引言: 因你的程式,沒有sleep還是跟不上,所以從加快你的繪圖速度著手.或者退而求其次解決你一次5,10格的話,線的兩旁會晃動的問題.
對呀,這正是我最頭痛的問題 如果可以就太好了.... 還是謝謝您的回應,謝謝...
brook
資深會員


發表:57
回覆:323
積分:371
註冊:2002-07-12

發送簡訊給我
#16 引用回覆 回覆 發表時間:2004-10-29 20:57:12 IP:218.160.xxx.xxx 未訂閱
假如你的圖只是黑白的方塊而己,那還可解決.長方形黑色區塊假如要往右移一格,則你可不需要重繪,只要長方形右邊加條黑線,左邊加條白線即可,你就可感覺方塊向右移,如此或許會快一點.假如圖形很花俏,那就比較傷腦筋了.
williams8807
一般會員


發表:40
回覆:37
積分:15
註冊:2003-11-22

發送簡訊給我
#17 引用回覆 回覆 發表時間:2004-11-01 20:45:09 IP:163.28.xxx.xxx 未訂閱
謝謝各位高手的解惑!
系統時間:2024-07-03 19:32:34
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!