2009-12-17 268 views
31

我只想知道如何创建简单的动画,如闪烁,移动C#控制台应用程序中的东西。有没有特别的方法呢?控制台动画

+0

+1喜欢这个问题,IMMD =) – 2009-12-17 17:41:16

回答

3

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

编辑:为灵感,Let's Dance

+1

我很想看到Lets Dance动画的框架作为文本,所以我们可以实现一个像Console Spinner这样的动画。 – TaterJuice 2017-03-10 21:35:49

0

我也不太相信,我正是你在说什么知道的。但我会给它一个镜头。 我认为导致“眨眼”影响(或我认为闪烁的影响)的最大和最好的方法是使用回车。 向您解释它的最佳方式是向您展示Foo Bar实验。 开始一个新的项目,并在你的主要功能,试试这个。

Console.WriteLine("Foo/nBar"); 

输出将看起来像这样

Foo 
Bar 

但是如果你使用一个回车。

Console.WriteLine("Foo/rBar"); 

输出将看起来像这样

Bar 

的原因是,富写入,然后回车带你回线的起点,然后将其棒被写入。你看到的只是酒吧。 这可能有助于在一行上“移动”事物,而不是在多行上再次重写相同的事物。 执行进程的一种方法是使用Console.Write(); 试试这个。

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

输出应该

Loading 

随之而来的是句号每秒10秒。

如果将Carriage return与Console.Write();函数,你可以在单行上写多个东西,清除行并写入其他东西,或者确实,只是略微移动了相同的东西。 (这当然需要比我向你展示的更多,比如记录你所控制的“对象”在哪里。如果你想要一个简短的例子,我会很乐意做一个,只是评论和问我:)

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

我希望这有助于

Skintkingle;)

+0

不幸的是,这并不适用于大多数情况。你不能用\ r上去,所以你非常有限,没有使用Console.SetCursorPosition – 2009-12-17 17:51:09

+0

当然是啊。 我不再在控制台上玩耍了,我忘记了最明显的东西。 :) – Skintkingle 2009-12-17 17:56:32

41

传统控制台微调:

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

刚看到这和一些其他线程关于它,喜欢这种东西!我拿了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); 
    } 
} 
1

我以为我会用我以前列出的代码版本。那就是:

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

再添延迟(不幸的是,通过Thread.Sleep()但是,嘿),能够循环动画向前和向后(启动时它击中年底反转),以及整体普遍改善。请享用!

1

这将是我的首选方法:

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

呼叫Spinner.Instance.Update()在控制台的当前位置开始微调。任何连续的调用都会将下一帧渲染到相同的位置。

如果您想写更多的文字,然后在新的位置添加一个新的微调控件,请致电Spinner.Reset()

8

这是我的微调。它的目的是为了有一个程序做了一些工作,而微调显示给用户的东西是实际发生的事情:

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

并使用类是这样的:

 var spinner = new Spinner(10, 10); 

    spinner.Start(); 

    // Do your work here instead of sleeping... 
    Thread.Sleep(10000); 

    spinner.Stop(); 
+0

真棒的例子,正是我想要的:虽然我正在做一个密集的任务,我想要一些进步 – jjay225 2016-04-20 18:37:14

+0

请看看我的anwser,我把你的代码,并修改了一下。 – Rumplin 2018-02-24 17:12:46

1

伟大的工作, 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); 
    } 
} 

实现:

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

输出:

enter image description here

0

我把anwser从@ThisGuy和修改它了一点,但我注意到,有时该行未被清除,因为它应该。

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

微调类:

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

我的控制台类:

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

为什么我有时会看到这样的结果?

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

看起来像Dispose没有触发?或者一个新的Task alredy开始了?

它应该是这样的:

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

UPDATE:

我加入ClearCurrentConsoleLine();,改变了Draw方法,这个固定的问题。

+0

我会认为它不是线程安全的,但如何解决这个问题? – Rumplin 2018-02-24 17:21:53

+0

前两次睡眠小于初始延迟(200 ms)。这就是为什么第三行是第一个显示消息的原因。你的睡眠在回调中发生,所以它们全部被并行启动。您正在打印当前控制台位置的状态,该位置在执行期间发生更改。 Hardcode left = 0,top = 5,你会看到你想要的输出。说了,我不太明白你在做什么。 – ThisGuy 2018-02-25 18:23:56

+0

从网络共享中读取文件。有了这个用户会看到发生了一些事情。它正在读取大量文件,并且需要一段时间来处理网络共享。这里的'Thread.Sleep'模拟这个。 – Rumplin 2018-02-26 19:19:51