Title: Draw animated text in C#
Note that the result in the executable is better than the result giving by the GIF on the right. The GIF is a bit jerky because it uses only 20 frames instead of the 200 used by the program.
This example shows how to draw animated text to make the letters in a word move slowly apart, an effect sometimes used in movie credits. The program draws the text in a PictureBox. When it starts, the program uses the following code to record some information about the PictureBox control's current and desired final sizes.
// The PictureBox's current size.
private float StartWidth;
private int StartHeight;
private float EndWidth = 260;
private float Dx, CurrentWidth;
private int TicksToGo, TotalTicks;
// Information about the string to draw.
private const string LabelText = "C# Programming";
private Font TextFont;
private float[] CharacterWidths;
private float TotalCharacterWidth;
private void Form1_Load(object sender, EventArgs e)
{
// Set the initial size.
StartWidth = picTitle2.Size.Width;
StartHeight = picTitle2.Size.Height;
CurrentWidth = StartWidth;
// Stretch for 2 seconds.
TotalTicks = 2 * 1000 / tmrResizePictureBox.Interval;
Dx = (EndWidth - StartWidth) / TotalTicks;
// Make the font and measure the characters.
CharacterWidths = new float[LabelText.Length];
TextFont = new Font("Times New Roman", 16);
using (Graphics gr = this.CreateGraphics())
{
for (int i = 0; i < LabelText.Length; i++)
{
SizeF ch_size = gr.MeasureString(
LabelText.Substring(i, 1), TextFont);
CharacterWidths[i] = ch_size.Width;
}
}
TotalCharacterWidth = CharacterWidths.Sum();
}
The code saves the PictureBox control's starting height, and starting and ending width.
It then calculates the number of times the tmrResizePictureBox Timer control's Tick event will fire in 2 seconds. It uses that number to calculate the amount Dx by which the program must enlarge the PictureBox during each Tick event to make the control reach its final width.
The form's Load event handler finishes by using the Graphics class's MeasureString method to measure each of the characters in the string that the program will display.
When you click the Animate button, the program use the following code to start the animated text moving.
// Resize the PictureBox.
private void btnAnimate_Click(object sender, EventArgs e)
{
btnAnimate.Enabled = false;
CurrentWidth = StartWidth;
picTitle2.Size =
new Size((int)StartWidth, picTitle2.Size.Height);
picTitle2.Refresh();
TicksToGo = TotalTicks;
tmrResizePictureBox.Enabled = true;
}
This code disables the button and resets the PictureBox control's width to its initial value. It sets TicksToGo = TotalTicks to keep track of the number of times the Tick event handler executes.
The following code shows the timer's Tick event handler, which makes the animated text move.
// Resize the PictureBox.
private void tmrResizePictureBox_Tick(object sender, EventArgs e)
{
CurrentWidth += Dx;
picTitle2.Size = new Size((int)CurrentWidth, StartHeight);
picTitle2.Refresh();
// If we're done moving, disable the Timer.
if (--TicksToGo <= 0)
{
tmrResizePictureBox.Enabled = false;
btnAnimate.Enabled = true;
}
}
This code adds Dx to the PictureBox control's width and resizes the PictureBox accordingly. Next it decrements TicksToGo and if the new value is 0 it disables the timer and enables the Animate button.
The program draws the animated text in the PictureBox control's Paint event handler. The following code shows the event handler and the SpaceTextToFit method that it calls.
// Draw the text on the control.
private void picTitle2_Paint(object sender, PaintEventArgs e)
{
// Use AntiAlias for the best result.
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAlias;
e.Graphics.Clear(picTitle2.BackColor);
SpaceTextToFit(e.Graphics, picTitle2.ClientRectangle,
TextFont, Brushes.Red, LabelText);
}
// Draw text inserting space between characters
// to make it fill the indicated width.
private void SpaceTextToFit(Graphics gr,
Rectangle rect, Font font, Brush brush, string text)
{
using (StringFormat string_format = new StringFormat())
{
string_format.Alignment = StringAlignment.Near;
string_format.LineAlignment = StringAlignment.Near;
// Calculate the spacing.
float space = (rect.Width - TotalCharacterWidth) /
(text.Length - 1);
// Draw the characters.
PointF point = new PointF(rect.X, rect.Y);
for (int i = 0; i < text.Length; i++)
{
gr.DrawString(text[i].ToString(), font, brush, point);
point.X += CharacterWidths[i] + space;
}
}
}
The Paint event handler simply calls the SpaceTextToFit method to do all of the interesting work. The only trick is setting the Graphics object's TextRenderingHint property to AntiAlias. The "grid fit" values (one of those is used by default) generally give better performance but mess up the character alignment in this example giving the result a jerky appearance.
The SpaceTextToFit method draws text adding extra space between the characters to fill a desired width. It doesn't do anything fancy with the text's height.
The code subtracts the total width of the characters from the width of the target area and divides it into pieces to insert between the characters.
The code finishes by drawing each character, moving to the right by the width of the character plus the extra space before drawing the next character.
Download the example to experiment with it and to see additional details.
|