简体   繁体   中英

How can i make on the pixels fade in/out like effect?

Today im doing the pixels in the paint event to blink. In form1 i have this code in a timer tick event that the interval is set to 1000ms.

private void timer1_Tick(object sender, EventArgs e)
        {
            CloudEnteringAlert.cloudColorIndex = (CloudEnteringAlert.cloudColorIndex + 1) % CloudEnteringAlert.cloudColors.Length;
            pictureBox1.Invalidate();
        }

In the CloudEntering class i have on the top:

public static Brush[] cloudColors = new[] { Brushes.Yellow, Brushes.Transparent };

Then in a paint method this paint method im calling from the form1 pictureBox1 paint event:

foreach (PointF pt in clouds)
                {
                    e.FillEllipse(cloudColors[cloudColorIndex], pt.X * (float)currentFactor, pt.Y * (float)currentFactor, 7f, 7f);
                }

So what i see now is one second the pixels are in yellow and one second the pixels are in Transparent.

Now what i want to do is:

  1. Each pixel will start from radius 5.

  2. Each pixel radius will get bigger to 25.

  3. The animation will be from the top to the bottom.

  4. Each pixel that got to radius 25 will start to get smaller back to radius 5.

  5. When the first pixel is started from 5 will get to radius 15 the next one will start to get bigger.

So if the first pixel is start at 5 now its 15 the next one will start to get bigger and the first one will continue to 25 when the first one is 25 it will get smaller. The second one when its 15 the third one will get bigger and so on.

All the pixels will start at radius 5 but only the first one will start get bigger get to 15 then the next one and when the first is 25 it will get smaller .

This is the time between each pixel.

And each pixel radius size change should take 300ms !

How can i do it ?

You want to start by encapsulating all information needed to render a single ellipse into a separate class. That would be something like:

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;
    }
}

The Ellipse.DiameterDelta property is the delta value which will be used for animation, and it can be positive (when going from diameter 5 to diameter 25 ), or negative (when going backwards). The value of this property (I've used 1 above) together with the your Timer.Interval will influence the speed of your animation.

A better OOP design would probably advocate moving animation-related properties out of this class, but for simplicity sake, it's better to start with this.

In your timer event, you might have something like:

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;
}

To get a smooth, non flickering animation, you will most likely have to use a custom control with ControlStyles.AllPaintingInWmPaint and ControlStyles.OptimizedDoubleBuffer set in its constructor, so instead of a PictureBox , I would do something like:

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);
    }
}

I haven't tested this, but I believe you'll be able to fill in the details. Setting Timer.Interval to 10 or 20 ms should yield a pretty fluid animation IMHO.

[Edit] To instantiate the whole thing with some test data (I don't know where you get it from), you could use something like this in your Form.Load event handler:

for (int i = 0; i < 400; i += 50)
    for (int j = 0; j < 400; j += 50)
        cloudBox.Clouds.Add(new Ellipse(i, j));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM