Use an animated cursor in C#

[animated cursor]

This example doesn’t show how to use an animated cursor file such as an animated gif or png file. As far as I know, C# doesn’t support that kind of cursor.

What this example does do is show how to use a series of cursor images to make an animated cursor at runtime.

The program starts by using the following code to define an array of Cursor objects.

// Cursors.
private Cursor[] Cursors;
private const int NumCursors = 18;

The form’s Load event handler creates the cursors at run time. That code is kind of long so if you’re not interested in seeing how the cursors are created, skip down past the following code.

private void Form1_Load(object sender, EventArgs e)
{
    // Geometry.
    const int cursor_wid = 32;
    const int cursor_hgt = 32;
    float cx = cursor_wid / 2f;
    float cy = cursor_hgt / 2f;
    float rx = cx * 0.9f;
    float ry = cx * 0.4f;
    RectangleF rect = new RectangleF(-rx, -ry, 2 * rx, 2 * ry);
    float radius = cx * 0.15f;

    // Make the transformations we will use.
    Matrix transform1 = new Matrix();
    transform1.Rotate(60f, MatrixOrder.Append);
    transform1.Translate(cx, cy, MatrixOrder.Append);
    Matrix transform2 = new Matrix();
    transform2.Rotate(-60f, MatrixOrder.Append);
    transform2.Translate(cx, cy, MatrixOrder.Append);
    Matrix transform3 = new Matrix();
    transform3.Translate(cx, cy, MatrixOrder.Append);

    // Make an orbital image.
    Bitmap orbital_bm = new Bitmap(cursor_wid, cursor_hgt);
    using (Graphics gr = Graphics.FromImage(orbital_bm))
    {
        // Use a transparent background.
        gr.SmoothingMode = SmoothingMode.AntiAlias;
        gr.Clear(Color.Transparent);

        // Draw the orbitals.
        gr.Transform = transform1;
        gr.DrawEllipse(Pens.Red, rect);

        gr.Transform = transform2;
        gr.DrawEllipse(Pens.Red, rect);

        gr.Transform = transform3;
        gr.DrawEllipse(Pens.Red, rect);

        // Draw the nucleus.
        gr.FillEllipse(Brushes.Black,
            -radius, -radius, 2 * radius, 2 * radius);
    }

    // Make the cursors.
    Cursors = new Cursor[NumCursors];
    double theta1 = 0;
    double dtheta1 = 2 * Math.PI / NumCursors;
    double theta2 = 0;
    double dtheta2 = 2 * Math.PI / NumCursors * 2;
    double theta3 = 0;
    double dtheta3 = 2 * Math.PI / NumCursors * 3;
    for (int i = 0; i < NumCursors; i++)
    {
        Bitmap cursor_bm = new Bitmap(cursor_wid, cursor_hgt);
        using (Graphics gr = Graphics.FromImage(cursor_bm))
        {
            // Copy the background orbitals.
            gr.SmoothingMode = SmoothingMode.AntiAlias;
            gr.DrawImage(orbital_bm, 0, 0);

            // Draw the electrons.
            gr.Transform = transform1;
            double x1 = rx * Math.Cos(theta1);
            double y1 = ry * Math.Sin(theta1);
            gr.FillEllipse(Brushes.Red,
                (int)(x1 - radius), (int)(y1 - radius),
                2 * radius, 2 * radius);
            theta1 += dtheta1;

            gr.Transform = transform2;
            double x2 = rx * Math.Cos(theta2);
            double y2 = ry * Math.Sin(theta2);
            gr.FillEllipse(Brushes.Green,
                (int)(x2 - radius), (int)(y2 - radius),
                2 * radius, 2 * radius);
            theta2 += dtheta2;

            gr.Transform = transform3;
            double x3 = rx * Math.Cos(theta3);
            double y3 = ry * Math.Sin(theta3);
            gr.FillEllipse(Brushes.Blue,
                (int)(x3 - radius), (int)(y3 - radius),
                2 * radius, 2 * radius);
            theta3 += dtheta3;
        }

        // Turn the bitmap into a cursor.
        Cursors[i] = new Cursor(cursor_bm.GetHicon());

        // Increment theta.
        theta1 += dtheta1;
    }
}

The Load event handler makes three transformations to rotate images 60°, -60°, and 0°. Each of the transformations also translates the result to center it on the cursor bitmap.

Next, the code makes a bitmap holding the orbits and nucleus of the atom. All of the cursors will share that background. The code makes the orbits by drawing an ellipse using each of the three transformations. It uses the final transformation to draw the nucleus centered in the cursor bitmap.

Now the code enters a loop to draw each of the animated cursor images. For each cursor, the code copies the orbit background image into a new bitmap. It then draws three electrons using the three transformations. After it draws each electron, the code updates that electron’s theta value, which indicates its position along its orbit. Each theta value is updated by a different amount, so the electrons move at different speeds.

After it has drawn a cursor’s image, the code passes its GetHicon value to the Cursor constructor to make the Cursor object.

The program uses a Timer to produce the animated cursor. The following code shows the Timer component’s Tick event.

// Display the next cursor.
private int CursorNumber = 0;
private void tmrSwitchCursor_Tick(object sender, EventArgs e)
{
    CursorNumber = (CursorNumber + 1) % NumCursors;
    this.Cursor = Cursors[CursorNumber];
}

This code simply cycles through the animated cursor images.

This example also lets you click and scribble to draw. I added that mostly to let you see that the cursors’ hotspot is at the center where the nucleus is. This part of the program isn’t hard but it’s also not central to the example so it isn’t described here. Download the example to see how it works.


Download Example   Follow me on Twitter   RSS feed   Donate




This entry was posted in drawing, graphics and tagged , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *