Console animations

前端 未结 11 1672
误落风尘
误落风尘 2020-12-04 10:15

I just want to know how to create simple animations like blinking, moving stuffs on C# console applications. Is there any special method for this?

相关标签:
11条回答
  • 2020-12-04 10:33

    Here is my spinner. The purpose of it is to have a program do some work while the spinner displays to the user that something is actually happening:

      public class Spinner : IDisposable
      {
         private const string Sequence = @"/-\|";
         private int counter = 0;
         private readonly int left;
         private readonly int top;
         private readonly int delay;
         private bool active;
         private readonly Thread thread;
    
         public Spinner(int left, int top, int delay = 100)
         {
            this.left = left;
            this.top = top;
            this.delay = delay;
            thread = new Thread(Spin);
         }
    
         public void Start()
         {
            active = true;
            if (!thread.IsAlive)
               thread.Start();
         }
    
         public void Stop()
         {
            active = false;
            Draw(' ');
         }
    
         private void Spin()
         {
            while (active)
            {
               Turn();
               Thread.Sleep(delay);
            }
         }
    
         private void Draw(char c)
         {
            Console.SetCursorPosition(left, top);
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write(c);
         }
    
         private void Turn()
         {
            Draw(Sequence[++counter % Sequence.Length]);
         }
    
         public void Dispose()
         {
            Stop();
         }
      }
    

    And you use the class like this:

         var spinner = new Spinner(10, 10);
    
         spinner.Start();
    
         // Do your work here instead of sleeping...
         Thread.Sleep(10000);
    
         spinner.Stop();
    
    0 讨论(0)
  • 2020-12-04 10:33

    Great work with the ConsoleSpinner and the sequence implementation. Thanks for the code. I thought about sharing my customized approach.

        public class ConsoleSpinner
        {
            static string[,] sequence = null;
    
            public int Delay { get; set; } = 200;
    
            int totalSequences = 0;
            int counter;
    
            public ConsoleSpinner()
            {
                counter = 0;
                sequence = new string[,] {
                    { "/", "-", "\\", "|" },
                    { ".", "o", "0", "o" },
                    { "+", "x","+","x" },
                    { "V", "<", "^", ">" },
                    { ".   ", "..  ", "... ", "...." },
                    { "=>   ", "==>  ", "===> ", "====>" },
                   // ADD YOUR OWN CREATIVE SEQUENCE HERE IF YOU LIKE
                };
    
                totalSequences = sequence.GetLength(0);
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="sequenceCode"> 0 | 1 | 2 |3 | 4 | 5 </param>
            public void Turn(string displayMsg = "", int sequenceCode = 0)
            {
                counter++;
                
                Thread.Sleep(Delay);
    
                sequenceCode = sequenceCode > totalSequences - 1 ? 0 : sequenceCode;
    
                int counterValue = counter % 4;
    
                string fullMessage = displayMsg + sequence[sequenceCode, counterValue];
                int msglength = fullMessage.Length;
    
                Console.Write(fullMessage);
    
                Console.SetCursorPosition(Console.CursorLeft - msglength, Console.CursorTop);
            }
        }
    

    Implementation:

        ConsoleSpinner spinner = new ConsoleSpinner();
        spinner.Delay = 300;
        while (true)
        {
            spinner.Turn(displayMsg: "Working ",sequenceCode:5);
        }
    

    Output:

    0 讨论(0)
  • 2020-12-04 10:33

    i'm not TOO sure i know exactly what you're on about. but i'll give it a shot. I think the biggest and best way to cause the "blinking" affect (or what i think the blinking affect is) is to use a carriage return. The best way to explain it to you is to show you the Foo Bar experiment. start a new project, and in your Main function, try this.

    Console.WriteLine("Foo/nBar");
    

    The output will look like this

    Foo
    Bar
    

    But if you use a carriage return.

    Console.WriteLine("Foo/rBar");
    

    The output will look like this

    Bar
    

    The reason is that Foo is written, then the carriage return takes you BACK to the start of the line, then Bar is written. All you ever see is Bar. This can be helpful for "Moving" things on one line, instead of rewriting the same things again on multiple lines. A way to do progression would be to use Console.Write(); Try this.

    Console.Write("Loading");
    for(int i = 0; i < 10; i++)
    {
        Thread.Sleep(1000);
        Console.Write(".");
    }
    

    The output should be

    Loading
    

    Followed by a Fullstop every second for 10 seconds.

    If you combine the Carriage return with the Console.Write(); function you can write multiple things on a single line, clear the line and write something else, or indeed, the same thing just moved slightly. (This would of course need more than i have shown you, like recording where the "object" you are controlling is situated. If you would like a short example i would be happy to do one, just comment and ask me for it :)

    Edit: I noticed people mentioning colour, which i forgot. If you were doing animation i guess colour would be a must. ForegroundColor and BackgroundColor are where it's at. note that ForegroundColor will apply to the next characters written to the console, it will not completely recolour the Console. /Edit

    I hope this helps,

    Skintkingle ;)

    0 讨论(0)
  • 2020-12-04 10:37

    Traditional Console Spinner:

        static void Main(string[] args)
        {
            ConsoleSpiner spin = new ConsoleSpiner();
            Console.Write("Working....");
            while (true) 
            {
                spin.Turn();
            }
        }
    
    public class ConsoleSpiner
    {
        int counter;
        public ConsoleSpiner()
        {
            counter = 0;
        }
        public void Turn()
        {
            counter++;        
            switch (counter % 4)
            {
                case 0: Console.Write("/"); break;
                case 1: Console.Write("-"); break;
                case 2: Console.Write("\\"); break;
                case 3: Console.Write("|"); break;
            }
            Console.SetCursorPosition(Console.CursorLeft - 1, Console.CursorTop);
        }
    }
    
    0 讨论(0)
  • 2020-12-04 10:38

    You'll need to use Console.ForegroundColor, Console.BackgroundColor and Console.SetCursorPosition(int, int)

    EDIT: For inspiration, Let's Dance

    0 讨论(0)
  • 2020-12-04 10:41

    I took the anwser from @ThisGuy and modified it a bit, but I noticed, that sometimes the row is not cleared as it should.

        static void Main(string[] args)
        {
            Console.WriteLine("1");
            var tddf = XConsole.BusyIndicator("test 0 trtg fdfdfvdgd 4343", () =>
            {
                return "";
            });
            Console.WriteLine("2");
            var tdd = XConsole.BusyIndicator("test 0 trtg fdfdfvdgd", () =>
            {
                Thread.Sleep(1);
                return "";
            });
            Console.WriteLine("3");
            var t = XConsole.BusyIndicator("test 1 trtg vdgd", () =>
            {
                Thread.Sleep(1000);
                return "";
            });
            Console.WriteLine("4");
            var xt = XConsole.BusyIndicator("test 2", () =>
            {
                Thread.Sleep(2000);
                return "";
            });
            var xtx = XConsole.BusyIndicator("test 2 csds fsd fdsf ds s", () =>
            {
                Thread.Sleep(2000);
                return "";
            });
    
            Console.WriteLine("5");
            Thread.Sleep(4000);
        }
    

    Spinner class:

        public class Spinner : IDisposable
        {
            private const string Sequence1 = @"/-\|";
            private const string Sequence3 = @".o0o";
            private const string Sequence2 = @"<^>v";
            private const string Sequence4 = @"#■.";
            private const string Sequence5 = @"▄▀";
            private const string Sequence = @"└┘┐┌";
            private string BusyMessage = "";
            private int counter = 0;
            private readonly int delay;
            private bool active;
            private readonly Thread thread;
    
            public Spinner(int delay = 200)
            {
                this.delay = delay;
                thread = new Thread(Spin);
            }
    
            public void Start()
            {
                active = true;
                Console.CursorVisible = false;
                if (!thread.IsAlive)
                {
                    thread.Start();
                }
            }
    
            public void Start(string busyMessage)
            {
                BusyMessage = busyMessage;
                Start();
            }
    
            public void Stop()
            {          
                active = false;
                Console.CursorVisible = true;
                ClearCurrentConsoleLine();
                BusyMessage = "";
            }
    
    private static void ClearCurrentConsoleLine()
    {
        int currentLineCursor = Console.CursorTop;
        Console.SetCursorPosition(0, Console.CursorTop);
        Console.Write(new string(' ', Console.WindowWidth));
        Console.SetCursorPosition(0, currentLineCursor);
    }
    
            private void Spin()
            {
                while (active)
                {
                    Turn();
                    Thread.Sleep(delay);
                }
            }
    
            /// <summary>
            /// Draws the busy indicator
            /// </summary>
            /// <param name="c">if empty char, then clear screen</param>
        private void Draw(char c)
        {
            int left = Console.CursorLeft;
            int top = Console.CursorTop;
    
            Console.Write('[');
            Console.ForegroundColor = ConsoleColor.Green;
            Console.Write(c);
            Console.ForegroundColor = ConsoleColor.Gray;
            Console.Write(']');
            if (!string.IsNullOrEmpty(BusyMessage))
            {
                Console.Write(" " + BusyMessage);
            }
    
            //reset cursor position
            Console.SetCursorPosition(left, top);
        }
    
            private void Turn()
            {
                Draw(Sequence[++counter % Sequence.Length]);
            }
    
            public void Dispose()
            {
                Stop();
            }
        }
    

    My console class:

    public static class XConsole {
        public static T BusyIndicator<T>(Func<T> action)
        {
            T result;
    
            using (var spinner = new Spinner())
            {
                spinner.Start();
    
                result = action();
    
                spinner.Stop();
            }
    
            return result;
        }
    
        public static T BusyIndicator<T>(string content, Func<T> action)
        {
            T result;
    
            using (var spinner = new Spinner())
            {
                spinner.Start(content);
    
                result = action();
    
                spinner.Stop();
            }
    
            return result;
        }
    }
    

    Why do I see this result sometimes?

    1
    2
    3   st 0 trtg fdfdfvdgd    ]
    4
    [└] test 2 csds fsd fdsf ds s
    

    Looks like the Dispose didn't trigger? Or a new Task alredy started?

    It should look like this:

    1
    2
    3
    4
    [└] test 2 csds fsd fdsf ds s
    

    UPDATE:

    I added ClearCurrentConsoleLine(); and changed the Draw method, this fixed the issue.

    0 讨论(0)
提交回复
热议问题