自分用の備忘録

VimとかJSとか。やったことのメモ。自分のため。

CLIでローディング出すやつ作ってみた

CLIでローディング出すやつ作ってみた話。
プログラム起動させてから、実際にアクションを起こす時間までの待機中が寂しかったので、「よく見るけど、実際どうやって作るん?」と思った次第。

結論、標準出力に\rをつけてあげて、上書きをしていけば良いらしい。

作る

ぐるぐるさせる

日付系は「chrono」を使います。
まずはぐるぐるさせるやつ。

[dependencies]
chrono = "0.4.23"
fn main() {
  let loading_markers = vec!["|", "/", "-", "\\"];  // ぐるぐるさせるやつ
  
  // タイマー終了の時間
  let endtime = Local.datetime_from_str("2023-01-11 09:00:00", "%Y-%m-%d %H:%M:%S")
    .unwrap()
    .timestamp_millis();
  let range = endtime - Local::now().timestamp_millis();
  
  loop {
    let diff = endtime - Local::now().timestamp_millis();
    // 100で割って、lengthとの余りを使う
    let marker = loading_markers[(diff / 100) as usize % loading_markers.len()];
    print!("{}\r", marker); 

    // 時間がきたらループを抜ける
    if diff <= 0 {
      break;
    }
  }
}

タイマー表示みたいなのをつける

fn main() {
  let loading_markers = vec!["|", "/", "-", "\\"];
  let mut progress = String::from("====================");  // 100%状態の文字

  let endtime = Local.datetime_from_str("2023-01-11 09:00:00", "%Y-%m-%d %H:%M:%S")
    .unwrap()
    .timestamp_millis();
  let range = endtime - Local::now().timestamp_millis();
  
  loop {
    let diff = endtime - Local::now().timestamp_millis();
    let marker = loading_markers[(diff / 100) as usize % loading_markers.len()];
    print!("{} [{}]\r", marker, progress);

    if diff <= 0 {
      break;
    } else {
      let percent = (diff as f64 / range as f64) * 100.0;
      let index = (20.0 * (percent / 100.0) - 1.0) as i64;
      // percentに見合うindexを "<"に置き換える
      progress.remove(index as usize);
      progress.insert(index as usize, '<');
      if (index + 1) < 20 {
        // 減ってなくなったところは空文字にする
        progress.remove((index + 1) as usize);
        progress.insert((index + 1) as usize, ' ');
      }
    }
  }
}

やってることは、let mut progress = String::from("====================");が初期状態を定義。
まずはこれを出力。
let index = (20.0 * (percent / 100.0) - 1.0) as i64;で、現在の位置を取得。
progressで定義している=が20文字なので、20.0です。
percentが50%なら、20.0 * (50.0 / 100.0) - 1.09.0。i64にキャストして9です。

progress.remove(index as usize)で、indexの文字を削除。
progress.insert(index as usize, '<')で、index<に書き換えます。
あと、今回は減っていくので、index + 1を空文字で置き換えておきます。

空文字を_にしています。
[====================] 初期状態
[==================<_] 1つ減った状態
[=================<__] 2つ減った状態

進捗度ならこの逆をやれば良いはず?

完成系

Webばっかりだと、この手のごにょごにょするやつのやり方を知らないことが多いのでは?
自分も全然知らない側なので「こうやってんのか」となりながらやってみました。

これで、あとどのくらいでアクションがトリガーされるかわかりやすくなりました。めでたしめでたし。

追記

// 点字
let loading_markers = vec!["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
// 円
let loading_markers = vec!["◜", "◝", "◞", "◟"];
// 四角
let loading_markers = vec!["⌜", "⌝", "⌟", "⌞"];
// 点線
let loading_markers = vec!["⋮", "⋰", "⋯", "⋱"];
// 点と棒
let loading_markers = vec!["꜈", "꜍", "꜎", "꜏", "꜐", "꜑", "꜌", "꜋", "꜊", "꜉"];

これにしたら、もうちょっとオシャレなやつできます。