@figliolia/chalk-animation 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,146 @@
1
+ # chalk-animation
2
+
3
+ [![Build Status](https://flat.badgen.net/github/checks/bokub/chalk-animation?label=tests)](https://github.com/bokub/chalk-animation/actions/workflows/run.yml)
4
+ [![Version](https://runkit.io/bokub/npm-version/branches/master/chalk-animation?style=flat)](https://www.npmjs.com/package/chalk-animation)
5
+ [![Codecov](https://flat.badgen.net/codecov/c/github/bokub/chalk-animation)](https://codecov.io/gh/bokub/chalk-animation)
6
+ [![Downloads](https://flat.badgen.net/npm/dm/chalk-animation?color=FF9800)](https://www.npmjs.com/package/chalk-animation)
7
+ [![XO code style](https://flat.badgen.net/badge/code%20style/XO/5ed9c7)](https://github.com/sindresorhus/xo)
8
+
9
+ > Colorful animations in terminal output
10
+
11
+
12
+ ## Available animations
13
+
14
+ | Name | Preview |
15
+ |:---------:|:------------------------------------------:|
16
+ | rainbow | ![rainbow](http://i.imgur.com/napdxdn.gif) |
17
+ | pulse | ![pulse](http://i.imgur.com/xdaETwr.gif) |
18
+ | glitch | ![glitch](http://i.imgur.com/834FJU1.gif) |
19
+ | radar | ![radar](http://i.imgur.com/3bFrtRc.gif) |
20
+ | neon | ![neon](http://i.imgur.com/YdAAroI.gif) |
21
+ | karaoke | ![karaoke](https://i.imgur.com/lG7EF1t.gif)|
22
+
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ $ npm i chalk-animation
28
+ ```
29
+
30
+
31
+ ## Usage
32
+
33
+ ```javascript
34
+ import chalkAnimation from 'chalk-animation';
35
+
36
+ chalkAnimation.rainbow('Lorem ipsum dolor sit amet');
37
+ ```
38
+
39
+ #### Start and stop
40
+
41
+ You can stop and resume an animation with `stop()` and `start()`.
42
+
43
+ When created, the instance of chalkAnimation **starts automatically**.
44
+
45
+ ```javascript
46
+ const rainbow = chalkAnimation.rainbow('Lorem ipsum'); // Animation starts
47
+
48
+ setTimeout(() => {
49
+ rainbow.stop(); // Animation stops
50
+ }, 1000);
51
+
52
+ setTimeout(() => {
53
+ rainbow.start(); // Animation resumes
54
+ }, 2000);
55
+
56
+ ```
57
+
58
+ #### Automatic stop
59
+
60
+ Anything printed to the console will stop the previous animation automatically
61
+
62
+ ```javascript
63
+ chalkAnimation.rainbow('Lorem ipsum');
64
+ setTimeout(() => {
65
+ // Stop the 'Lorem ipsum' animation, then write on a new line.
66
+ console.log('dolor sit amet');
67
+ }, 1000);
68
+ ```
69
+
70
+ #### Changing speed
71
+
72
+ Change the animation speed using a second parameter. Should be greater than 0, default is 1.
73
+
74
+ ```javascript
75
+ chalkAnimation.rainbow('Lorem ipsum', 2); // Two times faster than default
76
+ ```
77
+
78
+ #### Changing text
79
+
80
+ Change the animated text seamlessly with `replace()`
81
+
82
+ ```javascript
83
+ let str = 'Loading...';
84
+ const rainbow = chalkAnimation.rainbow(str);
85
+
86
+ // Add a new dot every second
87
+ setInterval(() => {
88
+ rainbow.replace(str += '.');
89
+ }, 1000);
90
+ ```
91
+
92
+ #### Manual rendering
93
+
94
+ Manually render frames with `render()`, or get the content of the next frame with `frame()`
95
+
96
+ ```javascript
97
+ const rainbow = chalkAnimation.rainbow('Lorem ipsum').stop(); // Don't start the animation
98
+
99
+ rainbow.render(); // Display the first frame
100
+
101
+ const frame = rainbow.frame(); // Get the second frame
102
+ console.log(frame);
103
+ ```
104
+
105
+
106
+ ## CLI mode
107
+
108
+ ```bash
109
+ # Install package globally
110
+ $ npm install --global chalk-animation
111
+ ```
112
+
113
+ ```
114
+ $ chalk-animation --help
115
+
116
+ Colorful animations in terminal output
117
+
118
+ Usage
119
+ $ chalk-animation <name> [options] [text...]
120
+
121
+ Options
122
+ --duration Duration of the animation in ms, defaults to Infinity
123
+ --speed Animation speed as number > 0, defaults to 1
124
+
125
+ Available animations
126
+ rainbow
127
+ pulse
128
+ glitch
129
+ radar
130
+ neon
131
+ karaoke
132
+
133
+ Example
134
+ $ chalk-animation rainbow Hello world!
135
+ ```
136
+
137
+
138
+ ## Related
139
+
140
+ - [gradient-string](https://github.com/bokub/gradient-string) - Output gradients to terminal
141
+ - [chalk](https://github.com/chalk/chalk) - Output colored text to terminal
142
+
143
+
144
+ ## License
145
+
146
+ MIT © [Boris K](https://github.com/bokub)
@@ -0,0 +1,20 @@
1
+ import type { AnimationFN } from "./types";
2
+ import { InstanceTracker } from "./InstanceTracker";
3
+ export declare class Animation extends InstanceTracker {
4
+ effect: any;
5
+ lines: number;
6
+ speed: number;
7
+ delay: number;
8
+ text: string[];
9
+ stopped: boolean;
10
+ currentFrame: number;
11
+ initialized: boolean;
12
+ controller: ReturnType<typeof setTimeout> | null;
13
+ constructor(effect: AnimationFN, str: string, delay?: number, speed?: number);
14
+ render(): void;
15
+ frame(): string;
16
+ replace(str: string): this;
17
+ stop(): this;
18
+ start(): this;
19
+ private clearFrames;
20
+ }
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Animation = void 0;
4
+ const InstanceTracker_1 = require("./InstanceTracker");
5
+ class Animation extends InstanceTracker_1.InstanceTracker {
6
+ constructor(effect, str, delay = 0, speed = 0) {
7
+ super();
8
+ this.stopped = false;
9
+ this.currentFrame = 0;
10
+ this.initialized = false;
11
+ this.controller = null;
12
+ this.effect = effect;
13
+ this.speed = speed;
14
+ this.delay = delay;
15
+ this.text = str.split(/\r\n|\r|\n/);
16
+ this.lines = this.text.length;
17
+ }
18
+ render() {
19
+ if (!this.initialized) {
20
+ Animation.LOG("\n".repeat(this.lines - 1));
21
+ this.initialized = true;
22
+ }
23
+ Animation.LOG(this.frame());
24
+ this.controller = setTimeout(() => {
25
+ if (!this.stopped) {
26
+ this.render();
27
+ }
28
+ }, this.delay / this.speed);
29
+ }
30
+ frame() {
31
+ this.currentFrame++;
32
+ return ("\u001B[" +
33
+ `${this.lines}` +
34
+ "F\u001B[G\u001B[2K" +
35
+ this.text.map((str) => this.effect(str, this.currentFrame)).join("\n"));
36
+ }
37
+ replace(str) {
38
+ this.text = str.split(/\r\n|\r|\n/);
39
+ this.lines = this.text.length;
40
+ return this;
41
+ }
42
+ stop() {
43
+ this.clearFrames();
44
+ this.stopped = true;
45
+ return this;
46
+ }
47
+ start() {
48
+ this.clearFrames();
49
+ this.stopped = false;
50
+ this.render();
51
+ return this;
52
+ }
53
+ clearFrames() {
54
+ if (this.controller) {
55
+ clearTimeout(this.controller);
56
+ this.controller = null;
57
+ }
58
+ }
59
+ }
60
+ exports.Animation = Animation;
@@ -0,0 +1,10 @@
1
+ import { Animation } from "./Animation";
2
+ export declare class ChalkAnimation {
3
+ static rainbow(str: string, speed?: number): Animation;
4
+ static pulse(str: string, speed?: number): Animation;
5
+ static glitch(str: string, speed?: number): Animation;
6
+ static radar(str: string, speed?: number): Animation;
7
+ static neon(str: string, speed?: number): Animation;
8
+ static karaoke(str: string, speed?: number): Animation;
9
+ static clearAllAnimations(): void;
10
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChalkAnimation = void 0;
4
+ const Effects_1 = require("./Effects");
5
+ const Animation_1 = require("./Animation");
6
+ const animateString_1 = require("./animateString");
7
+ class ChalkAnimation {
8
+ static rainbow(str, speed = 1) {
9
+ return (0, animateString_1.animateString)(Effects_1.Effects.rainbow, str, 15, speed);
10
+ }
11
+ static pulse(str, speed = 1) {
12
+ return (0, animateString_1.animateString)(Effects_1.Effects.pulse, str, 16, speed);
13
+ }
14
+ static glitch(str, speed = 1) {
15
+ return (0, animateString_1.animateString)(Effects_1.Effects.glitch, str, 55, speed);
16
+ }
17
+ static radar(str, speed = 1) {
18
+ return (0, animateString_1.animateString)(Effects_1.Effects.radar, str, 50, speed);
19
+ }
20
+ static neon(str, speed = 1) {
21
+ return (0, animateString_1.animateString)(Effects_1.Effects.neon, str, 500, speed);
22
+ }
23
+ static karaoke(str, speed = 1) {
24
+ return (0, animateString_1.animateString)(Effects_1.Effects.karaoke, str, 50, speed);
25
+ }
26
+ static clearAllAnimations() {
27
+ return Animation_1.Animation.clearAll();
28
+ }
29
+ }
30
+ exports.ChalkAnimation = ChalkAnimation;
@@ -0,0 +1,13 @@
1
+ export declare class Effects {
2
+ static longHsv: {
3
+ interpolation: string;
4
+ hsvSpin: string;
5
+ };
6
+ static glitchChars: string;
7
+ static rainbow(str: string, frame: number): string;
8
+ static pulse(str: string, frame: number): string;
9
+ static glitch(str: string, frame: number): string;
10
+ static radar(str: string, frame: number): string;
11
+ static neon(str: string, frame: number): string;
12
+ static karaoke(str: string, frame: number): string;
13
+ }
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.Effects = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const gradient_string_1 = __importDefault(require("gradient-string"));
9
+ class Effects {
10
+ static rainbow(str, frame) {
11
+ const hue = 5 * frame;
12
+ const leftColor = { h: hue % 360, s: 1, v: 1 };
13
+ const rightColor = { h: (hue + 1) % 360, s: 1, v: 1 };
14
+ return (0, gradient_string_1.default)(leftColor, rightColor)(str, Effects.longHsv);
15
+ }
16
+ static pulse(str, frame) {
17
+ frame = (frame % 120) + 1;
18
+ const transition = 6;
19
+ const duration = 10;
20
+ const on = "#ff1010";
21
+ const off = "#e6e6e6";
22
+ if (frame >= 2 * transition + duration) {
23
+ return chalk_1.default.hex(off)(str); // All white
24
+ }
25
+ if (frame >= transition && frame <= transition + duration) {
26
+ return chalk_1.default.hex(on)(str); // All red
27
+ }
28
+ frame =
29
+ frame >= transition + duration
30
+ ? 2 * transition + duration - frame
31
+ : frame; // Revert animation
32
+ const g = frame <= transition / 2
33
+ ? (0, gradient_string_1.default)([
34
+ { color: off, pos: 0.5 - frame / transition },
35
+ { color: on, pos: 0.5 },
36
+ { color: off, pos: 0.5 + frame / transition },
37
+ ])
38
+ : (0, gradient_string_1.default)([
39
+ { color: off, pos: 0 },
40
+ { color: on, pos: 1 - frame / transition },
41
+ { color: on, pos: frame / transition },
42
+ { color: off, pos: 1 },
43
+ ]);
44
+ return g(str);
45
+ }
46
+ static glitch(str, frame) {
47
+ if ((frame % 2) + (frame % 3) + (frame % 11) + (frame % 29) + (frame % 37) >
48
+ 52) {
49
+ return str.replace(/[^\r\n]/g, " ");
50
+ }
51
+ const chunkSize = Math.max(3, Math.round(str.length * 0.02));
52
+ const chunks = [];
53
+ for (let i = 0, length = str.length; i < length; i++) {
54
+ const skip = Math.round(Math.max(0, (Math.random() - 0.8) * chunkSize));
55
+ chunks.push(str.substring(i, i + skip).replace(/[^\r\n]/g, " "));
56
+ i += skip;
57
+ if (str[i]) {
58
+ if (str[i] !== "\n" && str[i] !== "\r" && Math.random() > 0.995) {
59
+ chunks.push(Effects.glitchChars[Math.floor(Math.random() * Effects.glitchChars.length)]);
60
+ }
61
+ else if (Math.random() > 0.005) {
62
+ chunks.push(str[i]);
63
+ }
64
+ }
65
+ }
66
+ let result = chunks.join("");
67
+ if (Math.random() > 0.99) {
68
+ result = result.toUpperCase();
69
+ }
70
+ else if (Math.random() < 0.01) {
71
+ result = result.toLowerCase();
72
+ }
73
+ return result;
74
+ }
75
+ static radar(str, frame) {
76
+ const depth = Math.floor(Math.min(str.length, str.length * 0.2));
77
+ const step = Math.floor(255 / depth);
78
+ const globalPos = frame % (str.length + depth);
79
+ const chars = [];
80
+ for (let i = 0, length = str.length; i < length; i++) {
81
+ const pos = -(i - globalPos);
82
+ if (pos > 0 && pos <= depth - 1) {
83
+ const shade = (depth - pos) * step;
84
+ chars.push(chalk_1.default.rgb(shade, shade, shade)(str[i]));
85
+ }
86
+ else {
87
+ chars.push(" ");
88
+ }
89
+ }
90
+ return chars.join("");
91
+ }
92
+ static neon(str, frame) {
93
+ const color = frame % 2 === 0
94
+ ? chalk_1.default.dim.rgb(88, 80, 85)
95
+ : chalk_1.default.bold.rgb(213, 70, 242);
96
+ return color(str);
97
+ }
98
+ static karaoke(str, frame) {
99
+ const chars = (frame % (str.length + 20)) - 10;
100
+ if (chars < 0) {
101
+ return chalk_1.default.white(str);
102
+ }
103
+ return (chalk_1.default.rgb(255, 187, 0).bold(str.substr(0, chars)) +
104
+ chalk_1.default.white(str.substr(chars)));
105
+ }
106
+ }
107
+ exports.Effects = Effects;
108
+ Effects.longHsv = { interpolation: "hsv", hsvSpin: "long" };
109
+ Effects.glitchChars = "x*0987654321[]0-~@#(____!!!!\\|?????....0000\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
@@ -0,0 +1,14 @@
1
+ import { AutoIncrementingID } from "@figliolia/event-emitter";
2
+ import type { Animation } from "./Animation";
3
+ export declare class InstanceTracker {
4
+ ID: string;
5
+ static readonly LOG: {
6
+ (...data: any[]): void;
7
+ (message?: any, ...optionalParams: any[]): void;
8
+ };
9
+ static IDs: AutoIncrementingID;
10
+ static bucket: Map<string, Animation>;
11
+ constructor();
12
+ get instance(): Animation;
13
+ static clearAll(): void;
14
+ }
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InstanceTracker = void 0;
4
+ const event_emitter_1 = require("@figliolia/event-emitter");
5
+ class InstanceTracker {
6
+ constructor() {
7
+ InstanceTracker.clearAll();
8
+ this.ID = InstanceTracker.IDs.get();
9
+ InstanceTracker.bucket.set(this.ID, this.instance);
10
+ }
11
+ get instance() {
12
+ return this;
13
+ }
14
+ static clearAll() {
15
+ for (const [, animation] of InstanceTracker.bucket) {
16
+ animation.stop();
17
+ }
18
+ InstanceTracker.bucket.clear();
19
+ }
20
+ }
21
+ exports.InstanceTracker = InstanceTracker;
22
+ InstanceTracker.LOG = console.log;
23
+ InstanceTracker.IDs = new event_emitter_1.AutoIncrementingID();
24
+ InstanceTracker.bucket = new Map();
25
+ (() => {
26
+ const methods = ["log", "warn", "info", "error"];
27
+ methods.forEach((method) => {
28
+ const original = console[method];
29
+ console[method] = function (...args) {
30
+ InstanceTracker.clearAll();
31
+ return original.apply(console, args);
32
+ };
33
+ });
34
+ })();
@@ -0,0 +1,2 @@
1
+ import { Animation } from "./Animation";
2
+ export declare function animateString(effect: any, str: string, delay?: number, speed?: number): Animation;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.animateString = void 0;
4
+ const Animation_1 = require("./Animation");
5
+ function animateString(effect, str, delay = 0, speed = 0) {
6
+ if (!speed || speed <= 0) {
7
+ throw new Error("Expected `speed` to be an number greater than 0");
8
+ }
9
+ const animation = new Animation_1.Animation(effect, str, delay, speed);
10
+ // setTimeout(() => {
11
+ // if (!animation.stopped) {
12
+ // animation.start();
13
+ // }
14
+ // }, delay / speed);
15
+ animation.start();
16
+ return animation;
17
+ }
18
+ exports.animateString = animateString;
@@ -0,0 +1,4 @@
1
+ export { ChalkAnimation } from "./ChalkAnimation";
2
+ export type { Animation } from "./Animation";
3
+ export type { InstanceTracker } from "./InstanceTracker";
4
+ export * from "./types";
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ChalkAnimation = void 0;
18
+ var ChalkAnimation_1 = require("./ChalkAnimation");
19
+ Object.defineProperty(exports, "ChalkAnimation", { enumerable: true, get: function () { return ChalkAnimation_1.ChalkAnimation; } });
20
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1 @@
1
+ export type AnimationFN = (str: string, frame: number) => string;
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,20 @@
1
+ import type { AnimationFN } from "./types";
2
+ import { InstanceTracker } from "./InstanceTracker";
3
+ export declare class Animation extends InstanceTracker {
4
+ effect: any;
5
+ lines: number;
6
+ speed: number;
7
+ delay: number;
8
+ text: string[];
9
+ stopped: boolean;
10
+ currentFrame: number;
11
+ initialized: boolean;
12
+ controller: ReturnType<typeof setTimeout> | null;
13
+ constructor(effect: AnimationFN, str: string, delay?: number, speed?: number);
14
+ render(): void;
15
+ frame(): string;
16
+ replace(str: string): this;
17
+ stop(): this;
18
+ start(): this;
19
+ private clearFrames;
20
+ }
@@ -0,0 +1,61 @@
1
+ import { InstanceTracker } from "./InstanceTracker";
2
+ export class Animation extends InstanceTracker {
3
+ effect;
4
+ lines;
5
+ speed;
6
+ delay;
7
+ text;
8
+ stopped = false;
9
+ currentFrame = 0;
10
+ initialized = false;
11
+ controller = null;
12
+ constructor(effect, str, delay = 0, speed = 0) {
13
+ super();
14
+ this.effect = effect;
15
+ this.speed = speed;
16
+ this.delay = delay;
17
+ this.text = str.split(/\r\n|\r|\n/);
18
+ this.lines = this.text.length;
19
+ }
20
+ render() {
21
+ if (!this.initialized) {
22
+ Animation.LOG("\n".repeat(this.lines - 1));
23
+ this.initialized = true;
24
+ }
25
+ Animation.LOG(this.frame());
26
+ this.controller = setTimeout(() => {
27
+ if (!this.stopped) {
28
+ this.render();
29
+ }
30
+ }, this.delay / this.speed);
31
+ }
32
+ frame() {
33
+ this.currentFrame++;
34
+ return ("\u001B[" +
35
+ `${this.lines}` +
36
+ "F\u001B[G\u001B[2K" +
37
+ this.text.map((str) => this.effect(str, this.currentFrame)).join("\n"));
38
+ }
39
+ replace(str) {
40
+ this.text = str.split(/\r\n|\r|\n/);
41
+ this.lines = this.text.length;
42
+ return this;
43
+ }
44
+ stop() {
45
+ this.clearFrames();
46
+ this.stopped = true;
47
+ return this;
48
+ }
49
+ start() {
50
+ this.clearFrames();
51
+ this.stopped = false;
52
+ this.render();
53
+ return this;
54
+ }
55
+ clearFrames() {
56
+ if (this.controller) {
57
+ clearTimeout(this.controller);
58
+ this.controller = null;
59
+ }
60
+ }
61
+ }
@@ -0,0 +1,10 @@
1
+ import { Animation } from "./Animation";
2
+ export declare class ChalkAnimation {
3
+ static rainbow(str: string, speed?: number): Animation;
4
+ static pulse(str: string, speed?: number): Animation;
5
+ static glitch(str: string, speed?: number): Animation;
6
+ static radar(str: string, speed?: number): Animation;
7
+ static neon(str: string, speed?: number): Animation;
8
+ static karaoke(str: string, speed?: number): Animation;
9
+ static clearAllAnimations(): void;
10
+ }
@@ -0,0 +1,26 @@
1
+ import { Effects } from "./Effects";
2
+ import { Animation } from "./Animation";
3
+ import { animateString } from "./animateString";
4
+ export class ChalkAnimation {
5
+ static rainbow(str, speed = 1) {
6
+ return animateString(Effects.rainbow, str, 15, speed);
7
+ }
8
+ static pulse(str, speed = 1) {
9
+ return animateString(Effects.pulse, str, 16, speed);
10
+ }
11
+ static glitch(str, speed = 1) {
12
+ return animateString(Effects.glitch, str, 55, speed);
13
+ }
14
+ static radar(str, speed = 1) {
15
+ return animateString(Effects.radar, str, 50, speed);
16
+ }
17
+ static neon(str, speed = 1) {
18
+ return animateString(Effects.neon, str, 500, speed);
19
+ }
20
+ static karaoke(str, speed = 1) {
21
+ return animateString(Effects.karaoke, str, 50, speed);
22
+ }
23
+ static clearAllAnimations() {
24
+ return Animation.clearAll();
25
+ }
26
+ }
@@ -0,0 +1,13 @@
1
+ export declare class Effects {
2
+ static longHsv: {
3
+ interpolation: string;
4
+ hsvSpin: string;
5
+ };
6
+ static glitchChars: string;
7
+ static rainbow(str: string, frame: number): string;
8
+ static pulse(str: string, frame: number): string;
9
+ static glitch(str: string, frame: number): string;
10
+ static radar(str: string, frame: number): string;
11
+ static neon(str: string, frame: number): string;
12
+ static karaoke(str: string, frame: number): string;
13
+ }
@@ -0,0 +1,102 @@
1
+ import chalk from "chalk";
2
+ import gradient from "gradient-string";
3
+ export class Effects {
4
+ static longHsv = { interpolation: "hsv", hsvSpin: "long" };
5
+ static glitchChars = "x*0987654321[]0-~@#(____!!!!\\|?????....0000\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
6
+ static rainbow(str, frame) {
7
+ const hue = 5 * frame;
8
+ const leftColor = { h: hue % 360, s: 1, v: 1 };
9
+ const rightColor = { h: (hue + 1) % 360, s: 1, v: 1 };
10
+ return gradient(leftColor, rightColor)(str, Effects.longHsv);
11
+ }
12
+ static pulse(str, frame) {
13
+ frame = (frame % 120) + 1;
14
+ const transition = 6;
15
+ const duration = 10;
16
+ const on = "#ff1010";
17
+ const off = "#e6e6e6";
18
+ if (frame >= 2 * transition + duration) {
19
+ return chalk.hex(off)(str); // All white
20
+ }
21
+ if (frame >= transition && frame <= transition + duration) {
22
+ return chalk.hex(on)(str); // All red
23
+ }
24
+ frame =
25
+ frame >= transition + duration
26
+ ? 2 * transition + duration - frame
27
+ : frame; // Revert animation
28
+ const g = frame <= transition / 2
29
+ ? gradient([
30
+ { color: off, pos: 0.5 - frame / transition },
31
+ { color: on, pos: 0.5 },
32
+ { color: off, pos: 0.5 + frame / transition },
33
+ ])
34
+ : gradient([
35
+ { color: off, pos: 0 },
36
+ { color: on, pos: 1 - frame / transition },
37
+ { color: on, pos: frame / transition },
38
+ { color: off, pos: 1 },
39
+ ]);
40
+ return g(str);
41
+ }
42
+ static glitch(str, frame) {
43
+ if ((frame % 2) + (frame % 3) + (frame % 11) + (frame % 29) + (frame % 37) >
44
+ 52) {
45
+ return str.replace(/[^\r\n]/g, " ");
46
+ }
47
+ const chunkSize = Math.max(3, Math.round(str.length * 0.02));
48
+ const chunks = [];
49
+ for (let i = 0, length = str.length; i < length; i++) {
50
+ const skip = Math.round(Math.max(0, (Math.random() - 0.8) * chunkSize));
51
+ chunks.push(str.substring(i, i + skip).replace(/[^\r\n]/g, " "));
52
+ i += skip;
53
+ if (str[i]) {
54
+ if (str[i] !== "\n" && str[i] !== "\r" && Math.random() > 0.995) {
55
+ chunks.push(Effects.glitchChars[Math.floor(Math.random() * Effects.glitchChars.length)]);
56
+ }
57
+ else if (Math.random() > 0.005) {
58
+ chunks.push(str[i]);
59
+ }
60
+ }
61
+ }
62
+ let result = chunks.join("");
63
+ if (Math.random() > 0.99) {
64
+ result = result.toUpperCase();
65
+ }
66
+ else if (Math.random() < 0.01) {
67
+ result = result.toLowerCase();
68
+ }
69
+ return result;
70
+ }
71
+ static radar(str, frame) {
72
+ const depth = Math.floor(Math.min(str.length, str.length * 0.2));
73
+ const step = Math.floor(255 / depth);
74
+ const globalPos = frame % (str.length + depth);
75
+ const chars = [];
76
+ for (let i = 0, length = str.length; i < length; i++) {
77
+ const pos = -(i - globalPos);
78
+ if (pos > 0 && pos <= depth - 1) {
79
+ const shade = (depth - pos) * step;
80
+ chars.push(chalk.rgb(shade, shade, shade)(str[i]));
81
+ }
82
+ else {
83
+ chars.push(" ");
84
+ }
85
+ }
86
+ return chars.join("");
87
+ }
88
+ static neon(str, frame) {
89
+ const color = frame % 2 === 0
90
+ ? chalk.dim.rgb(88, 80, 85)
91
+ : chalk.bold.rgb(213, 70, 242);
92
+ return color(str);
93
+ }
94
+ static karaoke(str, frame) {
95
+ const chars = (frame % (str.length + 20)) - 10;
96
+ if (chars < 0) {
97
+ return chalk.white(str);
98
+ }
99
+ return (chalk.rgb(255, 187, 0).bold(str.substr(0, chars)) +
100
+ chalk.white(str.substr(chars)));
101
+ }
102
+ }
@@ -0,0 +1,14 @@
1
+ import { AutoIncrementingID } from "@figliolia/event-emitter";
2
+ import type { Animation } from "./Animation";
3
+ export declare class InstanceTracker {
4
+ ID: string;
5
+ static readonly LOG: {
6
+ (...data: any[]): void;
7
+ (message?: any, ...optionalParams: any[]): void;
8
+ };
9
+ static IDs: AutoIncrementingID;
10
+ static bucket: Map<string, Animation>;
11
+ constructor();
12
+ get instance(): Animation;
13
+ static clearAll(): void;
14
+ }
@@ -0,0 +1,31 @@
1
+ import { AutoIncrementingID } from "@figliolia/event-emitter";
2
+ export class InstanceTracker {
3
+ ID;
4
+ static LOG = console.log;
5
+ static IDs = new AutoIncrementingID();
6
+ static bucket = new Map();
7
+ constructor() {
8
+ InstanceTracker.clearAll();
9
+ this.ID = InstanceTracker.IDs.get();
10
+ InstanceTracker.bucket.set(this.ID, this.instance);
11
+ }
12
+ static {
13
+ const methods = ["log", "warn", "info", "error"];
14
+ methods.forEach((method) => {
15
+ const original = console[method];
16
+ console[method] = function (...args) {
17
+ InstanceTracker.clearAll();
18
+ return original.apply(console, args);
19
+ };
20
+ });
21
+ }
22
+ get instance() {
23
+ return this;
24
+ }
25
+ static clearAll() {
26
+ for (const [, animation] of InstanceTracker.bucket) {
27
+ animation.stop();
28
+ }
29
+ InstanceTracker.bucket.clear();
30
+ }
31
+ }
@@ -0,0 +1,2 @@
1
+ import { Animation } from "./Animation";
2
+ export declare function animateString(effect: any, str: string, delay?: number, speed?: number): Animation;
@@ -0,0 +1,14 @@
1
+ import { Animation } from "./Animation";
2
+ export function animateString(effect, str, delay = 0, speed = 0) {
3
+ if (!speed || speed <= 0) {
4
+ throw new Error("Expected `speed` to be an number greater than 0");
5
+ }
6
+ const animation = new Animation(effect, str, delay, speed);
7
+ // setTimeout(() => {
8
+ // if (!animation.stopped) {
9
+ // animation.start();
10
+ // }
11
+ // }, delay / speed);
12
+ animation.start();
13
+ return animation;
14
+ }
@@ -0,0 +1,4 @@
1
+ export { ChalkAnimation } from "./ChalkAnimation";
2
+ export type { Animation } from "./Animation";
3
+ export type { InstanceTracker } from "./InstanceTracker";
4
+ export * from "./types";
@@ -0,0 +1,2 @@
1
+ export { ChalkAnimation } from "./ChalkAnimation";
2
+ export * from "./types";
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -0,0 +1 @@
1
+ export type AnimationFN = (str: string, frame: number) => string;
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,84 @@
1
+ {
2
+ "name": "@figliolia/chalk-animation",
3
+ "version": "1.0.0",
4
+ "description": "An ESM/Common.js compatible typescript port of the popular 'chalk-animation' library",
5
+ "keywords": [
6
+ "animation",
7
+ "chalk",
8
+ "cli",
9
+ "color",
10
+ "colors",
11
+ "colour",
12
+ "command-line",
13
+ "console",
14
+ "formatting",
15
+ "gradient",
16
+ "log",
17
+ "logging",
18
+ "rainbow",
19
+ "shell",
20
+ "string",
21
+ "style",
22
+ "styles",
23
+ "terminal"
24
+ ],
25
+ "homepage": "https://github.com/alexfigliolia/chalk-animation",
26
+ "bugs": "https://github.com/alexfigliolia/chalk-animation/issues",
27
+ "repository": "alexfigliolia/chalk-animation",
28
+ "license": "MIT",
29
+ "author": "Alex Figliolia",
30
+ "exports": {
31
+ ".": {
32
+ "import": "./dist/mjs/index.js",
33
+ "require": "./dist/cjs/index.js"
34
+ }
35
+ },
36
+ "main": "dist/cjs/index.js",
37
+ "module": "dist/mjs/index.js",
38
+ "types": "dist/mjs/index.d.ts",
39
+ "bin": "./cli.js",
40
+ "files": [
41
+ "dist",
42
+ "src/*"
43
+ ],
44
+ "scripts": {
45
+ "build": "rm -rf dist && yarn build-esm && yarn build-cjs && yarn package-targets",
46
+ "build-cjs": "tsc -p cd/tsconfig.cjs.json",
47
+ "build-esm": "tsc -p cd/tsconfig.mjs.json",
48
+ "lint": "tsc --noemit && eslint ./ --fix",
49
+ "package-targets": "cd/package.sh",
50
+ "test": "jest"
51
+ },
52
+ "dependencies": {
53
+ "@figliolia/event-emitter": "^1.1.1",
54
+ "chalk": "^4.1.2",
55
+ "gradient-string": "^2.0.2"
56
+ },
57
+ "devDependencies": {
58
+ "@swc/core": "^1.3.99",
59
+ "@types/gradient-string": "^1.1.5",
60
+ "@types/jest": "^29.5.10",
61
+ "@typescript-eslint/eslint-plugin": "^5.59.1",
62
+ "@typescript-eslint/parser": "^5.59.1",
63
+ "eslint": "^8.39.0",
64
+ "eslint-config-airbnb": "^19.0.4",
65
+ "eslint-config-airbnb-typescript": "^17.0.0",
66
+ "eslint-config-prettier": "^8.8.0",
67
+ "eslint-import-resolver-typescript": "^3.6.1",
68
+ "eslint-plugin-import": "^2.27.5",
69
+ "eslint-plugin-json-format": "^2.0.1",
70
+ "eslint-plugin-prettier": "^4.2.1",
71
+ "eslint-plugin-simple-import-sort": "^10.0.0",
72
+ "jest": "^29.7.0",
73
+ "prettier": "^2.8.8",
74
+ "ts-jest": "^29.1.1",
75
+ "ts-node": "^10.9.1",
76
+ "typescript": "^4.4.2"
77
+ },
78
+ "engines": {
79
+ "node": ">=16"
80
+ },
81
+ "publishConfig": {
82
+ "access": "public"
83
+ }
84
+ }
@@ -0,0 +1,71 @@
1
+ import type { AnimationFN } from "./types";
2
+ import { InstanceTracker } from "./InstanceTracker";
3
+
4
+ export class Animation extends InstanceTracker {
5
+ effect: any;
6
+ lines: number;
7
+ speed: number;
8
+ delay: number;
9
+ text: string[];
10
+ stopped = false;
11
+ currentFrame = 0;
12
+ initialized = false;
13
+ controller: ReturnType<typeof setTimeout> | null = null;
14
+ constructor(effect: AnimationFN, str: string, delay = 0, speed = 0) {
15
+ super();
16
+ this.effect = effect;
17
+ this.speed = speed;
18
+ this.delay = delay;
19
+ this.text = str.split(/\r\n|\r|\n/);
20
+ this.lines = this.text.length;
21
+ }
22
+
23
+ public render() {
24
+ if (!this.initialized) {
25
+ Animation.LOG("\n".repeat(this.lines - 1));
26
+ this.initialized = true;
27
+ }
28
+ Animation.LOG(this.frame());
29
+ this.controller = setTimeout(() => {
30
+ if (!this.stopped) {
31
+ this.render();
32
+ }
33
+ }, this.delay / this.speed);
34
+ }
35
+
36
+ public frame() {
37
+ this.currentFrame++;
38
+ return (
39
+ "\u001B[" +
40
+ `${this.lines}` +
41
+ "F\u001B[G\u001B[2K" +
42
+ this.text.map((str) => this.effect(str, this.currentFrame)).join("\n")
43
+ );
44
+ }
45
+
46
+ public replace(str: string) {
47
+ this.text = str.split(/\r\n|\r|\n/);
48
+ this.lines = this.text.length;
49
+ return this;
50
+ }
51
+
52
+ public stop() {
53
+ this.clearFrames();
54
+ this.stopped = true;
55
+ return this;
56
+ }
57
+
58
+ public start() {
59
+ this.clearFrames();
60
+ this.stopped = false;
61
+ this.render();
62
+ return this;
63
+ }
64
+
65
+ private clearFrames() {
66
+ if (this.controller) {
67
+ clearTimeout(this.controller);
68
+ this.controller = null;
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,33 @@
1
+ import { Effects } from "./Effects";
2
+ import { Animation } from "./Animation";
3
+ import { animateString } from "./animateString";
4
+
5
+ export class ChalkAnimation {
6
+ public static rainbow(str: string, speed = 1) {
7
+ return animateString(Effects.rainbow, str, 15, speed);
8
+ }
9
+
10
+ public static pulse(str: string, speed = 1) {
11
+ return animateString(Effects.pulse, str, 16, speed);
12
+ }
13
+
14
+ public static glitch(str: string, speed = 1) {
15
+ return animateString(Effects.glitch, str, 55, speed);
16
+ }
17
+
18
+ public static radar(str: string, speed = 1) {
19
+ return animateString(Effects.radar, str, 50, speed);
20
+ }
21
+
22
+ public static neon(str: string, speed = 1) {
23
+ return animateString(Effects.neon, str, 500, speed);
24
+ }
25
+
26
+ public static karaoke(str: string, speed = 1) {
27
+ return animateString(Effects.karaoke, str, 50, speed);
28
+ }
29
+
30
+ public static clearAllAnimations() {
31
+ return Animation.clearAll();
32
+ }
33
+ }
package/src/Effects.ts ADDED
@@ -0,0 +1,127 @@
1
+ import chalk from "chalk";
2
+ import gradient from "gradient-string";
3
+
4
+ export class Effects {
5
+ public static longHsv = { interpolation: "hsv", hsvSpin: "long" };
6
+ public static glitchChars =
7
+ "x*0987654321[]0-~@#(____!!!!\\|?????....0000\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
8
+ public static rainbow(str: string, frame: number) {
9
+ const hue = 5 * frame;
10
+ const leftColor = { h: hue % 360, s: 1, v: 1 };
11
+ const rightColor = { h: (hue + 1) % 360, s: 1, v: 1 };
12
+ return gradient(leftColor, rightColor)(str, Effects.longHsv);
13
+ }
14
+
15
+ public static pulse(str: string, frame: number) {
16
+ frame = (frame % 120) + 1;
17
+ const transition = 6;
18
+ const duration = 10;
19
+ const on = "#ff1010";
20
+ const off = "#e6e6e6";
21
+
22
+ if (frame >= 2 * transition + duration) {
23
+ return chalk.hex(off)(str); // All white
24
+ }
25
+ if (frame >= transition && frame <= transition + duration) {
26
+ return chalk.hex(on)(str); // All red
27
+ }
28
+
29
+ frame =
30
+ frame >= transition + duration
31
+ ? 2 * transition + duration - frame
32
+ : frame; // Revert animation
33
+
34
+ const g =
35
+ frame <= transition / 2
36
+ ? gradient([
37
+ { color: off, pos: 0.5 - frame / transition },
38
+ { color: on, pos: 0.5 },
39
+ { color: off, pos: 0.5 + frame / transition },
40
+ ])
41
+ : gradient([
42
+ { color: off, pos: 0 },
43
+ { color: on, pos: 1 - frame / transition },
44
+ { color: on, pos: frame / transition },
45
+ { color: off, pos: 1 },
46
+ ]);
47
+
48
+ return g(str);
49
+ }
50
+
51
+ public static glitch(str: string, frame: number) {
52
+ if (
53
+ (frame % 2) + (frame % 3) + (frame % 11) + (frame % 29) + (frame % 37) >
54
+ 52
55
+ ) {
56
+ return str.replace(/[^\r\n]/g, " ");
57
+ }
58
+
59
+ const chunkSize = Math.max(3, Math.round(str.length * 0.02));
60
+ const chunks = [];
61
+
62
+ for (let i = 0, length = str.length; i < length; i++) {
63
+ const skip = Math.round(Math.max(0, (Math.random() - 0.8) * chunkSize));
64
+ chunks.push(str.substring(i, i + skip).replace(/[^\r\n]/g, " "));
65
+ i += skip;
66
+ if (str[i]) {
67
+ if (str[i] !== "\n" && str[i] !== "\r" && Math.random() > 0.995) {
68
+ chunks.push(
69
+ Effects.glitchChars[
70
+ Math.floor(Math.random() * Effects.glitchChars.length)
71
+ ]
72
+ );
73
+ } else if (Math.random() > 0.005) {
74
+ chunks.push(str[i]);
75
+ }
76
+ }
77
+ }
78
+
79
+ let result = chunks.join("");
80
+ if (Math.random() > 0.99) {
81
+ result = result.toUpperCase();
82
+ } else if (Math.random() < 0.01) {
83
+ result = result.toLowerCase();
84
+ }
85
+
86
+ return result;
87
+ }
88
+
89
+ public static radar(str: string, frame: number) {
90
+ const depth = Math.floor(Math.min(str.length, str.length * 0.2));
91
+ const step = Math.floor(255 / depth);
92
+
93
+ const globalPos = frame % (str.length + depth);
94
+
95
+ const chars = [];
96
+ for (let i = 0, length = str.length; i < length; i++) {
97
+ const pos = -(i - globalPos);
98
+ if (pos > 0 && pos <= depth - 1) {
99
+ const shade = (depth - pos) * step;
100
+ chars.push(chalk.rgb(shade, shade, shade)(str[i]));
101
+ } else {
102
+ chars.push(" ");
103
+ }
104
+ }
105
+
106
+ return chars.join("");
107
+ }
108
+
109
+ public static neon(str: string, frame: number) {
110
+ const color =
111
+ frame % 2 === 0
112
+ ? chalk.dim.rgb(88, 80, 85)
113
+ : chalk.bold.rgb(213, 70, 242);
114
+ return color(str);
115
+ }
116
+
117
+ public static karaoke(str: string, frame: number) {
118
+ const chars = (frame % (str.length + 20)) - 10;
119
+ if (chars < 0) {
120
+ return chalk.white(str);
121
+ }
122
+ return (
123
+ chalk.rgb(255, 187, 0).bold(str.substr(0, chars)) +
124
+ chalk.white(str.substr(chars))
125
+ );
126
+ }
127
+ }
@@ -0,0 +1,36 @@
1
+ import { AutoIncrementingID } from "@figliolia/event-emitter";
2
+ import type { Animation } from "./Animation";
3
+
4
+ export class InstanceTracker {
5
+ ID: string;
6
+ static readonly LOG = console.log;
7
+ static IDs = new AutoIncrementingID();
8
+ static bucket = new Map<string, Animation>();
9
+ constructor() {
10
+ InstanceTracker.clearAll();
11
+ this.ID = InstanceTracker.IDs.get();
12
+ InstanceTracker.bucket.set(this.ID, this.instance);
13
+ }
14
+
15
+ static {
16
+ const methods = ["log", "warn", "info", "error"] as const;
17
+ methods.forEach((method) => {
18
+ const original = console[method];
19
+ console[method] = function (...args: Parameters<typeof console.log>) {
20
+ InstanceTracker.clearAll();
21
+ return original.apply(console, args);
22
+ };
23
+ });
24
+ }
25
+
26
+ public get instance() {
27
+ return this as unknown as Animation;
28
+ }
29
+
30
+ public static clearAll() {
31
+ for (const [, animation] of InstanceTracker.bucket) {
32
+ animation.stop();
33
+ }
34
+ InstanceTracker.bucket.clear();
35
+ }
36
+ }
@@ -0,0 +1,15 @@
1
+ import { Animation } from "./Animation";
2
+
3
+ export function animateString(effect: any, str: string, delay = 0, speed = 0) {
4
+ if (!speed || speed <= 0) {
5
+ throw new Error("Expected `speed` to be an number greater than 0");
6
+ }
7
+ const animation = new Animation(effect, str, delay, speed);
8
+ // setTimeout(() => {
9
+ // if (!animation.stopped) {
10
+ // animation.start();
11
+ // }
12
+ // }, delay / speed);
13
+ animation.start();
14
+ return animation;
15
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export { ChalkAnimation } from "./ChalkAnimation";
2
+ export type { Animation } from "./Animation";
3
+ export type { InstanceTracker } from "./InstanceTracker";
4
+ export * from "./types";
package/src/types.ts ADDED
@@ -0,0 +1 @@
1
+ export type AnimationFN = (str: string, frame: number) => string;