Browser GUI technologies are clearly better than VBA's. Html5 is a wonderful leap forward with its animation capabilities. However, sometimes you'll want some code to help avoid fiddling with CSS. Why not VBA?
So I hope you just seen the above messages fade in one after the after. How is this achieved? Firstly, there is the opacity attribute (0 = invisible, 1 = visible). Then we have to animate the opacity and for that need the animation-name, animation-delay and animation-duration attributes. We also need a @keyframes rule to give the schedule for the opacity to change over time, in this example given as percentages. The animation-name attribute must tie in to the identifier given in the @keyframes rule. What follows is the source to drive this simple animation.
HTML to fade in text messages
- <style>
- .allText {
- font-size: 2rem;
- font-family: sans-serif;
- color: #000000;
- }
- @keyframes FadeIn10_20 {
- 0% { opacity: 0; }
- 10% { opacity: 0; }
- 20% { opacity: 1; }
- 100% { opacity: 1; }
- }
- .text0 {
- animation-name: FadeIn10_20;
- animation-delay: 0s;
- animation-duration: 10s;
- }
- @keyframes FadeIn30_40 {
- 0% { opacity: 0; }
- 30% { opacity: 0; }
- 40% { opacity: 1; }
- 100% { opacity: 1; }
- }
- .text1 {
- animation-name: FadeIn30_40;
- animation-delay: 0s;
- animation-duration: 10s;
- }
- @keyframes FadeIn50_60 {
- 0% { opacity: 0; }
- 50% { opacity: 0; }
- 60% { opacity: 1; }
- 100% { opacity: 1; }
- }
- .text2 {
- animation-name: FadeIn50_60;
- animation-delay: 0s;
- animation-duration: 10s;
- }
- @keyframes FadeIn70_80 {
- 0% { opacity: 0; }
- 70% { opacity: 0; }
- 80% { opacity: 1; }
- 100% { opacity: 1; }
- }
- .text3 {
- animation-name: FadeIn70_80;
- animation-delay: 0s;
- animation-duration: 10s;
- }
- </style>
- <div class='allText text0'>First Message</div>
- <div class='allText text1'>Second Message</div>
- <div class='allText text2'>Third Message</div>
- <div class='allText text3'>Fourth Message</div>
Initially, I struggled with the animation directives, I grew frustrated editing source CSS manually so I wrote some VBA to help set the timings because a change in duration of one message had a knock-on effect on the following messages. I needed some code to total the timings in seconds and then express the timing of the animation events as percentages.
I am aware that there are CSS pre-processors out there such as LESS and SASS but I'd rather use VBA. Here is the source
modCSSFadeInAnimation Standard Module
- Option Explicit
- Option Private Module
- '*
- '* Brought to you by the Excel Development Platform Blog
- '* http://exceldevelopmentplatform.blogspot.com/2018/12/
- '*
- Private mdicLines As Scripting.Dictionary
- Private Type udtMessage
- sText As String
- dStart As Double 'In seconds
- dFadeIn As Double 'In seconds
- lStartPercentage As Long
- lFadeInPercentage As Long
- lTop As Long
- dDuration As Double 'In seconds
- End Type
- Private Type udtMessages
- Messages() As udtMessage
- End Type
- Private mlTotalSeconds As Long
- Private Sub Main()
- Dim bFullHtmlDocument As Boolean
- bFullHtmlDocument = False '*<---- change to True to get a full html document instead of a fragment
- Dim uMessages As udtMessages
- AddMessage uMessages, 10, 2, "First Message", 2
- AddMessage uMessages, 20, 2, "Second Message"
- AddMessage uMessages, 30, 2, "Third Message"
- AddMessage uMessages, 40, 2, "Fourth Message"
- CalcFades uMessages
- OpenStyleTag bFullHtmlDocument
- If bFullHtmlDocument Then WritePositioningCSS uMessages
- WriteAnimationCSS uMessages ', 30
- WriteEndOfStyleBlock bFullHtmlDocument
- WriteBody uMessages, bFullHtmlDocument
- Debug.Print Join(mdicLines.Items, vbNewLine)
- End Sub
- Private Sub AddMessage(ByRef uMessages As udtMessages, ByVal lTop As Long, ByVal dDuration As Double, ByVal sText As String,
- Optional dStart0 As Double)
- Dim lIndex As Long
- If Not IsArrayInitialized(uMessages) Then
- '* not yet initialised
- lIndex = 0
- ReDim uMessages.Messages(0 To 0) As udtMessage
- Else
- lIndex = UBound(uMessages.Messages) + 1
- ReDim Preserve uMessages.Messages(0 To lIndex) As udtMessage
- End If
- If lIndex = 0 Then
- uMessages.Messages(lIndex).dStart = dStart0
- Else
- uMessages.Messages(lIndex).dStart = uMessages.Messages(lIndex - 1).dStart + uMessages.Messages(lIndex - 1).dDuration
- End If
- uMessages.Messages(lIndex).sText = sText
- uMessages.Messages(lIndex).lTop = lTop
- uMessages.Messages(lIndex).dDuration = dDuration
- End Sub
- Private Function IsArrayInitialized(ByRef uMessages As udtMessages)
- On Error GoTo ErrHand
- Dim lUbound As Long
- lUbound = UBound(uMessages.Messages)
- IsArrayInitialized = True
- Exit Function
- ErrHand:
- End Function
- Private Sub CalcFades(ByRef uMessages As udtMessages, Optional dDefaultFade As Double = 1)
- Dim lLoop As Long
- For lLoop = LBound(uMessages.Messages) To UBound(uMessages.Messages)
- If uMessages.Messages(lLoop).dFadeIn = 0 Then
- uMessages.Messages(lLoop).dFadeIn = uMessages.Messages(lLoop).dStart - dDefaultFade
- End If
- If uMessages.Messages(lLoop).dFadeIn < 0 Then uMessages.Messages(lLoop).dFadeIn = 0
- Next
- mlTotalSeconds = uMessages.Messages(UBound(uMessages.Messages)).dStart +
- uMessages.Messages(UBound(uMessages.Messages)).dDuration
- For lLoop = LBound(uMessages.Messages) To UBound(uMessages.Messages)
- uMessages.Messages(lLoop).lStartPercentage = 100 * uMessages.Messages(lLoop).dStart / mlTotalSeconds
- uMessages.Messages(lLoop).lFadeInPercentage = 100 * uMessages.Messages(lLoop).dFadeIn / mlTotalSeconds
- Next
- End Sub
- Private Sub OpenStyleTag(Optional bWriteHtmlTags As Boolean = False)
- Set mdicLines = New Scripting.Dictionary
- If bWriteHtmlTags Then
- AddLine "<!DOCTYPE html>"
- AddLine "<html>"
- AddLine "<head>"
- AddLine "<title>Presentation</title>"
- AddLine "<meta name='viewport' content='width=device-width, initial-scale=1'></meta>"
- End If
- AddLine "<style>"
- AddLine ".allText {"
- AddLine " font-size:2rem;"
- AddLine " font-family:sans-serif;"
- AddLine " color: #000000;"
- AddLine "}"
- AddLine ""
- End Sub
- Private Sub WritePositioningCSS(ByRef uMessages As udtMessages)
- Dim lLoop As Long
- For lLoop = LBound(uMessages.Messages) To UBound(uMessages.Messages)
- AddLine ".text" & lLoop & " {"
- AddLine " position: absolute;"
- AddLine " left: 5%;"
- If uMessages.Messages(lLoop).lTop = 0 Then
- AddLine " top: " & 5 * (lLoop + 1) & "%;"
- Else
- AddLine " top: " & uMessages.Messages(lLoop).lTop & "%;"
- End If
- AddLine "}"
- AddLine ""
- Next
- End Sub
- Private Sub WriteAnimationCSS(ByRef uMessages As udtMessages, Optional lTotalSeconds As Variant)
- If Not IsMissing(lTotalSeconds) Then mlTotalSeconds = lTotalSeconds
- Dim lLoop As Long
- For lLoop = LBound(uMessages.Messages) To UBound(uMessages.Messages)
- '* write keyframe, store keyframe name
- Dim sKeyFrameName As String
- KeyFramesFadeIn uMessages.Messages(lLoop).lFadeInPercentage, uMessages.Messages(lLoop).lStartPercentage, sKeyFrameName
- AddLine ".text" & lLoop & " {"
- AddLine " animation-name: " & sKeyFrameName & ";"
- AddLine " animation-delay: 0s;"
- AddLine " animation-duration: " & mlTotalSeconds & "s;"
- AddLine "}"
- AddLine ""
- Next
- End Sub
- Private Sub WriteEndOfStyleBlock(Optional bWriteHeadTag As Boolean = False)
- AddLine "</style>"
- If bWriteHeadTag Then AddLine "</head>"
- End Sub
- Private Sub WriteBody(ByRef uMessages As udtMessages, Optional bWriteHtmlAndBodyTags As Boolean = False)
- If bWriteHtmlAndBodyTags Then AddLine "<body>"
- Dim lLoop As Long
- For lLoop = LBound(uMessages.Messages) To UBound(uMessages.Messages)
- AddLine "<div class='allText text" & lLoop & "'>" & uMessages.Messages(lLoop).sText & "</div>" 'position: absolute;"
- Next
- If bWriteHtmlAndBodyTags Then
- AddLine "</body>"
- AddLine "</html>"
- End If
- End Sub
- Private Function KeyFramesFadeIn(ByVal lStartPercent As Long, ByVal lEndPercent As Long, ByRef psKeyFrameName As String)
- psKeyFrameName = "FadeIn" & Pad(lStartPercent, 2, "0") & "_" & Pad(lEndPercent, 2, "0")
- AddLine "@keyframes " & psKeyFrameName & " {"
- AddLine " 0% { opacity: 0; }"
- AddLine " " & Pad(lStartPercent, 3, " ") & "% { opacity: 0; }"
- AddLine " " & Pad(lEndPercent, 3, " ") & "% { opacity: 1; }"
- AddLine " 100% { opacity: 1; }"
- AddLine "}"
- AddLine ""
- End Function
- Private Sub AddLine(ByVal sLine As String)
- mdicLines.Add mdicLines.Count, sLine
- End Sub
- Private Function Pad(ByVal lNum As Long, ByVal lLen As Long, ByVal sChar As String) As String
- If Len(CStr(lNum)) > lLen Then lLen = Len(CStr(lNum))
- Pad = Right$(String$(lLen, sChar) & CStr(lNum), lLen)
- End Function
Hey, that's pretty neat, nice job!
ReplyDelete