C# 控制台动画

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1923323/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 21:52:48  来源:igfitidea点击:

Console animations

c#

提问by Markus

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?

我只想知道如何在 C# 控制台应用程序上创建简单的动画,比如闪烁、移动东西。对此有什么特殊的方法吗?

回答by Rubens Farias

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

你需要使用Console.ForegroundColor,Console.BackgroundColorConsole.SetCursorPosition(int, int)

EDIT: For inspiration, Let's Dance

编辑:为了灵感,让我们跳舞

回答by Reed Copsey

Yes, there are quite a few methodsfor this.

是的,有很多方法可以做到这一点。

In particular, you may want to look at the following Console methods:

特别是,您可能需要查看以下控制台方法:

  1. SetCursorPosition(you can move the cursor around, and overwrite elements)
  2. MoveBufferArea(copy/paste over the top of regions)
  3. ForegroundColorand BackgroundColor(change coloring)
  1. SetCursorPosition(您可以四处移动光标,并覆盖元素)
  2. MoveBufferArea(在区域顶部复制/粘贴)
  3. ForegroundColorBackgroundColor(改变颜色)

回答by Skintkingle

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.

我不太确定我确切地知道你在说什么。但我会试一试。我认为引起“闪烁”影响(或者我认为闪烁影响是什么)的最大和最好的方法是使用回车。向您解释它的最好方法是向您展示 Foo Bar 实验。启动一个新项目,然后在您的 Main 函数中尝试此操作。

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.

原因是写了Foo,然后回车带你回到行首,然后写Bar。你所看到的只是酒吧。这对于在一行上“移动”事物很有帮助,而不是在多行上再次重写相同的事物。一种进行进度的方法是使用 Console.Write(); 尝试这个。

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.

接着是每秒一个句号,持续 10 秒。

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 :)

如果您将回车符与 Console.Write(); 结合使用;功能,您可以在一行上写多个内容,清除该行并写其他内容,或者实际上,同一件事只是稍微移动了一点。(这当然需要比我向您展示的更多,例如记录您正在控制的“对象”所在的位置。如果您想要一个简短的示例,我很乐意做一个,只需发表评论并询问我:)

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

编辑:我注意到人们提到了颜色,我忘记了。如果你在做动画,我想颜色是必须的。ForegroundColor 和 BackgroundColor 是它所在的位置。请注意, ForegroundColor 将应用于写入控制台的下一个字符,它不会完全重新着色控制台。/编辑

I hope this helps,

我希望这有帮助,

Skintkingle ;)

剥皮 ;)

回答by Skintkingle

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

回答by Chuck Claunch

Just saw this and a few other threads about it and love this kind of stuff! I took Tuukka's nice piece of code and improved it a bit so the class can easily be set to just about any spin sequence. I'll probably add some accessors and an overloaded constructor to polish it off and put it in the ol' toolbox. Fun stuff!

刚刚看到这个和其他一些关于它的线程并且喜欢这种东西!我采用了 Tuukka 的一段不错的代码并对其进行了一些改进,以便可以轻松地将类设置为几乎任何旋转序列。我可能会添加一些访问器和一个重载的构造函数来完善它并将其放入 ol' 工具箱中。好玩的东西!

class ConsoleSpinner
{
    int counter;
    string[] sequence;

    public ConsoleSpinner()
    {
        counter = 0;
        sequence = new string[] { "/", "-", "\", "|" };
        sequence = new string[] { ".", "o", "0", "o"};
        sequence = new string[] { "+", "x" };
        sequence = new string[] { "V", "<", "^", ">" };
        sequence = new string[] { ".   ", "..  ", "... ", "...." };
    }

    public void Turn()
    {
        counter++;

        if (counter >= sequence.Length)
            counter = 0;

        Console.Write(sequence[counter]);
        Console.SetCursorPosition(Console.CursorLeft - sequence[counter].Length, Console.CursorTop);
    }
}

回答by Gabriel Nahmias

I thought I'd chime in with my version of the previously listed code. Here it is:

我想我会加入我之前列出的代码版本。这里是:

class ConsoleSpinner
{
    bool increment = true,
         loop = false;
    int counter = 0;
    int delay;
    string[] sequence;

    public ConsoleSpinner(string sSequence = "dots", int iDelay = 100, bool bLoop = false)
    {
        delay = iDelay;
        if (sSequence == "dots")
        {
            sequence = new string[] { ".   ", "..  ", "... ", "...." };
            loop = true;
        }
        else if (sSequence == "slashes")
            sequence = new string[] { "/", "-", "\", "|" };
        else if (sSequence == "circles")
            sequence = new string[] { ".", "o", "0", "o" };
        else if (sSequence == "crosses")
            sequence = new string[] { "+", "x" };
        else if (sSequence == "arrows")
            sequence = new string[] { "V", "<", "^", ">" };
    }

    public void Turn()
    {
        if (loop)
        {
            if (counter >= sequence.Length - 1)
                increment = false;
            if (counter <= 0)
                increment = true;

            if (increment)
                counter++;
            else if (!increment)
                counter--;
        }
        else
        {
            counter++;

            if (counter >= sequence.Length)
                counter = 0;
        }

        Console.Write(sequence[counter]);
        Console.SetCursorPosition(Console.CursorLeft - sequence[counter].Length, Console.CursorTop);

        System.Threading.Thread.Sleep(delay);
    }
}

Adds delay (unfortunately through Thread.Sleep()but, hey), the ability to loop the animation forwards and backwards (starts to reverse when it hits the end), and general improvements overall. Enjoy!

添加延迟(不幸的是通过Thread.Sleep()但是,嘿),向前和向后循环动画的能力(当它结束时开始反转),以及总体改进。享受!

回答by Patrik Melander

This would be my prefered method:

这将是我的首选方法:

public sealed class Spinner
{
    private static Lazy<Spinner> lazy =
        new Lazy<Spinner>(()=> new Spinner());

    public static void Reset()
    {
        lazy = new Lazy<Spinner>(()=> new Spinner());
    }

    public static Spinner Instance { get { return lazy.Value; }}

    private readonly int _consoleX;
    private readonly int _consoleY;
    private readonly char[] _frames = { '|', '/', '-', '\' };
    private int _current;

    private Spinner()
    {
        _current = 0;
        _consoleX = Console.CursorLeft;
        _consoleY = Console.CursorTop;
    }

    public void Update()
    {
        Console.Write(_frames[_current]);
        Console.SetCursorPosition(_consoleX, _consoleY);

        if (++_current >= _frames.Length)
            _current = 0;
    }
}

Call Spinner.Instance.Update()to start the spinner at the current position of the console. Any consecutive call will render the next frame at the same position.

调用Spinner.Instance.Update()以在控制台的当前位置启动微调器。任何连续调用都将在同一位置渲染下一帧。

Call Spinner.Reset()if you want to write more text and then add a new spinner at a new location.

Spinner.Reset()如果您想编写更多文本,然后在新位置添加新微调器,请致电。

回答by ThisGuy

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

回答by Ozesh

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

ConsoleSpinner 和序列实现的出色工作。感谢您的代码。我考虑过分享我的定制方法。

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: "Loading ",sequenceCode:5);
}

Output:

输出:

enter image description here

enter image description here

回答by Rumplin

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

我从@ThisGuy 获取了 anwser 并对其进行了一些修改,但我注意到,有时该行未按应有的方式清除。

    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 Taskalredy started?

看起来 Dispose 没有触发?或者一个新的已经Task开始?

It should look like this:

它应该是这样的:

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

UPDATE:

更新:

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

我添加ClearCurrentConsoleLine();并更改了Draw方法,这解决了问题。