Build Colorful Command-Line Spinners in Nodejs

Build Colorful Command-Line Spinners in Nodejs

to color our spinners is very easy. We know that to color a text/data in terminal using Nodejs, there are color codes and data we need to write to produce a colored data. We have some colors here, the numbers in array on the right is the number code used to display the color.

In our last post “Build Spinners in Terminal using Nodejs”, we saw how we can implement spinners in terminal/command-line using Nodejs/JavaScript.

The spinners there were using the default terminal color. In this post, we will see how to make it colorful 🌈

Starting and Recap…

Our final code on spinners was this:


// index.js

const process = require("process")
const fs = require("fs")
const rdl = require("readline")
const l = console.log
const std = process.stdout
const spinners = JSON.parse(fs.readFileSync('./spinners.json').toString())

class Spinner {

    spin(spinnerName) {
        process.stdout.write("\x1B[?25l")
        const spin = spinners[spinnerName]
        const spinnerFrames = spin.frames
        const spinnerTimeInterval = spin.interval
        let index = 0
        this.timer = setInterval(() => {
            let now = spinnerFrames[index]
            if (now == undefined) {
                index = 0
                now = spinnerFrames[index]
            }
            std.write(now)
            rdl.cursorTo(std, 0, 0)
            index = index >= spinnerFrames.length ? 0 : index + 1

        }, spinnerTimeInterval)
    }
}

spin_recap.js

and the spinners.json file contained the following effects:


{
    "dots": {
        "interval": 80,
        "frames": [
            "⠋",
            "⠙",
            "⠹",
            "⠸",
            "⠼",
            "⠴",
            "⠦",
            "⠧",
            "⠇",
            "⠏"
        ]
    },
    "dots2": {
        "interval": 80,
        "frames": [
            "⣾",
            "⣽",
            "⣻",
            "⢿",
            "⡿",
            "⣟",
            "⣯",
            "⣷"
        ]
    },
    "line": {
        "interval": 130,
        "frames": [
            "-",
            "\\",
            "|",
            "/"
        ]
    },
    "arc": {
        "interval": 100,
        "frames": [
            "◜",
            "◠",
            "◝",
            "◞",
            "◡",
            "◟"
        ]
    },
}

spin_recap.json

Ramda components: Just collect and reuse components in your projects to build faster

The frames in each object, denotes each picture or frame to be displayed at a particular time. See each frame denotes a moment of rotation.

"frames": [
1.            "-",
2.            "\\",
3.            "|",
4.            "/"
        ]

See the frames for the line effect, the first frame 1. shows a moment of spin at a horizontal angle, 2. shows the left diagonal moment, 3. shows the vertical moment, 4. shows the right diagonal moment. Displaying all these four at a rate of 4 frames per 80ms, will show a spinning effect.

That's what the Spinner#spin method does, it collects the frames and the time interval from the spinners.json and uses the setInterval API to display the frames at frame rate of the time interval.

This goes with all effects in the spinners.json.

That’s enough on recap on what we did last time. Now, lets make them colorful.

Coloring the Spinners

To color our spinners is very easy. We know that to color a text/data in terminal using Nodejs, there are color codes and data we need to write to produce a colored data.

Every color has it color code:

"yellow": [33, 89],
"blue": [34, 89],
"green": [32, 89]

We have some colors here, the numbers in array on the right is the number code used to display the color. The first in the array is the background color code and the second is the foreground color code.

