The question is how do we make something like Material Design, Bootstrap progress-bars in a terminal. It seems impossible but it could be done.
In my previous article, we know that the terminal is divided into cells in an x-y plane like a graph with x and y-axis.
It starts with
(x, y) (0, 0) in a positive plane. The terminal by default starts at
0, 0 i.e the cursor is placed at
0, 0. The cursor current position determines where data will be written to next.
If we place the cursor at
(x, y) (9, 5), data will start from the (x, y) (9, 5) position. And this can be done with the
readline#cursorTo(stdout, int x: Number, int y: Number) in Nodejs.
Now, we have this knowledge.
To have a spinning line effect like this:
We know to spin a line, we start with this
-, then anticlockwise, we have
| and last
/. Then we start again from
/. Now if we can do the above pretty quickly we will have a spinning effect in our terminal.
- \ | /
So all we have to do is to write
/ in the same cell rapidly.
/ in an array. We will set up an index variable to hold the current index position in the array. Then we will set up a time interval with the
We will give a time of 100ms, this is to make it execute the function callback very fast to achieve the spinning effect. The function callback will retrieve the current line type from the array using the index we declared previously. Then we write the line type with the
process.stdout.write API. Using this API will make the cursor to advance to the next cell, no we don’t want that. So, we use the
readline#cursorTo API to set the cursor back to the previous cell, so on the next call, the next line type in the array will be written to the same cell.
So with all these executing at speed 100ms, we will see the spinning effect.
Scaffold a Node project, open the
index.js file and add the following code:
Run the file to see the effect:
Let’s go stylish. We can’t have the only effect
['-', '\', '|', '/']. We can make our Spinner class display different effects, to do that we will create a
This will hold different effects:
See this as an object that holds different effects. See each effect comes with its time interval. The frames property holds the sequence in which it will be displayed. See the
arc object has frames that when rendered at 100ms will produce an arc effect.
We have the dots objects, these dots objects display the dots effect when displayed rapidly at 80ms frame rate.
Now, we will refactor our
Spinner class to contain our new addition.
As we have
spinners.json, we use the
fs#fsReadSync API to pick up the json file and read the contents to the
spin method now has an argument
spinnerName that holds the name of the effect in the
spinners.json file. With the effect name, we extract from the spinners variable the effect and store the
interval values in
spinnerFrames will now be referenced from to get the next frame style and the
spinnerTimeInterval will be passed to
setInterval to set time interval for the function callback execution.
Now, to run the
Spinner, we create an instance and call the
spin method with the name of the effect we want.
// index.js ... new Spinner().spin("dots")
dots effect will be spinning.
// index.js ... new Spinner().spin("dots2")
dots2 effect will be shown.
// index.js ... new Spinner().spin("line")
This will show our prev
// index.js ... new Spinner().spin("arc")
This will show the arc effect.
You see, how easy it was. We used the setInterval API, process.stdout.write and readline#cursorTo and the style frames to produce the effect.
You can go forth and add the effects of your own. You can also write your implementation better than mine. I would like to see what you will come up with.
If you have any questions regarding this or anything I should add, correct or remove, feel free to comment, email or DM me.