views:

925

answers:

4

I want to make a text box in .NET "glow" yellow, and then "fade" to white (basically, by incrementally increasing the brightness). I think Stackoverflow does this after you've posted an answer. I know that increasing brightness is not all that simple (it's not just uniformly increasing/decreasing RGB), but I'm not sure how to do this.

Perfect color accuracy is not important for this. I am using C#, although VB examples would be just fine, too.

Edit: This is for Winforms.

A: 

You didn't originally specify a technology when I submitted this answer, but here's how you'd do it using jQuery.

UI/Effects/Highlight.

$("div").click(function () {
      $(this).effect("highlight", {}, 3000);
});
Chris Doggett
he said "winforms"
Neil N
Bah, apparently the future is "web apps" in "the cloud" on your "e-companion". So he should wrap Webkit in his application and use the CSS Effects and Animations that it supports!
JeeBee
I feel so retro :)
Jon B
In an edit, he said "winforms". Originally, when I responded, it referred to the effect here on SO only, with no mention of WinForms.
Chris Doggett
It's the danger in jumping on people for answering with the wrong technology. I would suggest qualifying the answer to avoid eager downvoters: "You didn't say what technology, but here's how I'd do it in jQuery..."
Michael Meadows
Good point. Edited.
Chris Doggett
+1  A: 

Just interpolate between the colors based on the time.

If your orange color is (r1,g1,b1) and you wish to fade to a different color (r2,g2,b2), the formula for linear interpolation is (r1 + (r2-r1) * t, g1 + (g2-g1) * t, b1 + (b2-b1) * t), where t is in the range of [0.0 1.0].

In your example, your first color is probably something like (255,200,0) and your second color would be (255,255,255).

If you wish for smoother transitions, look up different ways of interpolation.

Andrei Krotkov
A: 

If you want to do it qucikly and smoothly, take a look at the ColorMatrix class.

Neil N
+6  A: 

This may be more than you need, here's the code for the class I use:

public class ControlColorAnimator
{
    private const int INTERVAL = 100;

    private readonly decimal _alphaIncrement;
    private readonly decimal _blueIncrement;
    private readonly Color _endColor;
    private readonly decimal _greenIncrement;
    private readonly int _iterations;
    private readonly decimal _redIncrement;
    private readonly Color _startColor;

    private decimal _currentAlpha;
    private decimal _currentBlueValue;
    private decimal _currentGreenValue;
    private decimal _currentRedValue;

    private Timer _timer;

    public ControlColorAnimator(TimeSpan duration, Color startColor, Color endColor)
    {
        _startColor = startColor;
        _endColor = endColor;
        resetColor();

        _iterations = duration.Milliseconds / INTERVAL;
        _alphaIncrement = ((decimal) startColor.A - endColor.A) / _iterations;
        _redIncrement = ((decimal) startColor.R - endColor.R) / _iterations;
        _greenIncrement = ((decimal) startColor.G - endColor.G) / _iterations;
        _blueIncrement = ((decimal) startColor.B - endColor.B) / _iterations;
    }

    public Color CurrentColor
    {
        get
        {
            int alpha = Convert.ToInt32(_currentAlpha);
            int red = Convert.ToInt32(_currentRedValue);
            int green = Convert.ToInt32(_currentGreenValue);
            int blue = Convert.ToInt32(_currentBlueValue);

            return Color.FromArgb(alpha, red, green, blue);
        }
    }

    public event EventHandler<DataEventArgs<Color>> ColorChanged;

    public void Go()
    {
        disposeOfTheTimer();
        OnColorChanged(_startColor);

        resetColor();

        int currentIteration = 0;
        _timer = new Timer(delegate
            {
                if (currentIteration++ >= _iterations)
                {
                    Stop();
                    return;
                }
                _currentAlpha -= _alphaIncrement;
                _currentRedValue -= _redIncrement;
                _currentGreenValue -= _greenIncrement;
                _currentBlueValue -= _blueIncrement;
                OnColorChanged(CurrentColor);
            }, null, TimeSpan.FromMilliseconds(INTERVAL), TimeSpan.FromMilliseconds(INTERVAL));
    }

    public void Stop()
    {
        disposeOfTheTimer();
        OnColorChanged(_endColor);
    }

    protected virtual void OnColorChanged(Color color)
    {
        if (ColorChanged == null) return;
        ColorChanged(this, color);
    }

    private void disposeOfTheTimer()
    {
        Timer timer = _timer;
        _timer = null;

        if (timer != null) timer.Dispose();
    }

    private void resetColor()
    {
        _currentAlpha = _startColor.A;
        _currentRedValue = _startColor.R;
        _currentGreenValue = _startColor.G;
        _currentBlueValue = _startColor.B;
    }
}

This uses DataEventArgs<T> (shown below)

/// <summary>
/// Generic implementation of <see cref="EventArgs"/> that allows for a data element to be passed.
/// </summary>
/// <typeparam name="T">The type of data to contain.</typeparam>
[DebuggerDisplay("{Data}")]
public class DataEventArgs<T> : EventArgs
{
    private T _data;

    /// <summary>
    /// Constructs a <see cref="DataEventArgs{T}"/>.
    /// </summary>
    /// <param name="data">The data to contain in the <see cref="DataEventArgs{T}"/></param>
    [DebuggerHidden]
    public DataEventArgs(T data)
    {
        _data = data;
    }

    /// <summary>
    /// Gets the data for this <see cref="DataEventArgs{T}"/>.
    /// </summary>
    public virtual T Data
    {
        [DebuggerHidden]
        get { return _data; }
        [DebuggerHidden]
        protected set { _data = value; }
    }

    [DebuggerHidden]
    public static implicit operator DataEventArgs<T>(T data)
    {
        return new DataEventArgs<T>(data);
    }

    [DebuggerHidden]
    public static implicit operator T(DataEventArgs<T> e)
    {
        return e.Data;
    }
}

Use in your form like this:

private ControlColorAnimator _animator;

private void runColorLoop()
{
    endCurrentAnimation();
    startNewAnimation();
}

private void endCurrentAnimation()
{
    ControlColorAnimator animator = _animator;
    _animator = null;
    if (animator != null)
    {
        animator.ColorChanged -= _animator_ColorChanged;
        animator.Stop();
    }
}

private void startNewAnimation()
{
    _animator = new ControlColorAnimator(TimeSpan.FromSeconds(.6), Color.Yellow, BackColor);
    _animator.ColorChanged += _animator_ColorChanged;
    _animator.Go();
}

private void _animator_ColorChanged(object sender, DataEventArgs<Color> e)
{
    invokeOnFormThread(delegate { setColor(e); });
}

private void setColor(Color color)
{
    // code to set color of the controls goes here
}

private void invokeOnFormThread(MethodInvoker method)
{
    if (IsHandleCreated)
        Invoke(method);
    else
        method();
}
Michael Meadows
This is very clever, thanks :) I’ve modified it (to pass a Hide parameter so when the animation ends, the control is hidden) and added it to my baseclass, now all my inherited forms could potentially override setColor and use it. I haven’t found an elegant way to be able to pass a control where the animation will occur, instead of “hardcoding” it in setColor. Any ideas?
Martín Marconcini
Haven't tried it, but maybe an extension method on the control... then you could call it like this "invokeOnFormThread(myControl.SetColor(e));"
Michael Meadows
Thanks, will investigate. In the mean time, I’ve modified runColorLoop() so i can pass a color, a duration, a control and if I want to hide after the animation is over, that way I can use all this in my base class and every inherited form can make use of base.runColorLoop(x,y,z,f);
Martín Marconcini