@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 +146 -0
- package/dist/cjs/Animation.d.ts +20 -0
- package/dist/cjs/Animation.js +60 -0
- package/dist/cjs/ChalkAnimation.d.ts +10 -0
- package/dist/cjs/ChalkAnimation.js +30 -0
- package/dist/cjs/Effects.d.ts +13 -0
- package/dist/cjs/Effects.js +109 -0
- package/dist/cjs/InstanceTracker.d.ts +14 -0
- package/dist/cjs/InstanceTracker.js +34 -0
- package/dist/cjs/animateString.d.ts +2 -0
- package/dist/cjs/animateString.js +18 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/types.d.ts +1 -0
- package/dist/cjs/types.js +2 -0
- package/dist/mjs/Animation.d.ts +20 -0
- package/dist/mjs/Animation.js +61 -0
- package/dist/mjs/ChalkAnimation.d.ts +10 -0
- package/dist/mjs/ChalkAnimation.js +26 -0
- package/dist/mjs/Effects.d.ts +13 -0
- package/dist/mjs/Effects.js +102 -0
- package/dist/mjs/InstanceTracker.d.ts +14 -0
- package/dist/mjs/InstanceTracker.js +31 -0
- package/dist/mjs/animateString.d.ts +2 -0
- package/dist/mjs/animateString.js +14 -0
- package/dist/mjs/index.d.ts +4 -0
- package/dist/mjs/index.js +2 -0
- package/dist/mjs/package.json +3 -0
- package/dist/mjs/types.d.ts +1 -0
- package/dist/mjs/types.js +1 -0
- package/package.json +84 -0
- package/src/Animation.ts +71 -0
- package/src/ChalkAnimation.ts +33 -0
- package/src/Effects.ts +127 -0
- package/src/InstanceTracker.ts +36 -0
- package/src/animateString.ts +15 -0
- package/src/index.ts +4 -0
- package/src/types.ts +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# chalk-animation
|
|
2
|
+
|
|
3
|
+
[](https://github.com/bokub/chalk-animation/actions/workflows/run.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/chalk-animation)
|
|
5
|
+
[](https://codecov.io/gh/bokub/chalk-animation)
|
|
6
|
+
[](https://www.npmjs.com/package/chalk-animation)
|
|
7
|
+
[](https://github.com/sindresorhus/xo)
|
|
8
|
+
|
|
9
|
+
> Colorful animations in terminal output
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Available animations
|
|
13
|
+
|
|
14
|
+
| Name | Preview |
|
|
15
|
+
|:---------:|:------------------------------------------:|
|
|
16
|
+
| rainbow |  |
|
|
17
|
+
| pulse |  |
|
|
18
|
+
| glitch |  |
|
|
19
|
+
| radar |  |
|
|
20
|
+
| neon |  |
|
|
21
|
+
| karaoke | |
|
|
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,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,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 @@
|
|
|
1
|
+
export type AnimationFN = (str: string, frame: number) => string;
|
|
@@ -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,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 @@
|
|
|
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
|
+
}
|
package/src/Animation.ts
ADDED
|
@@ -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
package/src/types.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type AnimationFN = (str: string, frame: number) => string;
|