如何使像素淡入/淡出效果?

时间:2014-01-10 13:01:02

标签: c# winforms

今天我正在将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);
                }

所以我现在看到的是一秒钟内像素为黄色,一秒钟内像素为透明。

现在我想做的是:

  1. 每个像素将从半径5开始。

  2. 每个像素半径将变大到25。

  3. 动画将从上到下。

  4. 达到半径25的每个像素将开始变小到半径为5.

  5. 当第一个像素从5开始到达半径15时,下一个像素将开始变大。

  6. 因此,如果第一个像素现在从5开始,那么下一个像素将开始变大,第一个像素将继续到25,当第一个像素为25时,它将变小。第二个是第15个,第三个将变大,等等。

    所有像素都将从半径5开始,但只有第一个像素将开始变大,然后变为15,然后是下一个像素,当第一个像素变为25时,变小。

    这是每个像素之间的时间。

    每个像素半径大小的变化应该需要300毫秒!

    我该怎么做?

1 个答案:

答案 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.AllPaintingInWmPaintControlStyles.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));