#E6E6E6 【VB 物件】TextScroll 實現文字走馬燈 作者:吳文成

  在上一篇文章 , 介紹了實現圖片循環捲動的物件 PictureScroll, 接著要來談談文字循環捲動(即文字走馬燈)的問題,以及由我所撰寫的物件

向右單向捲動的走馬燈

向左單向捲動的走馬燈

來回捲動的走馬燈
 
類別 TextScroll 。 在網頁上, 我們可以輕易地寫出走馬燈效果,例如旁邊的範例分別呈現:向右單向捲動、向左單向捲動與來回捲動的走馬燈模式。只是簡單地使用網頁語法<MARQUEE></MARQUEE> 即可 ,我們以參數 direction 來控制文字捲動的方向,以參數 scrollamount 來調整文字移動的速度 , 利用設定參數 behavior=alternate 來使走馬燈來回捲動,例如第三個範例的整個語法是 : <MARQUEE scrollAmount=1 behavior=alternate bgColor=#006600 width=198 height=20>來回捲動的走馬燈</MARQUEE>。以上看起來很容易理解 , 但是如果想在 Visual Basic 或 Visual C++ 上實現走馬燈效果,那就要花點腦筋,土法煉鋼了。

  文字捲動與圖片捲動一樣,在視覺效果上其原理是簡單的,以來回捲動的走馬燈為例,我們將整個文字視為一個物件,當正在移動的文字物件右端「碰」到區域右邊界的時候,文字物件的移動方向就反過來,然後以同樣的方式,再偵測文字物件的左端是否「碰到」區域左邊界──這樣的構思流程是實體觀點的類比式描述,但是當我們想要將它寫成數位式的邏輯程序,也就是,當我們想要將類比式的描述「翻譯成」數位式的語法的時候,我們會需要考慮更多複雜的情況。例如怎麼翻譯「碰到」的數位意義?在實體觀點,「碰到」就是兩個物體邊界在某個時間點,最接近之後無法再交錯的情況,這個描述已經預設了最基本的剛體概念,但是在數位世界最基本的概念卻是數字與邏輯( and、or、>、<、= 等等), 也就是說,我們必須要用後面這些概念來翻譯什麼是「碰到」。然而在此之前
,我們要先界定什麼是文字物件,才能夠談移動中的文字物件「碰到」區域邊界的情況。所謂物件就是集合若干資料、屬性與方法的單位個體。

  編程語言可以選擇性地,只賦予某物件特定的內容與組成元素,例如對於上述的文字物件(假設叫做 Text),在所有可能的屬性裡,我們只需要知道它的長度(Text.TotalWidth)與目前相對於區域的位置(Text.Left)就可以來定義「碰到」的數位意義,當 Text.Left + Text.TotalWidth >= 區域右邊界,這就是文字物件右端與區域右邊界「重合」的數位分析,在固定時間間隔的檢測下,我們可以知道何時數值已經「重合」,然後再將文字物件的移動變化率乘上 (-1) 以表示反向移動。看到這兩段,我好像在談一個很奇怪的話題,對於熟練的程式設計者來說,上述技巧根本是基本功,但是我希望把焦點擺到「翻譯問題」上,翻譯問題可以存在於實體世界與邏輯世界、類比觀點與數位觀點、甚至也存在於兩套不同的數位編程裡,這類問題的尖銳性在人工生命與人工智慧的領域更為明顯,並非所有的(
互相)翻譯都是可能的,即使它們是可譯的,這種翻譯不代表是等價的。

  在我撰寫的 TextScroll 物件類別 ,考慮了稍微複雜的走馬燈樣式, 與[[img src=computer/TextScroll1.gif height=59 width=202 align=left]]上面的前兩個範例不同,如左圖所示,當文字「穿過」邊界的時候,消失的文字部分應該要出現在另外一邊才對 , TextScroll 物件[[img src=computer/TextScroll2.gif height=59 width=202 align=left]]考慮了這樣的呈現方式 ! TextScroll 也允許使用者設定文字捲動區域的留白空間(百分比 , X_MarginPercent 與 Y_MarginPercent 屬[[img src=computer/TextScroll3.gif height=59 width=202 align=left]]性),同時能夠設定背景圖片(當然,背景圖片不會跟著文字一起捲動)。在文字捲動(ScrollMode)方面 , 我並沒有提供向上與向下捲動的模式,有興趣的人可以增加不同的捲動模式。此物件使用了幾個不常用的 Window API : CreateRectRgnIndirect 與 SelectClipRgn 等等,前者的作用在於創建一個指定的矩形區域,而後者的作用在於將這個矩形區域設定為某控件設備的新的剪裁區,使得所有的繪製作業都只在這個剪裁[[img src=computer/TextScroll4.gif height=59 width=202 align=left]]區發生。這些函數的運用能夠實現留白空間
