WinForm的动画效果实现
近来在CodeProject上看到有关Animation的介绍,觉得很不错,就学习了一下。在此做个总结,供以后参考
该效果实现的关键点在于,在类中添加了Timer控件,根据Timer控件的Tick事件,对控件的大小、颜色变化等进行更改,从而实现了动画效果
其中,有关渐进量的大小的公式为:百分比 * (结束值 – 开始值)+ 开始值。这样当开始值和结束值相互颠倒后,渐进量就为负值,既可以实现控件的相反变化
使用时将子类拖入设计窗口,在代码中,将子类的control属性附为要进行动画效果的控件,另外设置一下子类的StartBounds和EndBounds用于设置初始值,在设置一下StepSize属性,用于指定动画效果的快慢(即一次所变化的百分比),然后再适当的位置执行一下子类的Start方法就可以实现了
另外还能针对一些事件进行相应的设置,如AnimationFinished事件等
就说到这里,还是看看代码吧,写的比说清楚
以下是抽象基类的代码
using System;
using System.ComponentModel;
using System.Collections;
using System.Diagnostics;
using System.Drawing;
namespace Animations
{
#region Enum SynchronizationMode
public enum SynchronizationMode
{
None,
Start,
End,
ResetToCurrent
}
#endregion
#region Enum LoopMode
public enum LoopMode
{
None,
Repeat,
Bidirectional
}
#endregion
public abstract class AnimatorBase : System.ComponentModel.Component, System.ComponentModel.ISupportInitialize
{
#region Fields
public const double DEFAULT_STEP_SIZE = 2;
public const int DEFAULT_INTERVALL = 10;
public const LoopMode DEFAULT_LOOP_ANIMATION = LoopMode.None;
private const bool DEFAULT_NEVER_ENDING_TIMER = false;
private const SynchronizationMode DEFAULT_SYNCHRONIZATION_MODE = SynchronizationMode.None;
private const string SET_PROP_WITH_PARENT_ANIMATOR_ERROR_MESSAGE = "Property cannot be set while ParentAnimator is set to anything other than null.";
private System.Windows.Forms.Timer _timer;
private System.ComponentModel.Container components = null;
private double _stepSize = DEFAULT_STEP_SIZE;
private double _currentStep;
private LoopMode _loopMode = DEFAULT_LOOP_ANIMATION;
private bool _neverEndingTimer = DEFAULT_NEVER_ENDING_TIMER;
private SynchronizationMode _syncMode = DEFAULT_SYNCHRONIZATION_MODE;
private AnimatorBase _parentAnimator;
private AnimatorBase _triggerAnimator;
private bool _isInitializing = false;
private ArrayList _childAnimators = new ArrayList();
private bool _settingCurrentValue = false;
#endregion
#region Events
public event EventHandler AnimationStarted;
public event EventHandler AnimationStopped;
public event EventHandler AnimationContinued;
public event EventHandler AnimationFinished;
public event EventHandler StepSizeChanged;
public event EventHandler IntervallChanged;
public event EventHandler CurrentStepChanged;
public event EventHandler LoopAnimationChanged;
public event EventHandler StartValueChanged;
public event EventHandler EndValueChanged;
public event EventHandler SynchronizationModeChanged;
#endregion
#region Constructors
public AnimatorBase(System.ComponentModel.IContainer container)
{
container.Add(this);
InitializeComponent();
Initialize();
}
public AnimatorBase()
{
InitializeComponent();
Initialize();
}
private void Initialize()
{
_timer.Interval = DEFAULT_INTERVALL;
}
#endregion
#region Designer generated code
private void InitializeComponent()
{
this._timer = new System.Windows.Forms.Timer();
//
// _timer
//
this._timer.Tick += new EventHandler(this.OnTimerElapsed);
}
#endregion
#region Overridden from Component
/// <summary>
/// Frees used resources.
/// </summary>
/// <param name="disposing"></param>
protected override void Dispose( bool disposing )
{
this.ParentAnimator = null;
_childAnimators.Clear();
this.TriggerAnimator = null;
if( disposing )
{
if(components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#endregion
#region Public interface
#region Properties
#region Value getters and setters (abstract)
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public abstract object StartValue { get; set; }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public abstract object EndValue { get; set; }
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public object CurrentValue
{
get { return CurrentValueInternal; }
set
{
if (_settingCurrentValue)
throw new InvalidOperationException();
try
{
_settingCurrentValue = true;
CurrentValueInternal = value;
}
finally
{
_settingCurrentValue = false;
}
}
}
#endregion
[Browsable(true), DefaultValue(null), Category("Behavior")]
[Description("Gets or sets the AnimatorBase which should trigger the animation of this instance when it has finished animating.")]
public AnimatorBase TriggerAnimator
{
get { return _triggerAnimator; }
set
{
if (_triggerAnimator == value)
return;
if (_triggerAnimator == this)
throw new InvalidOperationException("Cannot set itself as TriggerAnimator.");
if (_triggerAnimator != null)
_triggerAnimator.AnimationFinished -= new EventHandler(OnTriggerAnimatorAnimationFinished);
_triggerAnimator = value;
if (_triggerAnimator != null)
_triggerAnimator.AnimationFinished += new EventHandler(OnTriggerAnimatorAnimationFinished);
}
}
[Browsable(true), DefaultValue(null), Category("Behavior")]
[RefreshProperties(RefreshProperties.Repaint)]
public AnimatorBase ParentAnimator
{
get { return _parentAnimator; }
set
{
if (_parentAnimator == value)
return;
if (_parentAnimator == this)
throw new InvalidOperationException("Cannot set itself as ParentAnimator.");
if (_parentAnimator != null)
_parentAnimator.RemoveChildAnimator(this);
_parentAnimator = value;
if (_parentAnimator != null)
_parentAnimator.AddChildAnimator(this);
}
}
[Description("Gets or sets the mode of design time synchronization.")]
[Browsable(true), Category("Design"), RefreshProperties(RefreshProperties.Repaint)]
public SynchronizationMode SynchronizationMode
{
get { return _syncMode; }
set { SetSynchronizationMode(value, true); }
}
[Description("Gets or sets the intervall (in milliseconds) between updates to the animation.")]
[Browsable(true), Category("Behavior"), DefaultValue(DEFAULT_INTERVALL)]
public int Intervall
{
get { return _timer.Interval; }
set { SetIntervall(value, true); }
}
[Description("Gets or sets the size of each step (in %) when updating the animation.")]
[Browsable(true), Category("Behavior"), DefaultValue(DEFAULT_STEP_SIZE)]
public double StepSize
{
get { return _stepSize; }
set { SetStepSize(value, true); }
}
[Description("Gets or sets whether the animation should loop between StartValue and EndValue until Stop() is called.")]
[Browsable(true), Category("Behavior"), DefaultValue(DEFAULT_LOOP_ANIMATION)]
public LoopMode LoopMode
{
get { return _loopMode; }
set { SetLoopMode(value, true); }
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public double CurrentStep
{
get { return _currentStep; }
set
{
if (_currentStep == value)
return;
_currentStep = value;
if (_currentStep > 100)
_currentStep = 100;
else if (_currentStep < 0)
_currentStep = 0;
CurrentValue = GetValueForStep(_currentStep);
foreach (AnimatorBase childAnimator in _childAnimators)
childAnimator.CurrentStep = _currentStep;
OnCurrentStepChanged(EventArgs.Empty);
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsRunning
{
get { return _parentAnimator == null ? _timer.Enabled : _parentAnimator.IsRunning; }
}
[Description("Gets or sets whether the internal timer should always continue running even if the animation has reached its end")]
[Browsable(true), Category("Behavior"), DefaultValue(DEFAULT_NEVER_ENDING_TIMER)]
public bool NeverEndingTimer
{
get { return _neverEndingTimer; }
set { _neverEndingTimer = value; }
}
#endregion
#region Animation controlling
public void Continue()
{
_timer.Start();
OnAnimationContinued(EventArgs.Empty);
}
public void Start(object endValue)
{
if (_childAnimators.Count > 0)
throw new InvalidOperationException("Function cannot be called when ChildAnimators are set.");
EndValue = endValue;
Start(true);
}
/// <summary>
/// Sets <see cref="AnimatorBase.StartValue"/> to
/// <see cref="AnimatorBase.CurrentValue"/> and starts the animation.
/// </summary>
public void Start()
{
Start(true);
}
public void Start(bool setStartValuesToCurrentValues)
{
if (setStartValuesToCurrentValues)
SetStartValuesToCurrentValue();
this.CurrentStep = 0;
if (!_timer.Enabled)
_timer.Start();
OnAnimationStarted(EventArgs.Empty);
}
public void SetCurrentValuesToStartValues()
{
CurrentValue = StartValue;
foreach (AnimatorBase childAnimator in _childAnimators)
childAnimator.SetCurrentValuesToStartValues();
}
public void SetStartValuesToCurrentValue()
{
StartValue = CurrentValue;
foreach (AnimatorBase childAnimator in _childAnimators)
childAnimator.SetStartValuesToCurrentValue();
}
public void SwitchStartEndValues()
{
object temp = StartValue;
StartValue = EndValue;
EndValue = temp;
foreach (AnimatorBase childAnimator in _childAnimators)
childAnimator.SwitchStartEndValues();
}
public void Stop()
{
if (_timer.Enabled)
{
_timer.Stop();
OnAnimationStopped(EventArgs.Empty);
}
}
#endregion
#endregion
#region Protected
#region Design time synchronization
protected void SynchronizeToSource()
{
if (!base.DesignMode)
return;
switch (_syncMode)
{
case SynchronizationMode.Start:
CurrentValue = StartValue;
break;
case SynchronizationMode.End:
CurrentValue = EndValue;
break;
}
}
protected void SynchronizeFromSource()
{
if (!base.DesignMode)
return;
switch (_syncMode)
{
case SynchronizationMode.Start:
StartValue = CurrentValue;
break;
case SynchronizationMode.End:
EndValue = CurrentValue;
break;
}
}
protected void ResetValues()
{
if (_isInitializing)
return;
StartValue = CurrentValue;
EndValue = CurrentValue;
}
#endregion
#region Event raising
protected virtual void OnAnimationStarted(EventArgs eventArgs)
{
if (AnimationStarted != null)
AnimationStarted(this, eventArgs);
}
protected virtual void OnAnimationContinued(EventArgs eventArgs)
{
if (AnimationContinued != null)
AnimationContinued(this, eventArgs);
}
protected virtual void OnAnimationStopped(EventArgs eventArgs)
{
if (AnimationStopped != null)
AnimationStopped(this, eventArgs);
}
protected virtual void OnAnimationFinished(EventArgs eventArgs)
{
if (AnimationFinished != null)
AnimationFinished(this, eventArgs);
}
protected virtual void OnLoopAnimationChanged(EventArgs eventArgs)
{
if (LoopAnimationChanged != null)
LoopAnimationChanged(this, eventArgs);
}
protected virtual void OnStepSizeChanged(EventArgs eventArgs)
{
if (StepSizeChanged != null)
StepSizeChanged(this, eventArgs);
}
protected virtual void OnIntervallChanged(EventArgs eventArgs)
{
if (IntervallChanged != null)
IntervallChanged(this, eventArgs);
}
protected void OnSynchronizationModeChanged(EventArgs eventArgs)
{
if (SynchronizationModeChanged != null)
SynchronizationModeChanged(this, eventArgs);
}
protected virtual void OnCurrentStepChanged(EventArgs eventArgs)
{
if (CurrentStepChanged != null)
CurrentStepChanged(this, eventArgs);
}
protected virtual void OnStartValueChanged(EventArgs eventArgs)
{
if (_syncMode == SynchronizationMode.Start)
CurrentValue = StartValue;
if (StartValueChanged != null)
StartValueChanged(this, eventArgs);
}
protected virtual void OnEndValueChanged(EventArgs eventArgs)
{
if (_syncMode == SynchronizationMode.End)
CurrentValue = EndValue;
if (EndValueChanged != null)
EndValueChanged(this, eventArgs);
}
#endregion
protected abstract object CurrentValueInternal { get; set; }
protected bool SettingCurrentValue
{
get { return _settingCurrentValue; }
}
protected abstract object GetValueForStep(double step);
protected void AddChildAnimator(AnimatorBase animator)
{
if (animator == null)
throw new ArgumentNullException("animator");
if (!_childAnimators.Contains(animator))
_childAnimators.Add(animator);
animator.SetIntervall(this.Intervall, false);
animator.SetStepSize(this.StepSize, false);
animator.SetLoopMode(this.LoopMode, false);
animator.SetSynchronizationMode(this.SynchronizationMode, false);
}
protected void RemoveChildAnimator(AnimatorBase animator)
{
if (animator == null)
throw new ArgumentNullException("animator");
if (_childAnimators.Contains(animator))
_childAnimators.Remove(animator);
}
protected bool IsInitializing
{
get { return _isInitializing; }
}
protected virtual bool ShouldSerializeSynchronizationMode()
{
return false;
}
protected virtual void OnCurrentValueChanged(object sender, EventArgs e)
{
if (SettingCurrentValue)
return;
SynchronizeFromSource();
}
#endregion
#region Privates
#region Internal setters
private void SetSynchronizationMode(SynchronizationMode synchronizationMode, bool checkParentAnimator)
{
if (_syncMode == synchronizationMode)
return;
if (synchronizationMode == SynchronizationMode.ResetToCurrent)
{
if (!base.DesignMode)
return;
ResetValues();
return;
}
if (_parentAnimator != null && checkParentAnimator && !_isInitializing)
throw new InvalidOperationException(SET_PROP_WITH_PARENT_ANIMATOR_ERROR_MESSAGE);
_syncMode = synchronizationMode;
SynchronizeToSource();
foreach (AnimatorBase childAnimator in _childAnimators)
childAnimator.SetSynchronizationMode(synchronizationMode, false);
OnSynchronizationModeChanged(EventArgs.Empty);
}
private void SetIntervall(int intervall, bool checkParentAnimator)
{
if (_timer.Interval == intervall)
return;
if (_parentAnimator != null && checkParentAnimator && !_isInitializing)
throw new InvalidOperationException(SET_PROP_WITH_PARENT_ANIMATOR_ERROR_MESSAGE);
_timer.Interval = intervall;
foreach (AnimatorBase childAnimator in _childAnimators)
childAnimator.SetIntervall(intervall, false);
OnIntervallChanged(EventArgs.Empty);
}
private void SetStepSize(double stepSize, bool checkParentAnimator)
{
if (_stepSize == stepSize)
return;
if (_parentAnimator != null && checkParentAnimator && !_isInitializing)
throw new InvalidOperationException(SET_PROP_WITH_PARENT_ANIMATOR_ERROR_MESSAGE);
_stepSize = stepSize;
foreach (AnimatorBase childAnimator in _childAnimators)
childAnimator.SetStepSize(stepSize, false);
OnStepSizeChanged(EventArgs.Empty);
}
private void SetLoopMode(LoopMode loopAnimation, bool checkParentAnimator)
{
if (_loopMode == loopAnimation)
return;
if (_parentAnimator != null && checkParentAnimator && !_isInitializing)
throw new InvalidOperationException(SET_PROP_WITH_PARENT_ANIMATOR_ERROR_MESSAGE);
_loopMode = loopAnimation;
foreach (AnimatorBase childAnimator in _childAnimators)
childAnimator.SetLoopMode(loopAnimation, false);
OnLoopAnimationChanged(EventArgs.Empty);
}
#endregion
#region Event handler
private void OnTimerElapsed(object sender, EventArgs e)
{
this.CurrentStep += _stepSize;
if (this.CurrentStep >= 100)
{
bool timerEnabled = _timer.Enabled;
if (_timer.Enabled && !_neverEndingTimer && _loopMode == LoopMode.None)
_timer.Stop();
OnAnimationFinished(EventArgs.Empty);
if (timerEnabled)
{
if (_loopMode == LoopMode.Repeat)
{
this.CurrentStep -= 100;
}
else if (_loopMode == LoopMode.Bidirectional)
{
SwitchStartEndValues();
this.Start();
}
}
}
}
private void OnTriggerAnimatorAnimationFinished(object sender, EventArgs e)
{
this.Start();
}
#endregion
#endregion
#region Static helpers for value interpolation
public static Color InterpolateColors(Color color1, Color color2, double percent)
{
return Color.FromArgb(
InterpolateIntegerValues(color1.A, color2.A, percent),
InterpolateIntegerValues(color1.R, color2.R, percent),
InterpolateIntegerValues(color1.G, color2.G, percent),
InterpolateIntegerValues(color1.B, color2.B, percent));
}
public static Rectangle InterpolateRectangles(Rectangle rectangle1, Rectangle rectangle2, double percent)
{
return new Rectangle(InterpolatePoints(rectangle1.Location, rectangle2.Location, percent),
InterpolateSizes(rectangle1.Size, rectangle2.Size, percent));
}
public static Point InterpolatePoints(Point point1, Point point2, double percent)
{
return new Point(InterpolateIntegerValues(point1.X, point2.X, percent),
InterpolateIntegerValues(point1.Y, point2.Y, percent));
}
public static Size InterpolateSizes(Size size1, Size size2, double percent)
{
return new Size(InterpolateIntegerValues(size1.Width, size2.Width, percent),
InterpolateIntegerValues(size1.Height, size2.Height, percent));
}
public static double InterpolateDoubleValues(double value1, double value2, double percent)
{
if (percent < 0 || percent > 100)
throw new ArgumentException("Value must be between 0 and 100.", "percent");
return percent * (value2 - value1) / 100 + value1;
}
public static int InterpolateIntegerValues(int value1, int value2, double percent)
{
if (percent < 0 || percent > 100)
throw new ArgumentException("Value must be between 0 and 100.", "percent");
return Convert.ToInt32(percent * (value2 - value1) / 100 + value1);
}
#endregion
#region ISupportInitialize Member
public void BeginInit()
{
_isInitializing = true;
}
public void EndInit()
{
_isInitializing = false;
}
#endregion
}
}
下面是他子类的代码
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Animations
{
public class ControlBoundsAnimator : AnimatorBase
{
#region Fields
private const bool DEFAULT_ANIMATE = true;
private Control _control;
private Rectangle _startBounds;
private Rectangle _endBounds;
private bool _animateX = DEFAULT_ANIMATE;
private bool _animateY = DEFAULT_ANIMATE;
private bool _animateWidth = DEFAULT_ANIMATE;
private bool _animateHeight = DEFAULT_ANIMATE;
#endregion
#region Constructors
public ControlBoundsAnimator(IContainer container) : base(container)
{
Initialize();
}
public ControlBoundsAnimator()
{
Initialize();
}
private void Initialize()
{
_startBounds = DefaultStartBounds;
_endBounds = DefaultEndBounds;
}
#endregion
#region Public interface
[Browsable(true), Category("Appearance")]
[Description("Gets or sets the starting bounds for the animation.")]
public Rectangle StartBounds
{
get { return _startBounds; }
set
{
if (_startBounds == value)
return;
_startBounds = value;
CheckBounds();
OnStartValueChanged(EventArgs.Empty);
}
}
[Browsable(true), Category("Appearance")]
[Description("Gets or sets the ending bounds for the animation.")]
public Rectangle EndBounds
{
get { return _endBounds; }
set
{
if (_endBounds == value)
return;
_endBounds = value;
CheckBounds();
OnEndValueChanged(EventArgs.Empty);
}
}
[Browsable(true), Category("Behavior"), DefaultValue(DEFAULT_ANIMATE)]
[Description("Gets or sets whether the Left property of the control should be animated.")]
public bool AnimateX
{
get { return _animateX; }
set
{
_animateX = value;
CheckBounds();
}
}
[Browsable(true), Category("Behavior"), DefaultValue(DEFAULT_ANIMATE)]
[Description("Gets or sets whether the Top property of the control should be animated.")]
public bool AnimateY
{
get { return _animateY; }
set
{
_animateY = value;
CheckBounds();
}
}
[Browsable(true), Category("Behavior"), DefaultValue(DEFAULT_ANIMATE)]
[Description("Gets or sets whether the Width property of the control should be animated.")]
public bool AnimateWidth
{
get { return _animateWidth; }
set
{
_animateWidth = value;
CheckBounds();
}
}
[Browsable(true), Category("Behavior"), DefaultValue(DEFAULT_ANIMATE)]
[Description("Gets or sets whether the Height property of the control should be animated.")]
public bool AnimateHeight
{
get { return _animateHeight; }
set
{
_animateHeight = value;
CheckBounds();
}
}
[Browsable(true), Category("Behavior")]
[DefaultValue(null), RefreshProperties(RefreshProperties.Repaint)]
[Description("Gets or sets which Control should be animated.")]
public Control Control
{
get { return _control; }
set
{
if (_control == value)
return;
if (_control != null)
{
_control.LocationChanged -= new EventHandler(OnCurrentValueChanged);
_control.SizeChanged -= new EventHandler(OnCurrentValueChanged);
}
_control = value;
if (_control != null)
{
_control.LocationChanged += new EventHandler(OnCurrentValueChanged);
_control.SizeChanged += new EventHandler(OnCurrentValueChanged);
}
base.ResetValues();
}
}
#endregion
#region Protected Interface
protected virtual Rectangle DefaultStartBounds
{
get { return Rectangle.Empty; }
}
protected virtual Rectangle DefaultEndBounds
{
get { return Rectangle.Empty; }
}
protected virtual bool ShouldSerializeStartBounds()
{
return _startBounds != DefaultStartBounds;
}
protected virtual bool ShouldSerializeEndBounds()
{
return _endBounds != DefaultEndBounds;
}
#endregion
#region Overridden from AnimatorBase
protected override object CurrentValueInternal
{
get { return _control == null ? Rectangle.Empty : _control.Bounds; }
set
{
if (_control != null && _control.Bounds != (Rectangle)value)
_control.Bounds = (Rectangle)value;
}
}
public override object StartValue
{
get { return StartBounds; }
set { StartBounds = (Rectangle)value; }
}
public override object EndValue
{
get { return EndBounds; }
set { EndBounds = (Rectangle)value; }
}
protected override object GetValueForStep(double step)
{
return InterpolateRectangles(_startBounds, _endBounds, step);
}
protected override void OnCurrentValueChanged(object sender, EventArgs e)
{
base.OnCurrentValueChanged(sender, e);
CheckBounds();
}
#endregion
#region Privates
private void CheckBounds(ref Rectangle bounds)
{
if (_control != null)
{
if (!_animateX)
bounds.X = _control.Bounds.X;
if (!_animateY)
bounds.Y = _control.Bounds.Y;
if (!_animateWidth)
bounds.Width = _control.Bounds.Width;
if (!_animateHeight)
bounds.Height = _control.Bounds.Height;
}
}
private void CheckBounds()
{
CheckBounds(ref _startBounds);
CheckBounds(ref _endBounds);
}
#endregion
}
}