說明:以前本人參與個一個電子雜志項目,當時要求實現模擬現實生活中的雜志翻頁動畫效果,別人推薦了這篇文章,最後達到了我想要的效果,今天嘗試把這篇文章翻譯了一下。希望對英語水平不太好的同行有幫助。如果你的英語水平足夠好,我推薦你閱讀英文原文,網址是:http://www.codeproject.com/KB/GDI-plus/TurnThePage.aspx,同時希望大家原諒本人的翻譯水平。
介紹
圖1 翻頁效果
這篇文章用來介紹如何在電子書或者電子相冊中並列顯示圖片時創建一種翻頁效果,這種翻頁效果模仿了現實中的書本翻頁情況。
背景
寫作這篇文章的靈感來自於一個可重用的用於顯示圖片的組件的需求。我使用了C#和GDI+來提高我對.net下圖形圖象編程的理解。
創建這種效果的技術背景來自於一篇文章《FalshMX中的翻頁效果》,網址是:http://www.oreillynet.com/pub/a/javascript/2004/09/03/flashhacks.html,作者是Sham Bhangal。Sham在文章裡如何在動畫效果中使用對稱線來控制頁面的可見部分。盡管在Flash MX中和在GDI+中的處理有些不同,但是利用對稱線來計算頁面的可見部分這個概念是相同的。
在這篇文章中,我們假定第3頁和第4頁是當前頁,並且下一個動作是要翻到第5頁和第6頁。換句話說,我們要展現的是第4頁的翻頁效果。
動畫技巧
下圖闡述了在整個動畫中的關鍵區域和變化區域。
圖2 關鍵區域和參數
整個翻頁動畫可以概括為:
1、完全繪制出第3頁和第4頁。在繪制完成之後,B和C部分將會遮擋住第4頁的原來可見部分。
2、計算出對稱線和B和C的裁剪區域。
3、繪制B部分。這是下面的一頁的部分區域(第6頁)。
4、根據熱點進行相應轉換,並且進行相應旋轉。
5、繪制C部分。這是第4頁在翻頁過程中顯示的它的背面(第5頁)的內容。
當前頁
《FalshMX中的翻頁效果》一文中指出當前頁並不參與翻頁效果。這就意味著在翻頁中的每一面(第3頁和第4頁)都會首先被不進行任何裁減和轉換就繪制出來。B和C這兩個可見部分將會在當前頁之上進行繪制。
熱點
“熱點”這個詞是我從《FalshMX中的翻頁效果》一文中借鑒過來的。熱點一詞代表在翻頁時折痕在水平軸線上的位置,它標明對稱線的起始位置。在翻頁過程中,熱點一直在從頁邊緣到書中間這段距離之間移動。從頁邊到熱點的這段距離(x)將隨著頁面的翻動而變化。當熱點到達書的中間位置時,動畫就停止了,這時x=PAGE_WIDTH。
對稱線
對稱線代表翻頁時頁面的折痕。它是用於計算在翻頁時參與的頁面可見部分。在程序中,對稱線控制兩件事情:
1、B和C部分的裁剪區域。
2、C部分進行轉換的坐標原點。
對稱線可以用下面兩個等式來描述:
a = 45 + ( (45 *x) / PAGE_WIDTH )
h = x Tan ( a )
注意角度a會隨著x的變化而變化。這個等式表明當x=0時a = 45度並且當x= PAGE_WIDTH時a = 90度。
當動畫開始時,x=0,因此B和C部分並不可見。隨著x的增加,對稱線就形成了直角三角形的斜邊。三角形由三個長度確定:對稱線、x和高度h。圖2展示了這樣一種情況。
隨著x的增加,將會出現h>= PAGE_WIDTH的情況。當這種情況出現後,對稱線與頁面相交的區域將會由三角形變成梯形。梯形的高度就是PAGE_WIDTH。圖1就是這種情況。
不管是梯形還是三角形,這個閉合路線都指出了B和C這兩個裁減區域。要想看到運行中看到這種帶有邊框的效果,在提供的源代碼中將INCLUDE_DRAW_GRAPHICS_PATH這個參數設置為true就行了。程序將會在閉合路徑的外面繪制出一個金色的輪廓。
A部分區域
這部分是要翻動的頁面在裁減出B和C部分之後的可見部分(第4頁)。
B部分區域
B部分來自於正在翻動的頁面的之下的另一頁面,在這個例子中指的是第6頁的可見部分。B部分區域就是被對稱線與頁面形成的閉合曲線所裁減的部分。在這個部分之上的頁面(第4頁)的相應部分將會被直接裁減掉。顯而易見,隨著x的增加(a也隨著增加),這個可見部分將會越來越大。
要想看A和B部分區域的關系,請將INCLUDE_UNDERSIDE_PAGE_IN_ANIMATION變量設置為false。這將把C部分從動畫效果中去掉。
C部分區域
C部分區域的圖象表示了正在翻動的頁面的背面的圖象。在這個例子中,C部分區域代表了第5頁的可見部分。C部分也是通過對稱線指定的,但是是在頁面的這一邊(對稱線的左邊)。舉個例子,當翻動第4頁時,B部分將會是第6頁的右下部分,而C部分將會是第5頁的左下部分。圖3展示了B和C之間的關系。
圖3 B和C之間的關系
在繪制C部分區域時,我們用了一個後台的圖象來輔助,就是pageUndersideImage。這個新圖象的裁減區域通過對稱線來確定。第5頁的圖象被繪制在這個新的緩沖區裡面,這個新建的圖象將會靠近B部分區域繪制。
連接B和C部分區域
當C部分的後台圖像已經准備好,它通過下面的步驟繪制:
首先調整系統坐標到熱點。
將系統坐標旋轉180-2a度,請看圖4了解它們的關系。
將含有C部分區域的圖像繪制到相應坐標(-x,-PAGE_HEIGHT)。圖5標明了這種旋轉。
圖4 調整角度以匹配對稱線
圖5 旋轉坐標系
我發現沿著對稱線排列B和C部分區域非常具有挑戰性因為當前頁面的顏色容易出“血邊”(因為第4頁的一些紅色像素)。當我繪制B或者C部分區域時,我用了一個變通方法:指定圖形對象的pixeloffsetmode以pixeloffsetmode.half方式繪制。
g.PixelOffsetMode= PixelOffsetMode.Half;
我還發現,在繪制C部分區域之前,增加一個像素(或者減掉一個像素)來旋轉坐標也可以防止出“血邊”。
PathTranslationMatrix.Translate((float)hotSpot.Origin.X+1,
(float)hotSpot.Origin.Y);
要使用pixeloffsetmode變通方法,請將源代碼中的USE_PIXEL_MODE_OFFSET設置為true。
繪制動畫
每一幀動畫都是在timer1_Tick方法中繪制到緩沖區中的CurrentShownBitmap對象上。CurrentShownBitmap方法僅僅是將繪制到屏幕上。
主要功能說明
計算動畫中每一幀裡B和C部分的圖象都是由以下方法完成的:
private GraphicsPath GetPageUnderGraphicsPath(int x,
refdouble a, int height, int width,
bool isUnderSide, TurnType type)
參數x代表從頁面邊緣到熱點處的距離(前面已經說明),height和width參數代表當前頁面要顯示的高度和寬度。isUnderSide參數用來告訴程序是否在計算C部分區域(正在翻動的那一頁的背面),最後一個參數type是表示頁面是左翻頁還是右翻頁。基本上,isUnderSide和type參數是用來獲取圖形路徑的正確性的,參數a如上所述是代表當前角度的。參數a傳遞的是引用,隨後將在繪制C之前用於旋轉坐標(見圖4和圖5)。
使用代碼
安裝控件
為了簡單起見,這個控件有自己的圖片。初始化圖象的代碼在LoadSamples()方法中。這個方法在Sample類的構造方法中調用。
控制翻頁的速度和數目
翻頁的速度是通過一個timer來控制的,public屬性TickSpeed能用來控制翻頁速度(毫秒為單位),動畫的幀數是通過每個timer間隔熱點移動的距離來控制的。public屬性暴露給外界以控制已經移動的距離x。
調整高度
沿著背頁的頂部裁減是一個問題,public屬性HeightAdjustment用於在控件頂部留一點空白區域便於裁剪。
開始動畫
這個組件公開兩個方法來開始動畫。
animateRightPageTurn() and animateLeftPageTurn().
注意事項
我本來試圖解當h=PAGE_HEIGHT時的非線性方程h = x Tan( 45 + ((45 * (x)) / PAGE_WIDTH) )中的x的值,我的這個蠻力辦法的代碼包含在源代碼中。是不是還有一個更優雅的辦法呢?
說明:程序的源代碼可以到http://download.csdn.net/source/381727下載。