,並且使得指定區域內的文字繪製得到適當的裁剪(即剪裁區之外的文字將被「裁」掉
不見)
,如左圖所示。我將較為關鍵的部分原始碼,給列舉在下面,你可以下載範例原始碼,自己測試或新加入功能。

' 計算字型與文字捲動區域的大小,繪出文字
Private Sub CalculateMetrics()
 On Error Resume Next

 Dim tmp As Long

 With textBox
  ' 指定文字捲動區域
  cliprc.Left = .ScaleWidth * m_X_MarginPercent / 200
  cliprc.Right = .ScaleWidth - cliprc.Left
  cliprc.Top = .ScaleHeight * m_Y_MarginPercent / 200
  cliprc.Bottom = .ScaleHeight - cliprc.Top
  If cliprc.Top < 1 Then cliprc.Top = 1
  If cliprc.Left < 1 Then cliprc.Left = 1
  tmp = .ScaleWidth - 1
  If cliprc.Right > tmp Then cliprc.Right = tmp
  tmp = .ScaleHeight - 1
  If cliprc.Bottom > tmp Then cliprc.Bottom = tmp
  ' 依據捲動區域,計算字型的大小與總長度
  Set .Font = m_Font
  tmp = cliprc.Bottom - cliprc.Top - 4
  If tmp < 4 Then .FontSize = 4 Else .FontSize = tmp
 End With
 
 If textRgn > 0 Then DeleteObject textRgn
 ' 建立文字捲動區域,並繪出文字
 textRgn = CreateRectRgnIndirect(cliprc)
 resetScroll
End Sub

Private Sub tmrScroll_Timer()
 If textWidth = 0 Then Exit Sub
 
 With textBox
  .Refresh
  .Cls
  .AutoRedraw = False
  ' 選定文字捲動區域物件
  ' 這使得任何繪製不超出此區域

  SelectClipRgn .hDC, textRgn
 
  RedrawText True
 
  ' 取消原本選定的文字捲動區域物件
  SelectClipRgn .hDC, 0
 End With
End Sub

Private Sub RedrawText(Optional isInScroll As Boolean)
 Dim drawrc As RECT, cliprcWidth As Long, tmp As Long
 
 With textBox
 .Cls
 drawrc = cliprc
 cliprcWidth = cliprc.Right - cliprc.Left
 
 Select Case m_ScrollMode
 Case 向右單向捲動
  drawrc.Left = cliprc.Left + offsetX
  drawText .hDC, m_Text, -1, drawrc, textAlign
  
  tmp = drawrc.Left + textWidth - drawrc.Right
  If tmp > 0 Then
   drawrc.Left = cliprc.Left - textWidth + tmp
   drawText .hDC, m_Text, -1, drawrc, textAlign
  End If
  
  If isInScroll Then offsetX = offsetX + m_ScrollStep
  If offsetX >= cliprcWidth Then offsetX = 0
 Case 向左單向捲動
  drawrc.Left = cliprc.Left + offsetX
  drawText .hDC, m_Text, -1, drawrc, textAlign
  
  If offsetX < 0 Then
   drawrc.Left = cliprc.Right + offsetX
   drawText .hDC, m_Text, -1, drawrc, textAlign
  End If
  
  If isInScroll Then offsetX = offsetX - m_ScrollStep
  If offsetX <= -textWidth Then offsetX = cliprcWidth - textWidth
 Case 來回捲動
  drawrc.Left = cliprc.Left + offsetX
  drawText .hDC, m_Text, -1, drawrc, textAlign

  If isInScroll Then offsetX = offsetX + (m_ScrollStep * oscMode)
  If drawrc.Left + textWidth >= cliprc.Right Then oscMode = -1
  If offsetX <= 0 Then oscMode = 1
 Case Else
  Err.Raise 513, , "指定了錯誤的屬性值"
 End Select
 End With
End Sub

範例源碼下載
2004/10/09