Today is the last day of 2021, tomorrow is 2022. Looking back over the past year, I want to give a special thanks to all of you for your support.

Life is only a few decades, each year is worth remembering and blessing, so I would like to use node.js console animation to send my New Year wishes:

On the last day of 2021, let’s learn some CLI techniques.

Realize the principle of

Animations require a refresh frame by frame, and console animations are no exception.

So how does the console refresh?

One of the console’s features, called TTY, is the ability to set colors and clear or modify content in a particular location. Most of the Terminal that we use is this.

Node.js uses process.stdout.isTTY to check if it is a standard output stream of type TTY, and provides readline to manipulate it.

Readline.cursorto (stream, x, y) to move the cursor, readline.clearline (stream) to clear a line, Use readline.clearScreenDown(stream) to clear everything after a location.

The ability to move the cursor, the ability to clear content, the ability to refresh, the ability to do arbitrary drawing, is fundamental to console animation.

Draw with readline.wrtie(data) to output the character, you can specify the character color (use the chalk package).

Just output colored characters? What about the picture and the word art?

In fact, that is also to do with characters, but given a different color, the console can only display characters.

The image on the left is displayed by taking the pixel information of the image and turning it into characters of different colors. You can use the console-png package.

The display principle of the art word on the right is to fix some character information and set the color. You use the cFonts package.

To summarize:

A TTY console can set colors, clear and modify content anywhere, which is the basis for console animation to refresh frame by frame, and Node.js provides the readline module to do this.

The console can only display characters, images can be taken pixel information and displayed with colored characters, art characters are prepared to draw the character array in advance, draw the content in different locations, and then refresh the frame at a time to form the console animation.

Now that we have the idea, let’s write code to implement it.

Code implementation

First, we’ll use the readline built-in module to do a frame refresh.

Call readline.createInterface to create an instance, specifying the input and output streams as stdin and stdout.

Stdin is the standard input stream, the keyboard.

Stdout is the standard output stream and refers to the display.

const readline = require('readline');

const outStream = process.stdout;

const rl = readline.createInterface({
    input: process.stdin,
    output: outStream
});
Copy the code

Then clear the entire console, move the cursor to Start, and clear:

readline.cursorTo(outStream, 0.0);
readline.clearScreenDown(outStream);
Copy the code

Then start drawing the text:

Prepare an array for the text to be drawn, and periodically display the text in different locations

const textArr = ['2021'.'thank you'.Everyone's.'support'.'2022'.'我们'.'together'.'come on! '];

(async function () {
    for(let i = 0; i< textArr.length; i++) { readline.cursorTo(outStream, ... randomPos()); rl.write(randomStyle(textArr[i]));await delay(1000);
        readline.cursorTo(outStream, 0.0);
        readline.clearScreenDown(outStream);
    }
})();

function delay(time) {
    return new Promise((resolve) = > setTimeout(resolve, time));
}

Copy the code

I’ve used async await to organize the code based on encapsulating a delay method.

Where the position and style are random:

const chalk = require('chalk');

function randomPos() {
    const x = Math.floor(30 * Math.random());
    const y = Math.floor(10 * Math.random());
    return [x, y];
}

function randomStyle(text) {
    const styles = ['redBright'.'yellowBright'.'blueBright'.'cyanBright'.'greenBright'.'magentaBright'.'whiteBright'];
    const color = styles[Math.floor(Math.random() * styles.length)];
    return chalk[color](text);
}
Copy the code

The previous text animation is complete:

Then the picture and the art word display:

Images need to be console-png to extract image pixel information into character form:

const consolePng = require('console-png');
consolePng.attachTo(console);

const image = fs.readFileSync(__dirname + '/headpic.png');
console.png(image);
Copy the code

Fonts are drawn with cfonts and displayed on the right after they are picked up:

const CFonts = require('cfonts');

const prettyFont = CFonts.render('|HAPPY|NEW YEAR', {
    font:'block'.colors: ['blue'.'yellow']});let startX = 60;
let startY = 0;
prettyFont.array.forEach((line, index) = > {
    readline.cursorTo(outStream, startX + index, startY + index);
    rl.write(line);
});
Copy the code

The vertical line in the first argument of cfont-render indicates a line break, the font in the second argument specifies the style, and the colors in the second argument.

The returned array is then displayed after the cursor is offset.

Finally, display the public account mark in the lower right:

readline.cursorTo(outStream, 120.25);
rl.write(chalk.yellowBright('-- Magic Light programming Secrets'));
Copy the code

Thus, the last frame is drawn:

And you’re done! Let’s look at the overall effect:

The code is uploaded to github: github.com/QuarkGluonP…

Post a copy here, too:

const readline = require('readline');
const chalk = require('chalk');
const CFonts = require('cfonts');
const consolePng = require('console-png');
const fs = require('fs');

consolePng.attachTo(console);

const outStream = process.stdout;

const rl = readline.createInterface({
    input: process.stdin,
    output: outStream
});

function delay(time) {
    return new Promise((resolve) = > setTimeout(resolve, time));
}

function randomStyle(text) {
    const styles = ['redBright'.'yellowBright'.'blueBright'.'cyanBright'.'greenBright'.'magentaBright'.'whiteBright'];
    const color = styles[Math.floor(Math.random() * styles.length)];
    return chalk[color](text);
}

function randomPos() {
    const x = Math.floor(30 * Math.random());
    const y = Math.floor(10 * Math.random());
    return [x, y];
}

readline.cursorTo(outStream, 0.0);
readline.clearScreenDown(outStream);
 
const image = fs.readFileSync(__dirname + '/headpic.png');

const textArr = ['2021'.'thank you'.Everyone's.'support'.'2022'.'我们'.'together'.'come on! '];

(async function () {
    for(let i = 0; i< textArr.length; i++) { readline.cursorTo(outStream, ... randomPos()); rl.write(randomStyle(textArr[i]));await delay(1000);
        readline.cursorTo(outStream, 0.0);
        readline.clearScreenDown(outStream);
    }

    console.png(image);

    await delay(1000);
    const prettyFont = CFonts.render('|HAPPY|NEW YEAR', {font:'block'.colors: ['blue'.'yellow']});

    let startX = 60;
    let startY = 0;
    prettyFont.array.forEach((line, index) = > {
        readline.cursorTo(outStream, startX + index, startY + index);
        rl.write(line);
    });

    readline.cursorTo(outStream, 120.25);
    rl.write(chalk.yellowBright('-- Magic Light programming Secrets')); }) ();Copy the code

conclusion

TTY terminals support setting character colors and clearing and modifying content anywhere, which is the basis for console animations that can be refreshed.

We display the picture by turning the pixel of the picture into colored characters, and display the art word through the preset character array. Drawing these contents in different positions can achieve rich display effects.

Among them, the console cursor position modification and content clearance using node.js built-in readline module, the rest is a third-party package. Use cFonts for art fonts, console-png for image display, and chalk for font color.

The console is what we use every day, and most of the tools on the front end are in the form of cli. In-depth knowledge of cli display and animation helps you better understand cli tools and write better CLI tools.

Finally, thank you again for your support in the past year. Let’s go for it next year