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
Console animations
提问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.BackgroundColor
and Console.SetCursorPosition(int, int)
你需要使用Console.ForegroundColor
,Console.BackgroundColor
和Console.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:
特别是,您可能需要查看以下控制台方法:
- SetCursorPosition(you can move the cursor around, and overwrite elements)
- MoveBufferArea(copy/paste over the top of regions)
- ForegroundColorand BackgroundColor(change coloring)
- SetCursorPosition(您可以四处移动光标,并覆盖元素)
- MoveBufferArea(在区域顶部复制/粘贴)
- ForegroundColor和BackgroundColor(改变颜色)
回答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:
输出:
回答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 Task
alredy 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 Draw
method, this fixed the issue.
我添加ClearCurrentConsoleLine();
并更改了Draw
方法,这解决了问题。