今天我正在将paint事件中的像素闪烁。 在form1中,我在计时器刻度事件中有此代码,间隔设置为1000毫秒。
private void timer1_Tick(object sender, EventArgs e)
{
CloudEnteringAlert.cloudColorIndex = (CloudEnteringAlert.cloudColorIndex + 1) % CloudEnteringAlert.cloudColors.Length;
pictureBox1.Invalidate();
}
在CloudEntering类中,我位于顶部:
public static Brush[] cloudColors = new[] { Brushes.Yellow, Brushes.Transparent };
然后在一个paint方法中,这个paint方法我从form1 pictureBox1 paint事件调用:
foreach (PointF pt in clouds)
{
e.FillEllipse(cloudColors[cloudColorIndex], pt.X * (float)currentFactor, pt.Y * (float)currentFactor, 7f, 7f);
}
所以我现在看到的是一秒钟内像素为黄色,一秒钟内像素为透明。
现在我想做的是:
每个像素将从半径5开始。
每个像素半径将变大到25。
动画将从上到下。
达到半径25的每个像素将开始变小到半径为5.
当第一个像素从5开始到达半径15时,下一个像素将开始变大。
因此,如果第一个像素现在从5开始,那么下一个像素将开始变大,第一个像素将继续到25,当第一个像素为25时,它将变小。第二个是第15个,第三个将变大,等等。
所有像素都将从半径5开始,但只有第一个像素将开始变大,然后变为15,然后是下一个像素,当第一个像素变为25时,变小。
这是每个像素之间的时间。
每个像素半径大小的变化应该需要300毫秒!
我该怎么做?
答案 0 :(得分:0)
您希望首先将将单个椭圆呈现所需的所有信息封装到单独的类中。那将是这样的:
public class Ellipse
{
public PointF Center { get; set; }
public Brush Brush { get; set; }
public float Diameter { get; set; }
public float DiameterDelta { get; set; }
public Ellipse(float x, float y)
{
Center = new PointF(x, y);
Brush = Brushes.Blue;
Diameter = 5;
DiameterDelta = 1;
}
}
Ellipse.DiameterDelta
属性是将用于动画的delta值,它可以是正数(从直径5
到直径25
),或者是负数(当去向后)。此属性的值(我上面使用1
)和您的Timer.Interval
将影响动画的速度。
更好的OOP设计可能会提倡将动画相关属性移出此类,但为了简单起见,最好从此开始。
在您的计时器事件中,您可能会遇到以下情况:
private void timer_Tick(object sender, EventArgs e)
{
// presuming that you made a separate user control
// which has a collection of ellipses in a `Clouds` property
foreach (var c in cloudBox.Clouds)
Animate(c);
cloudBox.Invalidate();
}
private void Animate(Ellipse c)
{
// update diameter
c.Diameter += c.DiameterDelta;
// when you reach bounds, change delta direction
if ((c.DiameterDelta < 0 && c.Diameter <= 5) ||
(c.DiameterDelta > 0 && c.Diameter >= 25))
c.DiameterDelta = -c.DiameterDelta;
}
要获得平滑,无闪烁的动画,您很可能必须使用在其构造函数中设置ControlStyles.AllPaintingInWmPaint
和ControlStyles.OptimizedDoubleBuffer
的自定义控件,因此我不是PictureBox
,而是会做类似的事情:
public partial class CloudBox : UserControl
{
public CloudBox()
{
InitializeComponent();
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.UserPaint |
ControlStyles.ResizeRedraw, true);
}
private readonly List<Ellipse> _clouds = new List<Ellipse>();
public List<Ellipse> Clouds
{
get { return _clouds; }
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
foreach (var cloud in _clouds)
{
e.Graphics.FillEllipse(
cloud.Brush, cloud.Center.X, cloud.Center.Y,
cloud.Diameter, cloud.Diameter);
}
base.OnPaint(e);
}
}
我没有对此进行测试,但我相信您将能够填写详细信息。将Timer.Interval
设置为10或20毫秒应该会产生一个非常流畅的动画恕我直言。
[编辑] 要使用一些测试数据来实例化整个事物(我不知道你从哪里得到它),你可以在Form.Load
事件处理程序中使用这样的东西:
for (int i = 0; i < 400; i += 50)
for (int j = 0; j < 400; j += 50)
cloudBox.Clouds.Add(new Ellipse(i, j));