To write a data with any of the above colours fisrt we write \x1b[, the the background color code eg if yellow we put 33. Then, we add m, followed by the text/data we want colored, then we add \x1b with the foreground color code and last m.

So nnamdi in yellow will be:

\x1b[33mnnamdi\x1b[89m

Now, we have to modify our spin method to include the \xb[ to each spinner frame when it is to be displayed by std.write(now) code.

Also we will add an object of colors with their color codes, we will create a color method in the Spinner class, this will tell the Spinner what color we want displayed on our spinner.

Doing all above we will make our Spinner look like this:


// index.js
const process = require("process")
const fs = require("fs")
const rdl = require("readline")
const std = process.stdout
const colors = {
    "yellow": [33, 89],
    "blue": [34, 89],
    "green": [32, 89]
}

const spinners = JSON.parse(fs.readFileSync('./spinners.json').toString())


class Spinner {
    constructor() {
        this.timer = null
        this.colorTxt = {
            start: "",
            stop: ""
        }
    }

    spin(spinnerName) {
        process.stdout.write("\x1B[?25l")
        const spin = spinners[spinnerName]
        const spinnerFrames = spin.frames
        const spinnerTimeInterval = spin.interval
        let index = 0
        this.timer = setInterval(() => {
            let now = spinnerFrames[index]
            if (now == undefined) {
                index = 0
                now = spinnerFrames[index]
            }
            std.write(this.colorTxt.start + now + this.colorTxt.stop)
            rdl.cursorTo(std, 0, 0)
            index = index >= spinnerFrames.length ? 0 : index + 1
        }, spinnerTimeInterval)

    }

    color(colorName) {
        colorName = colors[colorName]
        this.setColor(colorName)
        return this
    }

    setColor(colorName) {
        this.colorTxt.start = "\x1b[" + colorName[0] + "m"
        this.colorTxt.stop = "\x1b[" + colorName[1] + "m\x1b[0m"
    }
}

spin_color.js

We added an object of colors colors, we added constructor to the Spinner class, the colorTxt object have properties start and stop. The start prop holds the background color code of the selected color and the stop prop holds the foreground color code.

In the spin method, it concatenates the start prop of colorTxt first with the current frame now and concatenates the result with the stop prop of colorTxt.

In the color method, it takes the name of the color in the colorName arg. It gets the color from the colors objects, then calls setColor method.

The setColor method, sets the colorTxt.start prop with the background code of the selected color, see it adds \x1b[ first then the color code and closes with m. It then sets the colorTxt.stop, it first adds the \x1b[ then the background code and ends with m with additional \x1b[0m. This code is a styling code that resets the color to the default terminal color, this is done so that the next data doesn't assume the color of the spinner.

The color method returns an instance of the Spinner which will enable us to chain method calls.

So to display the line effect with yellow color, we will do this:

new Spinner().color("yellow").spin("line")

Easy!!

It will display the line effect in yellow color:

This is image title

A blue dots effect will be this:

new Spinner().color("blue").spin("dots")

This is image title

A green dots2 effect will be this

new Spinner().color("green").spin("dots2")

This is image title

Making a Jacuzzi spinner

You know thw Jacuzzi type of lighting? It displays randoms colors: blue, red, yellow, green, etc at the same time.

We are going to make our spinner give that effect.

Now, we will modify our Spinner class to this:


const process = require("process")
const fs = require("fs")
const rdl = require("readline")
const std = process.stdout
const colors = {
    "yellow": [33, 89],
    "blue": [34, 89],
    "green": [32, 89]
}

function shuffle(arr) {
    let what;

    if (shuffle.lastIndex != undefined) {
        if (shuffle.lastIndex >= arr.length - 1) {
            shuffle.lastIndex = 0
            what = 0
        } else {
            what = shuffle.lastIndex + 1
            shuffle.lastIndex = shuffle.lastIndex + 1
        }
    } else {
        shuffle.lastIndex = 0
        return arr[0]
    }
    return arr[what]
}

const spinners = JSON.parse(fs.readFileSync('./spinners.json').toString())


class Spinner {
    constructor() {
        this.timer = null
        this.colorTxt = {
            start: "",
            stop: ""
        }
        this.random = false
    }

    spin(spinnerName) {
        process.stdout.write("\x1B[?25l")
        const spin = spinners[spinnerName]
        const spinnerFrames = spin.frames
        const spinnerTimeInterval = spin.interval
        let index = 0
        this.timer = setInterval(() => {
            let now = spinnerFrames[index]
            if (now == undefined) {
                index = 0
                now = spinnerFrames[index]
            }
            if (this.random) {
                this.randomise()
            }
            std.write(this.colorTxt.start + now + this.colorTxt.stop)
            rdl.cursorTo(std, 0, 0)
            index = index >= spinnerFrames.length ? 0 : index + 1
        }, spinnerTimeInterval)

    }

    randomise() {
        if (this.random != true) {
            this.random = true
            return this
        }
        const colorKeys = Object.keys(colors)
        const colorToSelect = shuffle(colorKeys)
        const color = colors[colorToSelect]
        this.setColor(color)
    }

    color(colorName) {
        colorName = colors[colorName]
        this.setColor(colorName)
        return this
    }

    setColor(colorName) {
        this.colorTxt.start = "\x1b[" + colorName[0] + "m"
        this.colorTxt.stop = "\x1b[" + colorName[1] + "m\x1b[0m"
    }
}

spin_random.js

We only added the randomise method and random flag in the constructor. Calling the randomise in the method call chain, will set the random flag to true, so the randomise method will be called every time the function callback in the setInterval is called. At every call, the randomise method will set a new color in the colorTxt.start and colorTxt.stop props before the std.write(this.colorTxt.start + now + this.colorTxt.stop) statement. This will make different colors display for each frame!! Thus, giving out the jacuzzi effect.

Let’s test it out.

line jacuzzi effect:

new Spinner().randomise().spin("line")

This is image title

dots jacuzzi effect:

new Spinner().randomise().spin("dots")

This is image title

dots2 jacuzzi effect:

new Spinner().randomise().spin("dots2")

This is image title

arc jacuzzi effect:

new Spinner().randomise().spin("arc")

This is image title

Code on Github

See the full code on Github.

Conclusion

We won!!! :) Adding color to our spinner kinda makes it fancy. Trust me, people like fancy and cool things.

It was very simple, adding color code to the frames did the trick and randomizing the colors again gave us the jacuzzi trick :)

If you have any questions regarding this or anything I should add, correct or remove, feel free to comment, email or DM me.

Thanks !!!

nodejs javascript programming

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Hire NodeJs Developer

Looking to build dynamic, extensively featured, and full-fledged web applications? **[Hire NodeJs Developer](https://hourlydeveloper.io/hire-dedicated-node-js-developer/ "Hire NodeJs Developer")** to create a real-time, faster, and scalable...

Learning JavaScript: Development Environments for JavaScript Programming

One of the nice things about learning JavaScript these days is that there is a plethora of choices for writing and running JavaScript code. In this article, I’m going to describe a few of these environments and show you the environment I’ll be using in this series of articles.

Learning JavaScript: Data Types and Variables

To paraphrase the title of an old computer science textbook, “Algorithms + Data = Programs.” The first step in learning a programming language such as JavaScript is to learn what types of data the language can work with. The second step is to learn how to store that data in variables. In this article I’ll discuss the different types of data you can work with in a JavaScript program and how to create and use variables to store and manipulate that data.

[ Professor JavaScript ]: Introduction

Professor JavaScript is a JavaScript online learning courses YouTube Channel. Students can learn how to develop codes with JavaScript from basic to advanced levels through the online courses in this YouTube channel.

From imperative to declarative JavaScript

In this post, I will explain why declarative code is better than imperative code. Then I will list some techniques to convert imperative JavaScript to a declarative one in common situations.