首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> .NET 網頁程式設計 >> C# >> 關於C#

使用 C# 與 GDI+ 實現雜誌翻頁動畫效果

     編輯:關於C#

前言

早年我參與一個電子雜誌專案,需求是模擬真實雜誌的翻頁動畫效果。同事推薦了一篇 CodeProject 的經典文章,最終成功達成預期效果。今日嘗試將該文翻譯整理,期盼能幫助英文不佳的開發者;若英文能力許可,建議直接閱讀 英文原文。翻譯水平有限,敬請包涵。

效果展示

翻頁效果示意圖

圖 1 翻頁效果

本文介紹如何在電子書或電子相簿並列顯示圖片時,建立逼真的翻頁效果,模擬實體書本翻頁的視覺體驗。

開發背景

靈感源自對可重用圖片顯示元件的需求,採用 C# 與 GDI+ 實現,旨在深化對 .NET 圖形程式設計的理解。核心演算法參考自 Sham Bhangal 的文章〈Flash MX 中的翻頁效果〉(原文連結),該文闡述如何利用對稱線控制動畫中頁面的可見區域。雖然 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 頁)。

當前頁面的處理

原文指出:當前頁(第 3、4 頁)不參與翻頁動畫,意即每一面皆先以無裁剪、無轉換的方式完整繪製,區域 B、C 再疊加於其上。

熱點

「熱點」一詞源自參考文獻,代表翻頁時折痕在水平軸上的位置,亦即對稱線的起點。動畫過程中,熱點從頁邊緣移動至書脊中央;從頁邊到熱點的距離 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 增加,對稱線與頁邊、高度 h 構成直角三角形。當 h >= PAGE_HEIGHT 時,交集區域由三角形轉為梯形(高度固定為 PAGE_HEIGHT),圖 1 即為此狀態。無論三角形或梯形,該閉合路徑即為 B、C 兩區域的裁剪邊界。若在原始碼中將 INCLUDE_DRAW_GRAPHICS_PATH 設為 true,程式會在閉合路徑外繪製金色輪廓,便於除錯觀察。

區域 A:當前頁剩餘可見部分

即第 4 頁扣除 B、C 後仍可見的區域。

區域 B:下一頁的可見部分

來源為正在翻動頁面之下的頁面(本例為第 6 頁)。其可見範圍由對稱線與頁面形成的閉合曲線裁剪而成;上層頁面(第 4 頁)對應部分會被直接裁剪移除。隨 x 增加(a 同步增大),此可見區域逐漸擴大。若將原始碼 INCLUDE_UNDERSIDE_PAGE_IN_ANIMATION 設為 false,可移除區域 C,僅觀察 A、B 關係。

區域 C:翻動頁面的背面內容

代表第 5 頁的可見部分。同樣由對稱線決定,但位於對稱線左側。例如翻第 4 頁時,B 為第 6 頁右下部,C 為第 5 頁左下部。圖 3 展示 B、C 空間關係。

B與C區域關係示意圖

圖 3 B 與 C 之間的關係

繪製區域 C 時,需建立一個輔助的後台圖像 pageUndersideImage,其裁剪區域同樣由對稱線決定。第 5 頁內容繪製至該緩衝區,再貼近區域 B 繪製。

連接 B 與 C 區域

後台圖像備妥後,依下列步驟繪製:

  1. 將座標系原點平移至熱點。
  2. 旋轉座標系 180 - 2a 度(參見圖 4)。
  3. 在座標 (-x, -PAGE_HEIGHT) 繪製含區域 C 的圖像(參見圖 5)。
調整角度以匹配對稱線

圖 4 調整角度以匹配對稱線

旋轉座標系

圖 5 旋轉座標系

解決「血邊」問題

實作中發現 B、C 沿對稱線銜接處易出現色彩溢出(如第 4 頁紅色像素外洩)。採用兩種變通方案:

  • 設定 g.PixelOffsetMode = PixelOffsetMode.Half;
  • 在旋轉座標前,對 X 軸微調 1 像素:
    PathTranslationMatrix.Translate((float)hotSpot.Origin.X + 1, (float)hotSpot.Origin.Y);

若啟用第一種方案,請將原始碼 USE_PIXEL_MODE_OFFSET 設為 true。

動畫繪製流程

每一幀皆在 timer1_Tick 中繪製至緩衝區 CurrentShownBitmap,再由 CurrentShownBitmap 直接呈現在螢幕上。

核心方法說明

計算每幀 B、C 區域圖像的關鍵方法:

private GraphicsPath GetPageUnderGraphicsPath(int x,
    ref double a, int height, int width,
    bool isUnderSide, TurnType type)
  • x:頁邊到熱點的距離。
  • height、width:當前頁顯示尺寸。
  • isUnderSide:是否計算區域 C(翻動頁背面)。
  • type:左翻或右翻。
  • a:傳址參數,回傳當前角度,供繪製區域 C 前旋轉座標使用(對應圖 4、5)。

元件使用指南

安裝與初始化

元件內建示範圖片,初始化邏輯位於 LoadSamples(),於 Sample 建構子中呼叫。

控制速度與幀數

透過 Timer 控制翻頁速度,公開屬性 TickSpeed(毫秒)調整間隔;動畫幀數由熱點每次移動距離決定,公開屬性暴露目前移動距離 x。

高度微調

針對背頁頂部裁剪問題,提供 HeightAdjustment 屬性在控制項頂部保留空白區域。

啟動動畫

提供兩個公開方法:animateRightPageTurn() 與 animateLeftPageTurn()。

已知問題與改進方向

原作者嘗試解析非線性方程 h = x * Tan(45 + (45 * x) / PAGE_WIDTH) 在 h = PAGE_HEIGHT 時的 x 值,採用暴力求解,程式碼已包含在原始碼中。期待社群提供更優雅的數學解法。

原始碼下載:https://download.csdn.net/source/381727

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved