react-video-ascii 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 +126 -0
- package/dist/components/VideoAscii.d.ts +3 -0
- package/dist/index.d.ts +2 -0
- package/dist/lib/ascii-props.d.ts +62 -0
- package/dist/lib/ascii-utils.d.ts +7 -0
- package/dist/lib/brighten-effect.d.ts +19 -0
- package/dist/lib/click-effect.d.ts +16 -0
- package/dist/lib/create-gl-resources.d.ts +49 -0
- package/dist/lib/scatter-effect.d.ts +22 -0
- package/dist/lib/spread-effect.d.ts +20 -0
- package/dist/lib/webgl-utils.d.ts +3 -0
- package/dist/react-video-ascii.js +513 -0
- package/dist/react-video-ascii.umd.cjs +280 -0
- package/package.json +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# react-video-ascii
|
|
2
|
+
|
|
3
|
+
React component optimized for rendering videos as ASCII using WebGL2.
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<img width="1512" height="949" alt="demo" src="https://github.com/user-attachments/assets/2b41b0af-e864-430d-ade5-0249335d1aac" />
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
[Try it out!](https://video-ascii-demo.vercel.app/)
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install react-video-ascii
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { VideoAscii } from 'react-video-ascii';
|
|
21
|
+
|
|
22
|
+
// Basic usage
|
|
23
|
+
<VideoAscii src="/video.mp4" />
|
|
24
|
+
|
|
25
|
+
// Multiple videos (loop sequentially)
|
|
26
|
+
const sources = ['/video1.mp4', '/video2.mp4'];
|
|
27
|
+
<VideoAscii src={sources} />
|
|
28
|
+
|
|
29
|
+
// With options
|
|
30
|
+
<VideoAscii
|
|
31
|
+
src="/video.mp4"
|
|
32
|
+
videoMode={false}
|
|
33
|
+
numColsRaw={150}
|
|
34
|
+
brightnessRaw={1.2}
|
|
35
|
+
saturationRaw={1.0}
|
|
36
|
+
bgOpacityRaw={0.3}
|
|
37
|
+
chars=" `.',-_:!abcdef"
|
|
38
|
+
charMode="shape"
|
|
39
|
+
mouseEffect={{
|
|
40
|
+
style: 'brighten',
|
|
41
|
+
radius: 0.08,
|
|
42
|
+
duration: 1.0,
|
|
43
|
+
trailLen: 15,
|
|
44
|
+
trailDecay: 10,
|
|
45
|
+
brightness: 2.0,
|
|
46
|
+
}}
|
|
47
|
+
clickEffect={{
|
|
48
|
+
style: 'ripple',
|
|
49
|
+
brightness: 1.1,
|
|
50
|
+
speed: 2,
|
|
51
|
+
}}
|
|
52
|
+
revealEffect={{
|
|
53
|
+
type: 'random',
|
|
54
|
+
duration: 0.4,
|
|
55
|
+
}}
|
|
56
|
+
className="my-ascii"
|
|
57
|
+
/>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
> **Note 1:** When passing an array to `src`, define it outside the component or in a `useMemo`/`useRef` (an inline array literal creates a new reference on every render and will cause the video to reload).
|
|
61
|
+
|
|
62
|
+
> **Note 2:** The component fills its parent container. Control size via the parent element or the `className` prop.
|
|
63
|
+
|
|
64
|
+
## Props
|
|
65
|
+
|
|
66
|
+
| Prop | Type | Default | Range | Description |
|
|
67
|
+
|------|------|---------|-------|-------------|
|
|
68
|
+
| `src` | `string \| string[]` | — | — | Video source URL(s). Multiple URLs play sequentially. |
|
|
69
|
+
| `videoMode` | `boolean` | `false` | — | Show original video colors instead of ASCII. |
|
|
70
|
+
| `numColsRaw` | `number` | `250` | `20–350` | Number of character columns. |
|
|
71
|
+
| `brightnessRaw` | `number` | `1.0` | `0.0–2.0` | Brightness multiplier. |
|
|
72
|
+
| `saturationRaw` | `number` | `1.0` | `0.0–2.0` | Saturation multiplier. |
|
|
73
|
+
| `bgOpacityRaw` | `number` | `0.3` | `0.0–1.0` | Background opacity. |
|
|
74
|
+
| `chars` | `string` | *(standard)* | — | Character set, ordered dark to bright. |
|
|
75
|
+
| `charMode` | `'shape' \| 'luminance'` | `'shape'` | — | `shape` matches glyph silhouettes; `luminance` uses brightness. |
|
|
76
|
+
| `mouseEffect` | `boolean \| MouseEffectOptions` | `true` | — | Mouse hover effects. `true` uses defaults. |
|
|
77
|
+
| `clickEffect` | `boolean \| ClickEffectOptions` | `true` | — | Click effects. `true` uses defaults. |
|
|
78
|
+
| `revealEffect` | `boolean \| RevealEffectOptions` | `false` | — | Reveal animation on load. `true` uses defaults. |
|
|
79
|
+
| `className` | `string` | — | — | CSS class on the outer container. |
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### `MouseEffectOptions`
|
|
84
|
+
|
|
85
|
+
Passed to `mouseEffect`. All fields optional.
|
|
86
|
+
|
|
87
|
+
| Field | Type | Default | Range | Description |
|
|
88
|
+
|-------|------|---------|-------|-------------|
|
|
89
|
+
| `style` | `'brighten' \| 'scatter'` | `'brighten'` | — | `brighten` highlights chars; `scatter` replaces them. |
|
|
90
|
+
| `radius` | `number` | `0.08` / `0.05` | `0.03–0.2` | Effect radius as a fraction of the smaller canvas dimension. |
|
|
91
|
+
| `duration` | `number` | `1.0` | `0.1–4` | Seconds the effect lingers after the cursor leaves. |
|
|
92
|
+
| `trailLen` | `number` | `15` | `0–30` | *(brighten)* Trail positions tracked. |
|
|
93
|
+
| `trailDecay` | `number` | `10` | `1–15` | *(brighten)* How fast older trail positions fade. |
|
|
94
|
+
| `brightness` | `number` | `2.0` | `0.2–5.0` | *(brighten)* Brightness boost at the cursor. |
|
|
95
|
+
| `scatterChars` | `string` | `'->o'` | — | *(scatter)* Chars substituted near the cursor. |
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### `ClickEffectOptions`
|
|
100
|
+
|
|
101
|
+
Passed to `clickEffect`. All fields optional.
|
|
102
|
+
|
|
103
|
+
| Field | Type | Default | Range | Description |
|
|
104
|
+
|-------|------|---------|-------|-------------|
|
|
105
|
+
| `style` | `'ripple' \| 'spread'` | `'ripple'` | — | `ripple` sends a brightness ring outward; `spread` expands a scatter region. |
|
|
106
|
+
| `brightness` | `number` | `1.1` | `1.05–2.0` | *(ripple)* Brightness of the ripple ring. |
|
|
107
|
+
| `speed` | `number` | `2` | `0.5–4.0` | *(ripple)* Ripple expansion speed. |
|
|
108
|
+
| `spreadExpandDuration` | `number` | `1.5` | `0.5–5.0` | *(spread)* Seconds for the region to fully expand. |
|
|
109
|
+
| `spreadSpeed` | `number` | `7.5` | `0.5–10.0` | *(spread)* Speed of the spread wave front. |
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### `RevealEffectOptions`
|
|
114
|
+
|
|
115
|
+
Passed to `revealEffect`. All fields optional.
|
|
116
|
+
|
|
117
|
+
| Field | Type | Default | Range | Description |
|
|
118
|
+
|-------|------|---------|-------|-------------|
|
|
119
|
+
| `type` | `'diagonal' \| 'radial' \| 'random'` | `'random'` | — | Pattern in which characters appear on load. |
|
|
120
|
+
| `duration` | `number` | `0.4` | `0.1–4` | Reveal animation duration in seconds. |
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
### `chars`
|
|
125
|
+
|
|
126
|
+
The default `chars` set is: `` `.',-_:!;|\"~+^lr[](\\/L)>t<v=Tz?icf1{sIxY*jJno}CZyVwmSXRqM$O%#9&NW0Q@``.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { Props } from '../lib/ascii-props';
|
|
2
|
+
declare function VideoAscii({ src, videoMode, numColsRaw, brightnessRaw, saturationRaw, bgOpacityRaw, revealEffect, chars, mouseEffect, clickEffect, charMode, className, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export default VideoAscii;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export declare const DEFAULT_CHARS = " `.',-_:!;|\"~+^lr[](\\/L)>t<v=Tz?icf1{sIxY*jJno}CZyVwmSXRqM$O%#9&NW0Q@";
|
|
2
|
+
export interface MouseEffectOptions {
|
|
3
|
+
style?: 'brighten' | 'scatter';
|
|
4
|
+
radius?: number;
|
|
5
|
+
duration?: number;
|
|
6
|
+
trailLen?: number;
|
|
7
|
+
trailDecay?: number;
|
|
8
|
+
brightness?: number;
|
|
9
|
+
scatterChars?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface ClickEffectOptions {
|
|
12
|
+
style?: 'ripple' | 'spread';
|
|
13
|
+
brightness?: number;
|
|
14
|
+
speed?: number;
|
|
15
|
+
spreadExpandDuration?: number;
|
|
16
|
+
spreadSpeed?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface RevealEffectOptions {
|
|
19
|
+
type?: 'diagonal' | 'radial' | 'random';
|
|
20
|
+
duration?: number;
|
|
21
|
+
}
|
|
22
|
+
export interface Props {
|
|
23
|
+
src: string | string[];
|
|
24
|
+
videoMode?: boolean;
|
|
25
|
+
numColsRaw?: number;
|
|
26
|
+
brightnessRaw?: number;
|
|
27
|
+
saturationRaw?: number;
|
|
28
|
+
bgOpacityRaw?: number;
|
|
29
|
+
revealEffect?: boolean | RevealEffectOptions;
|
|
30
|
+
chars?: string;
|
|
31
|
+
mouseEffect?: boolean | MouseEffectOptions;
|
|
32
|
+
clickEffect?: boolean | ClickEffectOptions;
|
|
33
|
+
charMode?: 'shape' | 'luminance';
|
|
34
|
+
className?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ParsedProps {
|
|
37
|
+
numCols: number;
|
|
38
|
+
brightness: number;
|
|
39
|
+
saturation: number;
|
|
40
|
+
bgOpacity: number;
|
|
41
|
+
mouseEnabled: boolean;
|
|
42
|
+
mouseStyle: 'brighten' | 'scatter';
|
|
43
|
+
brightenEnabled: boolean;
|
|
44
|
+
scatterEnabled: boolean;
|
|
45
|
+
scatterChars: string;
|
|
46
|
+
trailLen: number;
|
|
47
|
+
trailDecay: number;
|
|
48
|
+
duration: number;
|
|
49
|
+
mouseRadius: number;
|
|
50
|
+
mouseBrightness: number;
|
|
51
|
+
clickEnabled: boolean;
|
|
52
|
+
clickBrightness: number;
|
|
53
|
+
clickSpeed: number;
|
|
54
|
+
spreadEnabled: boolean;
|
|
55
|
+
spreadExpandDuration: number;
|
|
56
|
+
spreadSpeed: number;
|
|
57
|
+
revealEnabled: boolean;
|
|
58
|
+
revealType: string;
|
|
59
|
+
revealDuration: number;
|
|
60
|
+
revealEffectFlag: number;
|
|
61
|
+
}
|
|
62
|
+
export declare function parseProps(numColsRaw: number, brightnessRaw: number, saturationRaw: number, bgOpacityRaw: number, mouseEffect: boolean | MouseEffectOptions, clickEffect: boolean | ClickEffectOptions, revealEffect: boolean | RevealEffectOptions): ParsedProps;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface MouseTrailRefs {
|
|
2
|
+
brightenEnabledRef: {
|
|
3
|
+
current: boolean;
|
|
4
|
+
};
|
|
5
|
+
trailLenRef: {
|
|
6
|
+
current: number;
|
|
7
|
+
};
|
|
8
|
+
durationRef: {
|
|
9
|
+
current: number;
|
|
10
|
+
};
|
|
11
|
+
trailDecayRef: {
|
|
12
|
+
current: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare function createMouseTrail({ brightenEnabledRef, trailLenRef, durationRef, trailDecayRef, }: MouseTrailRefs): {
|
|
16
|
+
handleMouseMove(x: number, y: number, t: number): void;
|
|
17
|
+
tick(gl: WebGL2RenderingContext, mousePositionsLoc: WebGLUniformLocation | null, mouseLifeFracsLoc: WebGLUniformLocation | null): void;
|
|
18
|
+
};
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
interface ClickEffectRefs {
|
|
2
|
+
clickEnabledRef: {
|
|
3
|
+
current: boolean;
|
|
4
|
+
};
|
|
5
|
+
clickSpeedRef: {
|
|
6
|
+
current: number;
|
|
7
|
+
};
|
|
8
|
+
clickBrightnessRef: {
|
|
9
|
+
current: number;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export declare function createClickEffect({ clickEnabledRef, clickSpeedRef, clickBrightnessRef, }: ClickEffectRefs): {
|
|
13
|
+
handleClick(e: MouseEvent, canvas: HTMLCanvasElement): void;
|
|
14
|
+
tick(gl: WebGL2RenderingContext, canvas: HTMLCanvasElement, ripplePositionsLoc: WebGLUniformLocation | null, rippleRadiiLoc: WebGLUniformLocation | null, rippleBrightnessesLoc: WebGLUniformLocation | null): void;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export interface GLResources {
|
|
2
|
+
program: WebGLProgram;
|
|
3
|
+
pass1Program: WebGLProgram;
|
|
4
|
+
buffer: WebGLBuffer;
|
|
5
|
+
vertShader: WebGLShader;
|
|
6
|
+
fragShader: WebGLShader;
|
|
7
|
+
pass1FragShader: WebGLShader;
|
|
8
|
+
texture: WebGLTexture;
|
|
9
|
+
atlasTexture: WebGLTexture;
|
|
10
|
+
charVectorsTexture: WebGLTexture;
|
|
11
|
+
fboTexture: WebGLTexture;
|
|
12
|
+
fbo: WebGLFramebuffer;
|
|
13
|
+
scatterAtlasTexture: WebGLTexture;
|
|
14
|
+
scatterStateTexture: WebGLTexture;
|
|
15
|
+
spreadStateTexture: WebGLTexture;
|
|
16
|
+
p1ResLoc: WebGLUniformLocation | null;
|
|
17
|
+
p1CellsizeLoc: WebGLUniformLocation | null;
|
|
18
|
+
p1CircleNLoc: WebGLUniformLocation | null;
|
|
19
|
+
p1NumCharsLoc: WebGLUniformLocation | null;
|
|
20
|
+
p1ExponentLoc: WebGLUniformLocation | null;
|
|
21
|
+
p1CropOffsetLoc: WebGLUniformLocation | null;
|
|
22
|
+
p1CropScaleLoc: WebGLUniformLocation | null;
|
|
23
|
+
revealEffectFlagLoc: WebGLUniformLocation | null;
|
|
24
|
+
mouseEffectFlagLoc: WebGLUniformLocation | null;
|
|
25
|
+
clickEffectFlagLoc: WebGLUniformLocation | null;
|
|
26
|
+
shapeMatchingLoc: WebGLUniformLocation | null;
|
|
27
|
+
revealProgressLoc: WebGLUniformLocation | null;
|
|
28
|
+
brightnessLoc: WebGLUniformLocation | null;
|
|
29
|
+
saturationLoc: WebGLUniformLocation | null;
|
|
30
|
+
bgOpacityLoc: WebGLUniformLocation | null;
|
|
31
|
+
mouseBrightnessLoc: WebGLUniformLocation | null;
|
|
32
|
+
mousePositionsLoc: WebGLUniformLocation | null;
|
|
33
|
+
mouseLifeFracsLoc: WebGLUniformLocation | null;
|
|
34
|
+
mouseRadiusLoc: WebGLUniformLocation | null;
|
|
35
|
+
ripplePositionsLoc: WebGLUniformLocation | null;
|
|
36
|
+
rippleRadiiLoc: WebGLUniformLocation | null;
|
|
37
|
+
rippleBrightnessesLoc: WebGLUniformLocation | null;
|
|
38
|
+
scatterEffectFlagLoc: WebGLUniformLocation | null;
|
|
39
|
+
scatterNumCharsLoc: WebGLUniformLocation | null;
|
|
40
|
+
spreadEffectFlagLoc: WebGLUniformLocation | null;
|
|
41
|
+
videoModeLoc: WebGLUniformLocation | null;
|
|
42
|
+
resLoc: WebGLUniformLocation | null;
|
|
43
|
+
sizeLoc: WebGLUniformLocation | null;
|
|
44
|
+
numLoc: WebGLUniformLocation | null;
|
|
45
|
+
gridSizeLoc: WebGLUniformLocation | null;
|
|
46
|
+
cropOffsetLoc: WebGLUniformLocation | null;
|
|
47
|
+
cropScaleLoc: WebGLUniformLocation | null;
|
|
48
|
+
}
|
|
49
|
+
export declare function createGLResources(gl: WebGL2RenderingContext): GLResources | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
interface ScatterRefs {
|
|
2
|
+
scatterEnabledRef: {
|
|
3
|
+
current: boolean;
|
|
4
|
+
};
|
|
5
|
+
mouseRadiusRef: {
|
|
6
|
+
current: number;
|
|
7
|
+
};
|
|
8
|
+
durationRef: {
|
|
9
|
+
current: number;
|
|
10
|
+
};
|
|
11
|
+
scatterCharsRef: {
|
|
12
|
+
current: string;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare function createScatterEffect({ scatterEnabledRef, mouseRadiusRef, durationRef, scatterCharsRef, }: ScatterRefs): {
|
|
16
|
+
setup(gl: WebGL2RenderingContext, cols: number, rows: number, cw: number, ch: number, scatterStateTexture: WebGLTexture): void;
|
|
17
|
+
handleMouseMove(x: number, y: number, t: number): void;
|
|
18
|
+
handleMouseLeave(): void;
|
|
19
|
+
reset(gl: WebGL2RenderingContext): void;
|
|
20
|
+
tick(gl: WebGL2RenderingContext, canvas: HTMLCanvasElement): void;
|
|
21
|
+
};
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface SpreadRefs {
|
|
2
|
+
spreadEnabledRef: {
|
|
3
|
+
current: boolean;
|
|
4
|
+
};
|
|
5
|
+
scatterCharsRef: {
|
|
6
|
+
current: string;
|
|
7
|
+
};
|
|
8
|
+
spreadExpandDurationRef: {
|
|
9
|
+
current: number;
|
|
10
|
+
};
|
|
11
|
+
spreadSpeedRef: {
|
|
12
|
+
current: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export declare function createSpreadEffect({ spreadEnabledRef, scatterCharsRef, spreadExpandDurationRef, spreadSpeedRef, }: SpreadRefs): {
|
|
16
|
+
setup(gl: WebGL2RenderingContext, cols: number, rows: number, cw: number, ch: number, spreadStateTexture: WebGLTexture): void;
|
|
17
|
+
handleClick(e: MouseEvent, canvas: HTMLCanvasElement): void;
|
|
18
|
+
tick(gl: WebGL2RenderingContext): void;
|
|
19
|
+
};
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
import { useEffect as e, useRef as t } from "react";
|
|
2
|
+
import { jsx as n, jsxs as r } from "react/jsx-runtime";
|
|
3
|
+
//#region src/lib/ascii-utils.ts
|
|
4
|
+
var i = [
|
|
5
|
+
[.25, .25],
|
|
6
|
+
[.75, .25],
|
|
7
|
+
[.25, .5],
|
|
8
|
+
[.75, .5],
|
|
9
|
+
[.25, .75],
|
|
10
|
+
[.75, .75]
|
|
11
|
+
];
|
|
12
|
+
function a(e, t, n) {
|
|
13
|
+
let r = [], a = document.createElement("canvas");
|
|
14
|
+
a.width = t, a.height = n;
|
|
15
|
+
let o = a.getContext("2d", { willReadFrequently: !0 });
|
|
16
|
+
if (!o) return [];
|
|
17
|
+
o.font = `${n}px monospace`, o.textBaseline = "top";
|
|
18
|
+
let s = t / 5;
|
|
19
|
+
for (let a of e) {
|
|
20
|
+
o.fillStyle = "black", o.fillRect(0, 0, t, n), o.fillStyle = "white", o.fillText(a, 0, 0);
|
|
21
|
+
let e = o.getImageData(0, 0, t, n), c = [];
|
|
22
|
+
for (let [r, a] of i) {
|
|
23
|
+
let i = r * t, o = a * n, l = 0, u = 0;
|
|
24
|
+
for (let n = Math.floor(i - s); n <= Math.ceil(i + s); n += 1) for (let r = Math.floor(o - s); r <= Math.ceil(o + s); r += 1) {
|
|
25
|
+
let a = i - n, c = o - r;
|
|
26
|
+
if (a * a + c * c <= s * s) {
|
|
27
|
+
let i = (r * t + n) * 4, a = e.data[i];
|
|
28
|
+
l += a / 255, u++;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
let d = u > 0 ? l / u : 0;
|
|
32
|
+
c.push(d);
|
|
33
|
+
}
|
|
34
|
+
r.push({
|
|
35
|
+
char: a,
|
|
36
|
+
vector: c
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
let c = [
|
|
40
|
+
,
|
|
41
|
+
,
|
|
42
|
+
,
|
|
43
|
+
,
|
|
44
|
+
,
|
|
45
|
+
,
|
|
46
|
+
].fill(0);
|
|
47
|
+
for (let { vector: e } of r) for (let t = 0; t < 6; ++t) e[t] > c[t] && (c[t] = e[t]);
|
|
48
|
+
for (let { vector: e } of r) for (let t = 0; t < 6; ++t) e[t] /= c[t];
|
|
49
|
+
return r;
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
//#region src/lib/ascii-props.ts
|
|
53
|
+
var o = " `.',-_:!;|\"~+^lr[](\\/L)>t<v=Tz?icf1{sIxY*jJno}CZyVwmSXRqM$O%#9&NW0Q@";
|
|
54
|
+
function s(e, t, n, r, i, a, o) {
|
|
55
|
+
let s = !!i, c = typeof i == "object" ? i : {}, l = c.style ?? "brighten", u = s && l !== "scatter", d = s && l === "scatter", f = c.scatterChars ?? "->o", p = c.trailLen ?? 15, m = c.trailDecay ?? 10, h = c.duration ?? 1, g = c.radius ?? (l === "scatter" ? .05 : .08), _ = c.brightness ?? 2, v = !!a, y = typeof a == "object" ? a : {}, b = y.style ?? "ripple", x = v && b !== "spread", S = v && b === "spread", C = y.brightness ?? 1.1, w = y.speed ?? 2, T = y.spreadExpandDuration ?? 1.5, E = y.spreadSpeed ?? 7.5, D = !!o, O = typeof o == "object" ? o : {}, k = O.type ?? "random", A = O.duration ?? .4, j = Math.max(20, Math.min(350, Math.round(e))), M = Math.max(0, Math.min(2, t)), N = Math.max(0, Math.min(2, n)), P = Math.max(0, Math.min(1, r));
|
|
56
|
+
A = Math.max(.1, Math.min(4, A)), p = Math.max(0, Math.min(30, Math.round(p))), m = Math.max(1, Math.min(15, m)), h = Math.max(.1, Math.min(4, h)), g = Math.max(.03, Math.min(.2, g)), _ = Math.max(.2, Math.min(5, _)), C = Math.max(1.05, Math.min(2, C)), w = Math.max(.5, Math.min(4, w)), T = Math.max(.5, Math.min(5, T)), E = Math.max(.5, Math.min(10, E));
|
|
57
|
+
let F;
|
|
58
|
+
return F = D ? k === "diagonal" ? 1 : k === "radial" ? 2 : 3 : 0, {
|
|
59
|
+
numCols: j,
|
|
60
|
+
brightness: M,
|
|
61
|
+
saturation: N,
|
|
62
|
+
bgOpacity: P,
|
|
63
|
+
mouseEnabled: s,
|
|
64
|
+
mouseStyle: l,
|
|
65
|
+
brightenEnabled: u,
|
|
66
|
+
scatterEnabled: d,
|
|
67
|
+
scatterChars: f,
|
|
68
|
+
trailLen: p,
|
|
69
|
+
trailDecay: m,
|
|
70
|
+
duration: h,
|
|
71
|
+
mouseRadius: g,
|
|
72
|
+
mouseBrightness: _,
|
|
73
|
+
clickEnabled: x,
|
|
74
|
+
clickBrightness: C,
|
|
75
|
+
clickSpeed: w,
|
|
76
|
+
spreadEnabled: S,
|
|
77
|
+
spreadExpandDuration: T,
|
|
78
|
+
spreadSpeed: E,
|
|
79
|
+
revealEnabled: D,
|
|
80
|
+
revealType: k,
|
|
81
|
+
revealDuration: A,
|
|
82
|
+
revealEffectFlag: F
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//#endregion
|
|
86
|
+
//#region src/lib/webgl-utils.ts
|
|
87
|
+
function c(e, t, n) {
|
|
88
|
+
let r = e.createShader(t);
|
|
89
|
+
if (!r) throw Error("Failed to create shader");
|
|
90
|
+
return e.shaderSource(r, n), e.compileShader(r), e.getShaderParameter(r, e.COMPILE_STATUS) ? r : (console.log(e.getShaderInfoLog(r)), e.deleteShader(r), null);
|
|
91
|
+
}
|
|
92
|
+
function l(e, t, n) {
|
|
93
|
+
let r = e.createProgram();
|
|
94
|
+
return e.attachShader(r, t), e.attachShader(r, n), e.linkProgram(r), e.getProgramParameter(r, e.LINK_STATUS) ? r : (console.log(e.getProgramInfoLog(r)), e.deleteProgram(r), null);
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
//#region src/shaders/vertex.glsl
|
|
98
|
+
var u = "#version 300 es\n\nin vec2 a_position;\n\nvoid main() {\n gl_Position = vec4(a_position, 0.0, 1.0);\n}", d = "#version 300 es\n\nprecision highp float;\nprecision highp usampler2D;\n\nuniform int u_revealEffectFlag;\nuniform bool u_videoMode;\n\nuniform float u_revealProgress;\nuniform float u_brightness;\nuniform float u_saturation;\nuniform float u_bgOpacity;\n\nuniform bool u_mouseEffect;\n#define MOUSE_TRAIL_LEN 30 \nuniform vec2 u_mousePositions[MOUSE_TRAIL_LEN];\nuniform float u_mouseLifeFracs[MOUSE_TRAIL_LEN]; \nuniform float u_mouseRadius;\nuniform float u_mouseBrightness;\n\nuniform bool u_clickEffect;\n#define MAX_RIPPLES 10\nuniform vec2 u_ripplePositions[MAX_RIPPLES];\nuniform float u_rippleRadii[MAX_RIPPLES]; \nuniform float u_rippleBrightnesses[MAX_RIPPLES]; \n\nuniform bool u_scatterEffect;\nuniform usampler2D u_scatterStateTexture; \nuniform sampler2D u_scatterAtlas; \nuniform float u_scatterNumChars;\n\nuniform bool u_spreadEffect;\nuniform usampler2D u_spreadStateTexture;\n\nuniform bool u_shapeMatching;\n\nuniform sampler2D u_texture;\nuniform sampler2D u_atlas;\nuniform usampler2D u_fboTexture;\nuniform vec2 u_resolution;\nuniform vec2 u_cellsize;\nuniform float u_numChars;\nuniform vec2 u_gridSize;\nuniform vec2 u_cropOffset;\nuniform vec2 u_cropScale;\n\nout vec4 fragColor;\n\nfloat hash(vec2 p) {\n vec3 p3 = fract(vec3(p.xyx) * 0.1031); \n p3 += dot(p3, p3.yzx + 33.33); \n return fract((p3.x + p3.y) * p3.z); \n}\n\nvec3 rgb2hsl(vec3 c) {\n float maxC = max(c.r, max(c.g, c.b));\n float minC = min(c.r, min(c.g, c.b));\n float l = (maxC + minC) * 0.5;\n float d = maxC - minC;\n float h = 0.0, s = 0.0;\n if (d > 0.0) {\n s = d / (1.0 - abs(2.0 * l - 1.0));\n if (maxC == c.r) h = mod((c.g - c.b) / d, 6.0) / 6.0;\n else if (maxC == c.g) h = ((c.b - c.r) / d + 2.0) / 6.0;\n else h = ((c.r - c.g) / d + 4.0) / 6.0;\n }\n return vec3(h, s, l);\n}\n\nvec3 hsl2rgb(vec3 c) {\n float h = c.x, s = c.y, l = c.z;\n float a = s * (1.0 - abs(2.0 * l - 1.0));\n vec3 rgb = clamp(abs(mod(h * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);\n return l + a * (rgb - 0.5);\n}\n\nvoid main() {\n vec2 fragCoord = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y); \n vec2 cellCoord = floor(fragCoord / u_cellsize);\n\n if (u_revealEffectFlag == 1) {\n \n float revealThreshold = (cellCoord.x + cellCoord.y) / (u_gridSize.x + u_gridSize.y - 2.0);\n if (revealThreshold > u_revealProgress) {\n fragColor = vec4(0.0, 0.0, 0.0, 1.0);\n return;\n }\n } else if (u_revealEffectFlag == 2) {\n float dist = length(cellCoord / u_gridSize - vec2(0.5));\n float revealThreshold = dist / 0.7071; \n if (revealThreshold > u_revealProgress) {\n fragColor = vec4(0.0, 0.0, 0.0, 1.0);\n return;\n }\n } else if (u_revealEffectFlag == 3) {\n float revealThreshold = hash(cellCoord);\n if (revealThreshold > u_revealProgress) {\n fragColor = vec4(0.0, 0.0, 0.0, 1.0);\n return;\n }\n }\n\n vec2 cellCenter = (cellCoord + 0.5) * u_cellsize; \n \n vec2 rawUV = u_videoMode ? (fragCoord / u_resolution) : (cellCenter / u_resolution);\n vec2 sampleUV = u_cropOffset + rawUV * u_cropScale;\n\n vec3 cellColor = texture(u_texture, sampleUV).rgb;\n float luminosity = dot(cellColor, vec3(0.299, 0.587, 0.114)); \n vec3 hsl = rgb2hsl(cellColor);\n hsl.z = clamp(hsl.z * u_brightness, 0.0, 1.0);\n hsl.y = clamp(hsl.y * u_saturation, 0.0, 1.0);\n cellColor = hsl2rgb(hsl);\n\n vec2 withinCellPos = fract(fragCoord / u_cellsize);\n\n \n float glyphMask = 0.0;\n if (!u_videoMode) {\n uint charInd;\n if (u_shapeMatching) {\n charInd = texelFetch(u_fboTexture, ivec2(cellCoord), 0).r;\n } else {\n charInd = uint(floor(luminosity * (u_numChars - 1.0)));\n }\n float atlasU = (float(charInd) + withinCellPos.x) / u_numChars;\n float atlasV = withinCellPos.y;\n glyphMask = texture(u_atlas, vec2(atlasU, atlasV)).r;\n }\n\n vec3 bgColor = cellColor * u_bgOpacity;\n\n bool scatterHit = false;\n vec3 scatterColor = vec3(0.0);\n if (u_scatterEffect) {\n uint state = texelFetch(u_scatterStateTexture, ivec2(cellCoord), 0).r;\n if (state > 0u) {\n int idx = int(state) - 1;\n float su = (float(idx) + withinCellPos.x) / u_scatterNumChars;\n float mask = texture(u_scatterAtlas, vec2(su, withinCellPos.y)).r;\n \n vec3 scatterBg = u_videoMode ? cellColor : bgColor;\n scatterColor = mix(scatterBg, vec3(1.0), mask);\n scatterHit = true;\n }\n }\n \n if (!scatterHit && u_spreadEffect) { \n uint state = texelFetch(u_spreadStateTexture, ivec2(cellCoord), 0).r;\n if (state > 0u) {\n int idx = int(state) - 1;\n float su = (float(idx) + withinCellPos.x) / u_scatterNumChars;\n float mask = texture(u_scatterAtlas, vec2(su, withinCellPos.y)).r;\n vec3 scatterBg = u_videoMode ? cellColor : bgColor;\n scatterColor = mix(scatterBg, vec3(1.0), mask);\n scatterHit = true;\n }\n }\n\n vec3 finalColor;\n if (scatterHit) {\n finalColor = scatterColor;\n } else {\n finalColor = u_videoMode ? cellColor : mix(bgColor, cellColor, glyphMask);\n\n if (u_mouseEffect) {\n bool inside = false;\n for (int i = 0; i < MOUSE_TRAIL_LEN; i++) {\n if (u_mouseLifeFracs[i] <= 0.0) {\n continue;\n }\n float r = u_mouseRadius * u_mouseLifeFracs[i];\n if (distance(cellCenter, u_mousePositions[i]) < r) {\n inside = true;\n break;\n }\n }\n if (inside) {\n finalColor = clamp(finalColor * u_mouseBrightness, 0.0, 1.0);\n }\n }\n\n if (u_clickEffect) {\n float boost = 1.0;\n for (int i = 0; i < MAX_RIPPLES; i++) {\n if (u_rippleBrightnesses[i] <= 1.0) {\n continue;\n }\n if (distance(cellCenter, u_ripplePositions[i]) < u_rippleRadii[i]) {\n boost *= u_rippleBrightnesses[i];\n }\n }\n if (boost > 1.0) {\n finalColor = clamp(finalColor * boost, 0.0, 1.0);\n }\n }\n }\n\n fragColor = vec4(finalColor, 1.0);\n}", f = "#version 300 es\n\nprecision highp float;\nprecision highp usampler2D;\n\nuniform sampler2D u_texture;\nuniform vec2 u_resolution;\nuniform vec2 u_cellsize;\nuniform int u_circleN;\nuniform sampler2D u_charVectors;\nuniform int u_numCharsInt;\nuniform float u_shapeExponent;\nuniform vec2 u_cropOffset;\nuniform vec2 u_cropScale;\n\nout uvec4 fragCharInd;\n\nvoid main() {\n \n ivec2 cellCoord = ivec2(floor(gl_FragCoord.xy));\n\n const vec2 CIRCLES[6] = vec2[6](\n vec2(0.25, 0.25), vec2(0.75, 0.25),\n vec2(0.25, 0.50), vec2(0.75, 0.50),\n vec2(0.25, 0.75), vec2(0.75, 0.75)\n );\n vec2 radiusUV = vec2(u_cellsize.x / 5.0) / u_resolution * u_cropScale; \n\n float sv[6];\n for (int ci = 0; ci < 6; ci++) {\n \n vec2 centerUV = u_cropOffset + (vec2(cellCoord) + CIRCLES[ci]) * u_cellsize / u_resolution * u_cropScale;\n float total = 0.0;\n int count = 0;\n for (int dx = -u_circleN; dx <= u_circleN; dx++) {\n for (int dy = -u_circleN; dy <= u_circleN; dy++) {\n if (dx*dx + dy*dy <= u_circleN*u_circleN) {\n vec2 sampleUV = centerUV + vec2(float(dx), float(dy)) * radiusUV / float(u_circleN);\n vec3 sc = texture(u_texture, sampleUV).rgb;\n total += dot(sc, vec3(0.299, 0.587, 0.114));\n count++;\n }\n }\n }\n \n sv[ci] = count > 0 ? total / float(count) : 0.0;\n }\n\n float maxVal = 0.0;\n for (int d = 0; d < 6; d++) maxVal = max(maxVal, sv[d]);\n if (maxVal > 0.0) {\n for (int d = 0; d < 6; d++) sv[d] = pow(sv[d] / maxVal, u_shapeExponent) * maxVal;\n }\n\n \n uint bestChar = 0u;\n float bestDist = 1e10;\n for (int i = 0; i < u_numCharsInt; i++) {\n vec4 r0 = texelFetch(u_charVectors, ivec2(i, 0), 0);\n vec4 r1 = texelFetch(u_charVectors, ivec2(i, 1), 0);\n float dist = 0.0;\n float diffs[6] = float[6](\n sv[0] - r0.r, sv[1] - r0.g, sv[2] - r0.b, sv[3] - r0.a,\n sv[4] - r1.r, sv[5] - r1.g\n );\n for (int d = 0; d < 6; d++) {\n dist += diffs[d] * diffs[d];\n }\n if (dist < bestDist) { \n bestDist = dist; bestChar = uint(i); \n }\n }\n\n fragCharInd = uvec4(bestChar, 0u, 0u, 0u);\n}";
|
|
99
|
+
//#endregion
|
|
100
|
+
//#region src/lib/create-gl-resources.ts
|
|
101
|
+
function p(e) {
|
|
102
|
+
let t = c(e, e.VERTEX_SHADER, u), n = c(e, e.FRAGMENT_SHADER, d);
|
|
103
|
+
if (!t || !n) return null;
|
|
104
|
+
let r = l(e, t, n);
|
|
105
|
+
if (!r) return null;
|
|
106
|
+
let i = new Float32Array([
|
|
107
|
+
1,
|
|
108
|
+
1,
|
|
109
|
+
-1,
|
|
110
|
+
1,
|
|
111
|
+
1,
|
|
112
|
+
-1,
|
|
113
|
+
-1,
|
|
114
|
+
-1,
|
|
115
|
+
-1,
|
|
116
|
+
1,
|
|
117
|
+
1,
|
|
118
|
+
-1
|
|
119
|
+
]), a = e.createBuffer();
|
|
120
|
+
e.bindBuffer(e.ARRAY_BUFFER, a), e.bufferData(e.ARRAY_BUFFER, i, e.STATIC_DRAW);
|
|
121
|
+
let o = e.getAttribLocation(r, "a_position");
|
|
122
|
+
e.vertexAttribPointer(o, 2, e.FLOAT, !1, 0, 0), e.enableVertexAttribArray(o), e.useProgram(r);
|
|
123
|
+
let s = e.createTexture();
|
|
124
|
+
e.activeTexture(e.TEXTURE0), e.bindTexture(e.TEXTURE_2D, s), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST);
|
|
125
|
+
let p = e.getUniformLocation(r, "u_texture");
|
|
126
|
+
e.uniform1i(p, 0);
|
|
127
|
+
let m = e.createTexture();
|
|
128
|
+
e.activeTexture(e.TEXTURE1), e.bindTexture(e.TEXTURE_2D, m), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST);
|
|
129
|
+
let h = e.getUniformLocation(r, "u_atlas");
|
|
130
|
+
e.uniform1i(h, 1);
|
|
131
|
+
let g = e.createTexture();
|
|
132
|
+
e.activeTexture(e.TEXTURE2), e.bindTexture(e.TEXTURE_2D, g), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST);
|
|
133
|
+
let _ = c(e, e.FRAGMENT_SHADER, f);
|
|
134
|
+
if (!_) return null;
|
|
135
|
+
let v = l(e, t, _);
|
|
136
|
+
if (!v) return null;
|
|
137
|
+
e.useProgram(v), e.uniform1i(e.getUniformLocation(v, "u_texture"), 0), e.uniform1i(e.getUniformLocation(v, "u_charVectors"), 2);
|
|
138
|
+
let y = e.getUniformLocation(v, "u_resolution"), b = e.getUniformLocation(v, "u_cellsize"), x = e.getUniformLocation(v, "u_circleN"), S = e.getUniformLocation(v, "u_numCharsInt"), C = e.getUniformLocation(v, "u_shapeExponent"), w = e.getUniformLocation(v, "u_cropOffset"), T = e.getUniformLocation(v, "u_cropScale"), E = e.createTexture();
|
|
139
|
+
e.activeTexture(e.TEXTURE3), e.bindTexture(e.TEXTURE_2D, E), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST);
|
|
140
|
+
let D = e.createFramebuffer(), O = e.createTexture();
|
|
141
|
+
e.activeTexture(e.TEXTURE4), e.bindTexture(e.TEXTURE_2D, O), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST);
|
|
142
|
+
let k = e.createTexture();
|
|
143
|
+
e.activeTexture(e.TEXTURE5), e.bindTexture(e.TEXTURE_2D, k), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST);
|
|
144
|
+
let A = e.createTexture();
|
|
145
|
+
return e.activeTexture(e.TEXTURE6), e.bindTexture(e.TEXTURE_2D, A), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.useProgram(r), e.uniform1i(e.getUniformLocation(r, "u_fboTexture"), 3), e.uniform1i(e.getUniformLocation(r, "u_scatterAtlas"), 4), e.uniform1i(e.getUniformLocation(r, "u_scatterStateTexture"), 5), e.uniform1i(e.getUniformLocation(r, "u_spreadStateTexture"), 6), {
|
|
146
|
+
program: r,
|
|
147
|
+
pass1Program: v,
|
|
148
|
+
buffer: a,
|
|
149
|
+
vertShader: t,
|
|
150
|
+
fragShader: n,
|
|
151
|
+
pass1FragShader: _,
|
|
152
|
+
texture: s,
|
|
153
|
+
atlasTexture: m,
|
|
154
|
+
charVectorsTexture: g,
|
|
155
|
+
fboTexture: E,
|
|
156
|
+
fbo: D,
|
|
157
|
+
scatterAtlasTexture: O,
|
|
158
|
+
scatterStateTexture: k,
|
|
159
|
+
spreadStateTexture: A,
|
|
160
|
+
p1ResLoc: y,
|
|
161
|
+
p1CellsizeLoc: b,
|
|
162
|
+
p1CircleNLoc: x,
|
|
163
|
+
p1NumCharsLoc: S,
|
|
164
|
+
p1ExponentLoc: C,
|
|
165
|
+
p1CropOffsetLoc: w,
|
|
166
|
+
p1CropScaleLoc: T,
|
|
167
|
+
revealEffectFlagLoc: e.getUniformLocation(r, "u_revealEffectFlag"),
|
|
168
|
+
mouseEffectFlagLoc: e.getUniformLocation(r, "u_mouseEffect"),
|
|
169
|
+
clickEffectFlagLoc: e.getUniformLocation(r, "u_clickEffect"),
|
|
170
|
+
shapeMatchingLoc: e.getUniformLocation(r, "u_shapeMatching"),
|
|
171
|
+
revealProgressLoc: e.getUniformLocation(r, "u_revealProgress"),
|
|
172
|
+
brightnessLoc: e.getUniformLocation(r, "u_brightness"),
|
|
173
|
+
saturationLoc: e.getUniformLocation(r, "u_saturation"),
|
|
174
|
+
bgOpacityLoc: e.getUniformLocation(r, "u_bgOpacity"),
|
|
175
|
+
mouseBrightnessLoc: e.getUniformLocation(r, "u_mouseBrightness"),
|
|
176
|
+
mousePositionsLoc: e.getUniformLocation(r, "u_mousePositions"),
|
|
177
|
+
mouseLifeFracsLoc: e.getUniformLocation(r, "u_mouseLifeFracs"),
|
|
178
|
+
mouseRadiusLoc: e.getUniformLocation(r, "u_mouseRadius"),
|
|
179
|
+
ripplePositionsLoc: e.getUniformLocation(r, "u_ripplePositions"),
|
|
180
|
+
rippleRadiiLoc: e.getUniformLocation(r, "u_rippleRadii"),
|
|
181
|
+
rippleBrightnessesLoc: e.getUniformLocation(r, "u_rippleBrightnesses"),
|
|
182
|
+
scatterEffectFlagLoc: e.getUniformLocation(r, "u_scatterEffect"),
|
|
183
|
+
scatterNumCharsLoc: e.getUniformLocation(r, "u_scatterNumChars"),
|
|
184
|
+
spreadEffectFlagLoc: e.getUniformLocation(r, "u_spreadEffect"),
|
|
185
|
+
videoModeLoc: e.getUniformLocation(r, "u_videoMode"),
|
|
186
|
+
resLoc: e.getUniformLocation(r, "u_resolution"),
|
|
187
|
+
sizeLoc: e.getUniformLocation(r, "u_cellsize"),
|
|
188
|
+
numLoc: e.getUniformLocation(r, "u_numChars"),
|
|
189
|
+
gridSizeLoc: e.getUniformLocation(r, "u_gridSize"),
|
|
190
|
+
cropOffsetLoc: e.getUniformLocation(r, "u_cropOffset"),
|
|
191
|
+
cropScaleLoc: e.getUniformLocation(r, "u_cropScale")
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/lib/scatter-effect.ts
|
|
196
|
+
function m({ scatterEnabledRef: e, mouseRadiusRef: t, durationRef: n, scatterCharsRef: r }) {
|
|
197
|
+
let i = new Uint8Array(), a = new Float32Array(), o = new Float32Array(), s = new Uint8Array(), c = null, l = null, u = 0, d = 0, f = -1, p = () => Math.floor(Math.random() * r.current.length), m = 0, h = 0, g = 1, _ = 1, v = null;
|
|
198
|
+
return {
|
|
199
|
+
setup(e, t, n, r, f, p) {
|
|
200
|
+
m = t, h = n, g = r, _ = f, v = p;
|
|
201
|
+
let y = t * n;
|
|
202
|
+
i = new Uint8Array(y), a = new Float32Array(y), o = new Float32Array(y), s = new Uint8Array(y), c = null, l = null, u = 0, d = 0, e.activeTexture(e.TEXTURE5), e.bindTexture(e.TEXTURE_2D, p), e.texImage2D(e.TEXTURE_2D, 0, e.R8UI, t, n, 0, e.RED_INTEGER, e.UNSIGNED_BYTE, i);
|
|
203
|
+
},
|
|
204
|
+
handleMouseMove(t, n, r) {
|
|
205
|
+
e.current && (l = c, c = {
|
|
206
|
+
x: t,
|
|
207
|
+
y: n,
|
|
208
|
+
t: r
|
|
209
|
+
});
|
|
210
|
+
},
|
|
211
|
+
handleMouseLeave() {
|
|
212
|
+
c = null, l = null, u = 0, d = 0;
|
|
213
|
+
},
|
|
214
|
+
reset(e) {
|
|
215
|
+
i.fill(0), v && m > 0 && (e.activeTexture(e.TEXTURE5), e.bindTexture(e.TEXTURE_2D, v), e.texSubImage2D(e.TEXTURE_2D, 0, 0, 0, m, h, e.RED_INTEGER, e.UNSIGNED_BYTE, i));
|
|
216
|
+
},
|
|
217
|
+
tick(r, y) {
|
|
218
|
+
if (!e.current || m === 0 || h === 0) return;
|
|
219
|
+
let b = performance.now(), x = f < 0 ? 0 : Math.min(.1, (b - f) / 1e3);
|
|
220
|
+
f = b;
|
|
221
|
+
let S = Math.min(y.width, y.height) * t.current, C = !1;
|
|
222
|
+
if (c && b - c.t < 80) {
|
|
223
|
+
if (l) {
|
|
224
|
+
let e = (c.t - l.t) / 1e3;
|
|
225
|
+
if (e > 0 && e < .2) {
|
|
226
|
+
let t = (c.x - l.x) / e, n = (c.y - l.y) / e;
|
|
227
|
+
u = u * .7 + t * .3, d = d * .7 + n * .3;
|
|
228
|
+
}
|
|
229
|
+
} else u = 0, d = 0;
|
|
230
|
+
Math.hypot(u, d) > g * 2 && (C = !0);
|
|
231
|
+
} else u *= .6, d *= .6;
|
|
232
|
+
if (s.fill(0), C && c && S > 0) {
|
|
233
|
+
let e = c.x, t = c.y, n = Math.max(0, Math.floor((e - S) / g)), r = Math.min(m - 1, Math.floor((e + S) / g)), l = Math.max(0, Math.floor((t - S) / _)), u = Math.min(h - 1, Math.floor((t + S) / _));
|
|
234
|
+
for (let c = l; c <= u; c++) for (let l = n; l <= r; l++) {
|
|
235
|
+
let n = (l + .5) * g, r = (c + .5) * _;
|
|
236
|
+
if (Math.hypot(n - e, r - t) > S) continue;
|
|
237
|
+
let u = c * m + l;
|
|
238
|
+
s[u] = 1, i[u] === 0 && (i[u] = 1 + p(), o[u] = .5 + Math.random() * 2), a[u] = 1;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
let w = n.current;
|
|
242
|
+
if (x > 0) for (let e = 0; e < h; e++) for (let t = 0; t < m; t++) {
|
|
243
|
+
let n = e * m + t;
|
|
244
|
+
if (i[n] === 0 || s[n]) continue;
|
|
245
|
+
let r = t > 0 ? i[n - 1] !== 0 : !1, c = t < m - 1 ? i[n + 1] !== 0 : !1, l = e > 0 ? i[n - m] !== 0 : !1, u = e < h - 1 ? i[n + m] !== 0 : !1, d = (!r || !c || !l || !u ? 5 : 1) * o[n] / w;
|
|
246
|
+
a[n] -= d * x, a[n] <= 0 && (i[n] = 0);
|
|
247
|
+
}
|
|
248
|
+
r.activeTexture(r.TEXTURE5), r.bindTexture(r.TEXTURE_2D, v), r.texSubImage2D(r.TEXTURE_2D, 0, 0, 0, m, h, r.RED_INTEGER, r.UNSIGNED_BYTE, i);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
//#endregion
|
|
253
|
+
//#region src/lib/brighten-effect.ts
|
|
254
|
+
function h({ brightenEnabledRef: e, trailLenRef: t, durationRef: n, trailDecayRef: r }) {
|
|
255
|
+
let i = [];
|
|
256
|
+
return {
|
|
257
|
+
handleMouseMove(n, r, a) {
|
|
258
|
+
e.current && (i.push({
|
|
259
|
+
x: n,
|
|
260
|
+
y: r,
|
|
261
|
+
t: a
|
|
262
|
+
}), i.length > t.current && i.shift());
|
|
263
|
+
},
|
|
264
|
+
tick(a, o, s) {
|
|
265
|
+
if (!e.current) return;
|
|
266
|
+
let c = performance.now(), l = new Float32Array(t.current * 2), u = new Float32Array(t.current);
|
|
267
|
+
for (let e = 0; e < i.length; e++) {
|
|
268
|
+
let t = c - i[e].t, a = Math.max(0, 1 - t / (n.current * 1e3)) ** r.current;
|
|
269
|
+
l[e * 2] = i[e].x, l[e * 2 + 1] = i[e].y, u[e] = a;
|
|
270
|
+
}
|
|
271
|
+
a.uniform2fv(o, l), a.uniform1fv(s, u);
|
|
272
|
+
}
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/lib/click-effect.ts
|
|
277
|
+
var g = 10;
|
|
278
|
+
function _({ clickEnabledRef: e, clickSpeedRef: t, clickBrightnessRef: n }) {
|
|
279
|
+
let r = [];
|
|
280
|
+
return {
|
|
281
|
+
handleClick(t, n) {
|
|
282
|
+
if (!e.current) return;
|
|
283
|
+
let i = n.getBoundingClientRect(), a = (t.clientX - i.left) * (n.width / i.width), o = (t.clientY - i.top) * (n.height / i.height);
|
|
284
|
+
r.push({
|
|
285
|
+
x: a,
|
|
286
|
+
y: o,
|
|
287
|
+
t: performance.now()
|
|
288
|
+
}), r.length > g && r.shift();
|
|
289
|
+
},
|
|
290
|
+
tick(i, a, o, s, c) {
|
|
291
|
+
if (!e.current) return;
|
|
292
|
+
let l = performance.now(), u = Math.hypot(a.width, a.height), d = new Float32Array(g * 2), f = new Float32Array(g), p = new Float32Array(g);
|
|
293
|
+
for (; r.length > 0 && (l - r[0].t) * t.current >= u;) r.shift();
|
|
294
|
+
for (let e = 0; e < r.length; e++) {
|
|
295
|
+
let i = (l - r[e].t) * t.current, a = i / u, o = 1 + (n.current - 1) * (1 - a ** 2);
|
|
296
|
+
d[e * 2] = r[e].x, d[e * 2 + 1] = r[e].y, f[e] = i, p[e] = o;
|
|
297
|
+
}
|
|
298
|
+
i.uniform2fv(o, d), i.uniform1fv(s, f), i.uniform1fv(c, p);
|
|
299
|
+
}
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
//#endregion
|
|
303
|
+
//#region src/lib/spread-effect.ts
|
|
304
|
+
function v({ spreadEnabledRef: e, scatterCharsRef: t, spreadExpandDurationRef: n, spreadSpeedRef: r }) {
|
|
305
|
+
let i = new Uint8Array(), a = new Uint8Array(), o = 0, s = 0, c = 1, l = 1, u = null, d = -1, f = -1, p = () => Math.floor(Math.random() * t.current.length), m = [
|
|
306
|
+
[-1, 0],
|
|
307
|
+
[1, 0],
|
|
308
|
+
[0, -1],
|
|
309
|
+
[0, 1]
|
|
310
|
+
];
|
|
311
|
+
return {
|
|
312
|
+
setup(e, t, n, r, p, m) {
|
|
313
|
+
o = t, s = n, c = r, l = p, u = m;
|
|
314
|
+
let h = t * n;
|
|
315
|
+
i = new Uint8Array(h), a = new Uint8Array(h), f = -1, d = -1, e.activeTexture(e.TEXTURE6), e.bindTexture(e.TEXTURE_2D, m), e.texImage2D(e.TEXTURE_2D, 0, e.R8UI, t, n, 0, e.RED_INTEGER, e.UNSIGNED_BYTE, i);
|
|
316
|
+
},
|
|
317
|
+
handleClick(t, r) {
|
|
318
|
+
if (!e.current || o === 0) return;
|
|
319
|
+
let u = r.getBoundingClientRect(), d = (t.clientX - u.left) * (r.width / u.width), m = (t.clientY - u.top) * (r.height / u.height), h = Math.floor(d / c), g = Math.floor(m / l);
|
|
320
|
+
if (h < 0 || h >= o || g < 0 || g >= s) return;
|
|
321
|
+
let _ = g * o + h;
|
|
322
|
+
i[_] = 1 + p(), a[_] = 1, f = performance.now() + n.current * 1e3;
|
|
323
|
+
},
|
|
324
|
+
tick(t) {
|
|
325
|
+
if (!e.current || o === 0 || s === 0) return;
|
|
326
|
+
let n = performance.now(), c = d < 0 ? 0 : Math.min(.1, (n - d) / 1e3);
|
|
327
|
+
d = n;
|
|
328
|
+
let l = f > 0 && n < f;
|
|
329
|
+
if (!l && f > 0) {
|
|
330
|
+
for (let e = 0; e < i.length; e++) a[e] === 1 && (a[e] = 2);
|
|
331
|
+
f = -1;
|
|
332
|
+
}
|
|
333
|
+
if (c > 0) for (let e = 0; e < s; e++) for (let t = 0; t < o; t++) {
|
|
334
|
+
let n = e * o + t;
|
|
335
|
+
if (a[n] !== 0) {
|
|
336
|
+
if (l && a[n] === 1) for (let [n, l] of m) {
|
|
337
|
+
let u = e + n, d = t + l;
|
|
338
|
+
if (u < 0 || u >= s || d < 0 || d >= o) continue;
|
|
339
|
+
let f = u * o + d;
|
|
340
|
+
a[f] === 0 && Math.random() < r.current * c && (i[f] = 1 + p(), a[f] = 1);
|
|
341
|
+
}
|
|
342
|
+
else if (a[n] === 2) for (let [l, u] of m) {
|
|
343
|
+
let d = e + l, f = t + u;
|
|
344
|
+
if (d < 0 || d >= s || f < 0 || f >= o || i[d * o + f] === 0) {
|
|
345
|
+
Math.random() < r.current * c && (i[n] = 0, a[n] = 0);
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
t.activeTexture(t.TEXTURE6), t.bindTexture(t.TEXTURE_2D, u), t.texSubImage2D(t.TEXTURE_2D, 0, 0, 0, o, s, t.RED_INTEGER, t.UNSIGNED_BYTE, i);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region src/components/VideoAscii.tsx
|
|
357
|
+
function y({ src: i, videoMode: c = !1, numColsRaw: l = 250, brightnessRaw: u = 1, saturationRaw: d = 1, bgOpacityRaw: f = .3, revealEffect: g = !1, chars: y = o, mouseEffect: b = !0, clickEffect: x = !0, charMode: S = "shape", className: C }) {
|
|
358
|
+
let w = t(null), T = t(null), E = t(null), D = t(null), O = t(null), { numCols: k, brightness: A, saturation: j, bgOpacity: M, mouseEnabled: N, mouseStyle: P, brightenEnabled: F, scatterEnabled: I, scatterChars: L, trailLen: R, trailDecay: z, duration: B, mouseRadius: V, mouseBrightness: H, clickEnabled: U, clickBrightness: W, clickSpeed: G, spreadEnabled: K, spreadExpandDuration: q, spreadSpeed: ee, revealEnabled: te, revealDuration: ne, revealEffectFlag: re } = s(l, u, d, f, b, x, g), ie = t(A), ae = t(j), oe = t(M), se = t(N), ce = t(P), J = t(F), Y = t(I), le = t(H), X = t(V), ue = t(R), de = t(z), Z = t(B), Q = t(L), fe = t(U), pe = t(W), me = t(G), he = t(K), ge = t(q), _e = t(ee), ve = t(k), ye = t(c);
|
|
359
|
+
e(() => {
|
|
360
|
+
ie.current = A, ae.current = j, oe.current = M, se.current = N, ce.current = P, J.current = F, Y.current = I, le.current = H, X.current = V, ue.current = R, de.current = z, Z.current = B, Q.current !== L && $.current ? (Q.current = L, xe.current?.()) : Q.current = L, fe.current = U, pe.current = W, me.current = G, he.current = K, ge.current = q, _e.current = ee, ve.current = k, ye.current = c;
|
|
361
|
+
}, [
|
|
362
|
+
A,
|
|
363
|
+
j,
|
|
364
|
+
M,
|
|
365
|
+
N,
|
|
366
|
+
P,
|
|
367
|
+
F,
|
|
368
|
+
I,
|
|
369
|
+
H,
|
|
370
|
+
V,
|
|
371
|
+
R,
|
|
372
|
+
z,
|
|
373
|
+
B,
|
|
374
|
+
L,
|
|
375
|
+
U,
|
|
376
|
+
W,
|
|
377
|
+
G,
|
|
378
|
+
K,
|
|
379
|
+
q,
|
|
380
|
+
ee,
|
|
381
|
+
k,
|
|
382
|
+
c
|
|
383
|
+
]);
|
|
384
|
+
let be = t(null), xe = t(null), $ = t(!1);
|
|
385
|
+
return e(() => {
|
|
386
|
+
$.current && be.current?.(k);
|
|
387
|
+
}, [k]), e(() => {
|
|
388
|
+
$.current = !1;
|
|
389
|
+
let e = [], t = 0, n = 0, r = 1, o = 1, s = w.current;
|
|
390
|
+
if (!s) return;
|
|
391
|
+
let c = T.current;
|
|
392
|
+
if (!c) return;
|
|
393
|
+
let l = s.getContext("webgl2");
|
|
394
|
+
if (!l) return;
|
|
395
|
+
l.pixelStorei(l.UNPACK_ALIGNMENT, 1);
|
|
396
|
+
let u = p(l);
|
|
397
|
+
if (!u) return;
|
|
398
|
+
let { program: d, pass1Program: f } = u;
|
|
399
|
+
D.current = u.atlasTexture, O.current = u.scatterAtlasTexture, l.uniform1i(u.shapeMatchingLoc, +(S === "shape")), l.uniform1i(u.revealEffectFlagLoc, re), l.uniform1f(u.revealProgressLoc, 0);
|
|
400
|
+
let g = m({
|
|
401
|
+
scatterEnabledRef: Y,
|
|
402
|
+
mouseRadiusRef: X,
|
|
403
|
+
durationRef: Z,
|
|
404
|
+
scatterCharsRef: Q
|
|
405
|
+
}), b = h({
|
|
406
|
+
brightenEnabledRef: J,
|
|
407
|
+
trailLenRef: ue,
|
|
408
|
+
durationRef: Z,
|
|
409
|
+
trailDecayRef: de
|
|
410
|
+
}), x = _({
|
|
411
|
+
clickEnabledRef: fe,
|
|
412
|
+
clickSpeedRef: me,
|
|
413
|
+
clickBrightnessRef: pe
|
|
414
|
+
}), C = v({
|
|
415
|
+
spreadEnabledRef: he,
|
|
416
|
+
scatterCharsRef: Q,
|
|
417
|
+
spreadExpandDurationRef: ge,
|
|
418
|
+
spreadSpeedRef: _e
|
|
419
|
+
}), k, A = -1, j = -1, M = 0, N = Array.isArray(i) ? i : [i], P = N.length > 1, F = document.createElement("canvas"), I = F.getContext("2d"), L = (i) => {
|
|
420
|
+
r = Math.max(1, Math.floor(s.width / i));
|
|
421
|
+
let c = r * 2;
|
|
422
|
+
if (I.font = `${c}px monospace`, o = Math.max(1, Math.round(c * r / I.measureText("M").width)), t = Math.floor(s.width / r), n = Math.floor(s.height / o), s.width = t * r, s.height = n * o, l.viewport(0, 0, s.width, s.height), l.useProgram(d), l.uniform2f(u.resLoc, s.width, s.height), l.useProgram(f), l.uniform2f(u.p1ResLoc, s.width, s.height), l.useProgram(d), g.setup(l, t, n, r, o, u.scatterStateTexture), C.setup(l, t, n, r, o, u.spreadStateTexture), S === "shape") {
|
|
423
|
+
e = a(y, r, o);
|
|
424
|
+
let i = e.length, s = new Float32Array(i * 8);
|
|
425
|
+
for (let t = 0; t < i; t++) {
|
|
426
|
+
let n = e[t].vector;
|
|
427
|
+
s[t * 4 + 0] = n[0], s[t * 4 + 1] = n[1], s[t * 4 + 2] = n[2], s[t * 4 + 3] = n[3], s[i * 4 + t * 4 + 0] = n[4], s[i * 4 + t * 4 + 1] = n[5];
|
|
428
|
+
}
|
|
429
|
+
l.activeTexture(l.TEXTURE2), l.bindTexture(l.TEXTURE_2D, u.charVectorsTexture), l.texImage2D(l.TEXTURE_2D, 0, l.RGBA32F, i, 2, 0, l.RGBA, l.FLOAT, s), l.activeTexture(l.TEXTURE3), l.bindTexture(l.TEXTURE_2D, u.fboTexture), l.texImage2D(l.TEXTURE_2D, 0, l.R8UI, t, n, 0, l.RED_INTEGER, l.UNSIGNED_BYTE, null), l.bindFramebuffer(l.FRAMEBUFFER, u.fbo), l.framebufferTexture2D(l.FRAMEBUFFER, l.COLOR_ATTACHMENT0, l.TEXTURE_2D, u.fboTexture, 0), l.bindFramebuffer(l.FRAMEBUFFER, null);
|
|
430
|
+
let c = Math.max(1, Math.round(r / 5));
|
|
431
|
+
l.useProgram(f), l.uniform2f(u.p1CellsizeLoc, r, o), l.uniform1i(u.p1CircleNLoc, c), l.uniform1i(u.p1NumCharsLoc, i), l.uniform1f(u.p1ExponentLoc, 2), l.useProgram(d);
|
|
432
|
+
}
|
|
433
|
+
l.uniform2f(u.gridSizeLoc, t, n), F.width = y.length * r, F.height = o, I.font = `${o}px monospace`, I.fillStyle = "black", I.fillRect(0, 0, y.length * r, o), I.fillStyle = "white", I.textBaseline = "top";
|
|
434
|
+
for (let e = 0; e < y.length; e++) I.fillText(y[e], e * r, 0);
|
|
435
|
+
l.activeTexture(l.TEXTURE1), l.bindTexture(l.TEXTURE_2D, D.current), l.texImage2D(l.TEXTURE_2D, 0, l.RGBA, l.RGBA, l.UNSIGNED_BYTE, F), l.uniform1f(u.numLoc, y.length), l.uniform2f(u.sizeLoc, r, o), R();
|
|
436
|
+
}, R = () => {
|
|
437
|
+
let e = Q.current;
|
|
438
|
+
F.width = e.length * r, F.height = o, I.fillStyle = "black", I.fillRect(0, 0, F.width, o), I.fillStyle = "white", I.textBaseline = "top", I.font = `${o}px monospace`;
|
|
439
|
+
for (let t = 0; t < e.length; t++) I.fillText(e[t], t * r, 0);
|
|
440
|
+
l.activeTexture(l.TEXTURE4), l.bindTexture(l.TEXTURE_2D, O.current), l.texImage2D(l.TEXTURE_2D, 0, l.RGBA, l.RGBA, l.UNSIGNED_BYTE, F), l.uniform1f(u.scatterNumCharsLoc, e.length), g.reset(l);
|
|
441
|
+
};
|
|
442
|
+
be.current = L, xe.current = R;
|
|
443
|
+
let z = (e, t) => {
|
|
444
|
+
s.width = Math.round(e), s.height = Math.round(t), L(ve.current);
|
|
445
|
+
let n = c.videoWidth / c.videoHeight, r = s.width / s.height, i = 1, a = 1, o = 0, p = 0;
|
|
446
|
+
r > n ? (a = n / r, p = (1 - a) / 2) : (i = r / n, o = (1 - i) / 2), l.uniform2f(u.cropOffsetLoc, o, p), l.uniform2f(u.cropScaleLoc, i, a), l.useProgram(f), l.uniform2f(u.p1CropOffsetLoc, o, p), l.uniform2f(u.p1CropScaleLoc, i, a), l.useProgram(d);
|
|
447
|
+
}, B = (e) => {
|
|
448
|
+
if (!se.current) return;
|
|
449
|
+
let t = s.getBoundingClientRect(), n = (e.clientX - t.left) * (s.width / t.width), r = (e.clientY - t.top) * (s.height / t.height), i = performance.now();
|
|
450
|
+
b.handleMouseMove(n, r, i), g.handleMouseMove(n, r, i);
|
|
451
|
+
};
|
|
452
|
+
s.addEventListener("mousemove", B);
|
|
453
|
+
let V = () => g.handleMouseLeave();
|
|
454
|
+
s.addEventListener("mouseleave", V);
|
|
455
|
+
let H = (e) => {
|
|
456
|
+
x.handleClick(e, s), C.handleClick(e, s);
|
|
457
|
+
};
|
|
458
|
+
s.addEventListener("click", H);
|
|
459
|
+
let U = () => {
|
|
460
|
+
if (l.uniform1i(u.videoModeLoc, +!!ye.current), l.uniform1f(u.brightnessLoc, ie.current), l.uniform1f(u.saturationLoc, ae.current), l.uniform1f(u.bgOpacityLoc, oe.current), l.uniform1i(u.mouseEffectFlagLoc, +!!J.current), l.uniform1i(u.scatterEffectFlagLoc, +!!Y.current), l.uniform1i(u.clickEffectFlagLoc, +!!fe.current), l.uniform1i(u.spreadEffectFlagLoc, +!!he.current), l.uniform1f(u.mouseBrightnessLoc, le.current), l.uniform1f(u.mouseRadiusLoc, Math.min(s.width, s.height) * X.current), te) {
|
|
461
|
+
let e = j < 0 ? 0 : Math.min(1, (performance.now() - j) / (ne * 1e3));
|
|
462
|
+
l.uniform1f(u.revealProgressLoc, e);
|
|
463
|
+
}
|
|
464
|
+
$.current && c.currentTime != A && c.readyState >= 2 && (l.activeTexture(l.TEXTURE0), l.bindTexture(l.TEXTURE_2D, u.texture), l.texSubImage2D(l.TEXTURE_2D, 0, 0, 0, l.RGBA, l.UNSIGNED_BYTE, c), A = c.currentTime), b.tick(l, u.mousePositionsLoc, u.mouseLifeFracsLoc), g.tick(l, s), x.tick(l, s, u.ripplePositionsLoc, u.rippleRadiiLoc, u.rippleBrightnessesLoc), C.tick(l), S === "shape" && (l.bindFramebuffer(l.FRAMEBUFFER, u.fbo), l.viewport(0, 0, t, n), l.useProgram(f), l.drawArrays(l.TRIANGLES, 0, 6), l.bindFramebuffer(l.FRAMEBUFFER, null), l.viewport(0, 0, s.width, s.height), l.useProgram(d)), l.drawArrays(l.TRIANGLES, 0, 6), k = requestAnimationFrame(U);
|
|
465
|
+
}, W = () => {
|
|
466
|
+
let e = E.current;
|
|
467
|
+
z(e.clientWidth || c.videoWidth, e.clientHeight || c.videoHeight), l.activeTexture(l.TEXTURE0), l.bindTexture(l.TEXTURE_2D, u.texture), l.texImage2D(l.TEXTURE_2D, 0, l.RGBA, l.RGBA, l.UNSIGNED_BYTE, c), c.play(), j = performance.now(), $.current = !0, k = requestAnimationFrame(U);
|
|
468
|
+
}, G = () => {
|
|
469
|
+
M = (M + 1) % N.length, c.src = N[M], c.load(), c.addEventListener("canplay", () => c.play(), { once: !0 });
|
|
470
|
+
}, K = new ResizeObserver((e) => {
|
|
471
|
+
let { width: t, height: n } = e[0].contentRect;
|
|
472
|
+
$.current && t > 0 && n > 0 && z(t, n);
|
|
473
|
+
});
|
|
474
|
+
return E.current && K.observe(E.current), c.addEventListener("loadeddata", W, { once: !0 }), P && c.addEventListener("ended", G), c.readyState >= 2 && c.currentSrc.endsWith(N[0]) ? W() : c.currentSrc.endsWith(N[0]) || c.load(), () => {
|
|
475
|
+
K.disconnect(), be.current = null, xe.current = null, $.current = !1, cancelAnimationFrame(k), c.removeEventListener("loadeddata", W), P && c.removeEventListener("ended", G), s.removeEventListener("mousemove", B), s.removeEventListener("mouseleave", V), s.removeEventListener("click", H), l.deleteTexture(u.texture), l.deleteTexture(u.charVectorsTexture), l.deleteFramebuffer(u.fbo), l.deleteTexture(u.fboTexture), l.deleteBuffer(u.buffer), l.deleteShader(u.vertShader), l.deleteShader(u.fragShader), l.deleteShader(u.pass1FragShader), l.deleteProgram(d), l.deleteProgram(f), l.deleteTexture(D.current), l.deleteTexture(O.current), l.deleteTexture(u.scatterStateTexture), l.deleteTexture(u.spreadStateTexture);
|
|
476
|
+
};
|
|
477
|
+
}, [
|
|
478
|
+
i,
|
|
479
|
+
S,
|
|
480
|
+
y,
|
|
481
|
+
re,
|
|
482
|
+
ne,
|
|
483
|
+
te
|
|
484
|
+
]), /* @__PURE__ */ r("div", {
|
|
485
|
+
ref: E,
|
|
486
|
+
className: C,
|
|
487
|
+
style: {
|
|
488
|
+
height: "100%",
|
|
489
|
+
width: "100%"
|
|
490
|
+
},
|
|
491
|
+
children: [/* @__PURE__ */ n("video", {
|
|
492
|
+
ref: T,
|
|
493
|
+
muted: !0,
|
|
494
|
+
playsInline: !0,
|
|
495
|
+
autoPlay: !0,
|
|
496
|
+
loop: !Array.isArray(i) || i.length === 1,
|
|
497
|
+
style: { display: "none" },
|
|
498
|
+
children: /* @__PURE__ */ n("source", {
|
|
499
|
+
src: Array.isArray(i) ? i[0] : i,
|
|
500
|
+
type: "video/mp4"
|
|
501
|
+
})
|
|
502
|
+
}), /* @__PURE__ */ n("canvas", {
|
|
503
|
+
ref: w,
|
|
504
|
+
style: {
|
|
505
|
+
width: "100%",
|
|
506
|
+
height: "100%",
|
|
507
|
+
display: "block"
|
|
508
|
+
}
|
|
509
|
+
})]
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
//#endregion
|
|
513
|
+
export { y as VideoAscii };
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
(function(e,t){typeof exports==`object`&&typeof module<`u`?t(exports,require(`react`),require(`react/jsx-runtime`)):typeof define==`function`&&define.amd?define([`exports`,`react`,`react/jsx-runtime`],t):(e=typeof globalThis<`u`?globalThis:e||self,t(e.ReactVideoAscii={},e.React,e.ReactJSXRuntime))})(this,function(e,t,n){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var r=[[.25,.25],[.75,.25],[.25,.5],[.75,.5],[.25,.75],[.75,.75]];function i(e,t,n){let i=[],a=document.createElement(`canvas`);a.width=t,a.height=n;let o=a.getContext(`2d`,{willReadFrequently:!0});if(!o)return[];o.font=`${n}px monospace`,o.textBaseline=`top`;let s=t/5;for(let a of e){o.fillStyle=`black`,o.fillRect(0,0,t,n),o.fillStyle=`white`,o.fillText(a,0,0);let e=o.getImageData(0,0,t,n),c=[];for(let[i,a]of r){let r=i*t,o=a*n,l=0,u=0;for(let n=Math.floor(r-s);n<=Math.ceil(r+s);n+=1)for(let i=Math.floor(o-s);i<=Math.ceil(o+s);i+=1){let a=r-n,c=o-i;if(a*a+c*c<=s*s){let r=(i*t+n)*4,a=e.data[r];l+=a/255,u++}}let d=u>0?l/u:0;c.push(d)}i.push({char:a,vector:c})}let c=[,,,,,,].fill(0);for(let{vector:e}of i)for(let t=0;t<6;++t)e[t]>c[t]&&(c[t]=e[t]);for(let{vector:e}of i)for(let t=0;t<6;++t)e[t]/=c[t];return i}var a=` \`.',-_:!;|"~+^lr[](\\/L)>t<v=Tz?icf1{sIxY*jJno}CZyVwmSXRqM$O%#9&NW0Q@`;function o(e,t,n,r,i,a,o){let s=!!i,c=typeof i==`object`?i:{},l=c.style??`brighten`,u=s&&l!==`scatter`,d=s&&l===`scatter`,f=c.scatterChars??`->o`,p=c.trailLen??15,m=c.trailDecay??10,h=c.duration??1,g=c.radius??(l===`scatter`?.05:.08),_=c.brightness??2,v=!!a,y=typeof a==`object`?a:{},b=y.style??`ripple`,x=v&&b!==`spread`,S=v&&b===`spread`,C=y.brightness??1.1,w=y.speed??2,T=y.spreadExpandDuration??1.5,E=y.spreadSpeed??7.5,D=!!o,O=typeof o==`object`?o:{},k=O.type??`random`,A=O.duration??.4,j=Math.max(20,Math.min(350,Math.round(e))),M=Math.max(0,Math.min(2,t)),N=Math.max(0,Math.min(2,n)),P=Math.max(0,Math.min(1,r));A=Math.max(.1,Math.min(4,A)),p=Math.max(0,Math.min(30,Math.round(p))),m=Math.max(1,Math.min(15,m)),h=Math.max(.1,Math.min(4,h)),g=Math.max(.03,Math.min(.2,g)),_=Math.max(.2,Math.min(5,_)),C=Math.max(1.05,Math.min(2,C)),w=Math.max(.5,Math.min(4,w)),T=Math.max(.5,Math.min(5,T)),E=Math.max(.5,Math.min(10,E));let F;return F=D?k===`diagonal`?1:k===`radial`?2:3:0,{numCols:j,brightness:M,saturation:N,bgOpacity:P,mouseEnabled:s,mouseStyle:l,brightenEnabled:u,scatterEnabled:d,scatterChars:f,trailLen:p,trailDecay:m,duration:h,mouseRadius:g,mouseBrightness:_,clickEnabled:x,clickBrightness:C,clickSpeed:w,spreadEnabled:S,spreadExpandDuration:T,spreadSpeed:E,revealEnabled:D,revealType:k,revealDuration:A,revealEffectFlag:F}}function s(e,t,n){let r=e.createShader(t);if(!r)throw Error(`Failed to create shader`);return e.shaderSource(r,n),e.compileShader(r),e.getShaderParameter(r,e.COMPILE_STATUS)?r:(console.log(e.getShaderInfoLog(r)),e.deleteShader(r),null)}function c(e,t,n){let r=e.createProgram();return e.attachShader(r,t),e.attachShader(r,n),e.linkProgram(r),e.getProgramParameter(r,e.LINK_STATUS)?r:(console.log(e.getProgramInfoLog(r)),e.deleteProgram(r),null)}var l=`#version 300 es
|
|
2
|
+
|
|
3
|
+
in vec2 a_position;
|
|
4
|
+
|
|
5
|
+
void main() {
|
|
6
|
+
gl_Position = vec4(a_position, 0.0, 1.0);
|
|
7
|
+
}`,u=`#version 300 es
|
|
8
|
+
|
|
9
|
+
precision highp float;
|
|
10
|
+
precision highp usampler2D;
|
|
11
|
+
|
|
12
|
+
uniform int u_revealEffectFlag;
|
|
13
|
+
uniform bool u_videoMode;
|
|
14
|
+
|
|
15
|
+
uniform float u_revealProgress;
|
|
16
|
+
uniform float u_brightness;
|
|
17
|
+
uniform float u_saturation;
|
|
18
|
+
uniform float u_bgOpacity;
|
|
19
|
+
|
|
20
|
+
uniform bool u_mouseEffect;
|
|
21
|
+
#define MOUSE_TRAIL_LEN 30
|
|
22
|
+
uniform vec2 u_mousePositions[MOUSE_TRAIL_LEN];
|
|
23
|
+
uniform float u_mouseLifeFracs[MOUSE_TRAIL_LEN];
|
|
24
|
+
uniform float u_mouseRadius;
|
|
25
|
+
uniform float u_mouseBrightness;
|
|
26
|
+
|
|
27
|
+
uniform bool u_clickEffect;
|
|
28
|
+
#define MAX_RIPPLES 10
|
|
29
|
+
uniform vec2 u_ripplePositions[MAX_RIPPLES];
|
|
30
|
+
uniform float u_rippleRadii[MAX_RIPPLES];
|
|
31
|
+
uniform float u_rippleBrightnesses[MAX_RIPPLES];
|
|
32
|
+
|
|
33
|
+
uniform bool u_scatterEffect;
|
|
34
|
+
uniform usampler2D u_scatterStateTexture;
|
|
35
|
+
uniform sampler2D u_scatterAtlas;
|
|
36
|
+
uniform float u_scatterNumChars;
|
|
37
|
+
|
|
38
|
+
uniform bool u_spreadEffect;
|
|
39
|
+
uniform usampler2D u_spreadStateTexture;
|
|
40
|
+
|
|
41
|
+
uniform bool u_shapeMatching;
|
|
42
|
+
|
|
43
|
+
uniform sampler2D u_texture;
|
|
44
|
+
uniform sampler2D u_atlas;
|
|
45
|
+
uniform usampler2D u_fboTexture;
|
|
46
|
+
uniform vec2 u_resolution;
|
|
47
|
+
uniform vec2 u_cellsize;
|
|
48
|
+
uniform float u_numChars;
|
|
49
|
+
uniform vec2 u_gridSize;
|
|
50
|
+
uniform vec2 u_cropOffset;
|
|
51
|
+
uniform vec2 u_cropScale;
|
|
52
|
+
|
|
53
|
+
out vec4 fragColor;
|
|
54
|
+
|
|
55
|
+
float hash(vec2 p) {
|
|
56
|
+
vec3 p3 = fract(vec3(p.xyx) * 0.1031);
|
|
57
|
+
p3 += dot(p3, p3.yzx + 33.33);
|
|
58
|
+
return fract((p3.x + p3.y) * p3.z);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
vec3 rgb2hsl(vec3 c) {
|
|
62
|
+
float maxC = max(c.r, max(c.g, c.b));
|
|
63
|
+
float minC = min(c.r, min(c.g, c.b));
|
|
64
|
+
float l = (maxC + minC) * 0.5;
|
|
65
|
+
float d = maxC - minC;
|
|
66
|
+
float h = 0.0, s = 0.0;
|
|
67
|
+
if (d > 0.0) {
|
|
68
|
+
s = d / (1.0 - abs(2.0 * l - 1.0));
|
|
69
|
+
if (maxC == c.r) h = mod((c.g - c.b) / d, 6.0) / 6.0;
|
|
70
|
+
else if (maxC == c.g) h = ((c.b - c.r) / d + 2.0) / 6.0;
|
|
71
|
+
else h = ((c.r - c.g) / d + 4.0) / 6.0;
|
|
72
|
+
}
|
|
73
|
+
return vec3(h, s, l);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
vec3 hsl2rgb(vec3 c) {
|
|
77
|
+
float h = c.x, s = c.y, l = c.z;
|
|
78
|
+
float a = s * (1.0 - abs(2.0 * l - 1.0));
|
|
79
|
+
vec3 rgb = clamp(abs(mod(h * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
|
|
80
|
+
return l + a * (rgb - 0.5);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
void main() {
|
|
84
|
+
vec2 fragCoord = vec2(gl_FragCoord.x, u_resolution.y - gl_FragCoord.y);
|
|
85
|
+
vec2 cellCoord = floor(fragCoord / u_cellsize);
|
|
86
|
+
|
|
87
|
+
if (u_revealEffectFlag == 1) {
|
|
88
|
+
|
|
89
|
+
float revealThreshold = (cellCoord.x + cellCoord.y) / (u_gridSize.x + u_gridSize.y - 2.0);
|
|
90
|
+
if (revealThreshold > u_revealProgress) {
|
|
91
|
+
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
} else if (u_revealEffectFlag == 2) {
|
|
95
|
+
float dist = length(cellCoord / u_gridSize - vec2(0.5));
|
|
96
|
+
float revealThreshold = dist / 0.7071;
|
|
97
|
+
if (revealThreshold > u_revealProgress) {
|
|
98
|
+
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
} else if (u_revealEffectFlag == 3) {
|
|
102
|
+
float revealThreshold = hash(cellCoord);
|
|
103
|
+
if (revealThreshold > u_revealProgress) {
|
|
104
|
+
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
vec2 cellCenter = (cellCoord + 0.5) * u_cellsize;
|
|
110
|
+
|
|
111
|
+
vec2 rawUV = u_videoMode ? (fragCoord / u_resolution) : (cellCenter / u_resolution);
|
|
112
|
+
vec2 sampleUV = u_cropOffset + rawUV * u_cropScale;
|
|
113
|
+
|
|
114
|
+
vec3 cellColor = texture(u_texture, sampleUV).rgb;
|
|
115
|
+
float luminosity = dot(cellColor, vec3(0.299, 0.587, 0.114));
|
|
116
|
+
vec3 hsl = rgb2hsl(cellColor);
|
|
117
|
+
hsl.z = clamp(hsl.z * u_brightness, 0.0, 1.0);
|
|
118
|
+
hsl.y = clamp(hsl.y * u_saturation, 0.0, 1.0);
|
|
119
|
+
cellColor = hsl2rgb(hsl);
|
|
120
|
+
|
|
121
|
+
vec2 withinCellPos = fract(fragCoord / u_cellsize);
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
float glyphMask = 0.0;
|
|
125
|
+
if (!u_videoMode) {
|
|
126
|
+
uint charInd;
|
|
127
|
+
if (u_shapeMatching) {
|
|
128
|
+
charInd = texelFetch(u_fboTexture, ivec2(cellCoord), 0).r;
|
|
129
|
+
} else {
|
|
130
|
+
charInd = uint(floor(luminosity * (u_numChars - 1.0)));
|
|
131
|
+
}
|
|
132
|
+
float atlasU = (float(charInd) + withinCellPos.x) / u_numChars;
|
|
133
|
+
float atlasV = withinCellPos.y;
|
|
134
|
+
glyphMask = texture(u_atlas, vec2(atlasU, atlasV)).r;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
vec3 bgColor = cellColor * u_bgOpacity;
|
|
138
|
+
|
|
139
|
+
bool scatterHit = false;
|
|
140
|
+
vec3 scatterColor = vec3(0.0);
|
|
141
|
+
if (u_scatterEffect) {
|
|
142
|
+
uint state = texelFetch(u_scatterStateTexture, ivec2(cellCoord), 0).r;
|
|
143
|
+
if (state > 0u) {
|
|
144
|
+
int idx = int(state) - 1;
|
|
145
|
+
float su = (float(idx) + withinCellPos.x) / u_scatterNumChars;
|
|
146
|
+
float mask = texture(u_scatterAtlas, vec2(su, withinCellPos.y)).r;
|
|
147
|
+
|
|
148
|
+
vec3 scatterBg = u_videoMode ? cellColor : bgColor;
|
|
149
|
+
scatterColor = mix(scatterBg, vec3(1.0), mask);
|
|
150
|
+
scatterHit = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!scatterHit && u_spreadEffect) {
|
|
155
|
+
uint state = texelFetch(u_spreadStateTexture, ivec2(cellCoord), 0).r;
|
|
156
|
+
if (state > 0u) {
|
|
157
|
+
int idx = int(state) - 1;
|
|
158
|
+
float su = (float(idx) + withinCellPos.x) / u_scatterNumChars;
|
|
159
|
+
float mask = texture(u_scatterAtlas, vec2(su, withinCellPos.y)).r;
|
|
160
|
+
vec3 scatterBg = u_videoMode ? cellColor : bgColor;
|
|
161
|
+
scatterColor = mix(scatterBg, vec3(1.0), mask);
|
|
162
|
+
scatterHit = true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
vec3 finalColor;
|
|
167
|
+
if (scatterHit) {
|
|
168
|
+
finalColor = scatterColor;
|
|
169
|
+
} else {
|
|
170
|
+
finalColor = u_videoMode ? cellColor : mix(bgColor, cellColor, glyphMask);
|
|
171
|
+
|
|
172
|
+
if (u_mouseEffect) {
|
|
173
|
+
bool inside = false;
|
|
174
|
+
for (int i = 0; i < MOUSE_TRAIL_LEN; i++) {
|
|
175
|
+
if (u_mouseLifeFracs[i] <= 0.0) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
float r = u_mouseRadius * u_mouseLifeFracs[i];
|
|
179
|
+
if (distance(cellCenter, u_mousePositions[i]) < r) {
|
|
180
|
+
inside = true;
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (inside) {
|
|
185
|
+
finalColor = clamp(finalColor * u_mouseBrightness, 0.0, 1.0);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (u_clickEffect) {
|
|
190
|
+
float boost = 1.0;
|
|
191
|
+
for (int i = 0; i < MAX_RIPPLES; i++) {
|
|
192
|
+
if (u_rippleBrightnesses[i] <= 1.0) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (distance(cellCenter, u_ripplePositions[i]) < u_rippleRadii[i]) {
|
|
196
|
+
boost *= u_rippleBrightnesses[i];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
if (boost > 1.0) {
|
|
200
|
+
finalColor = clamp(finalColor * boost, 0.0, 1.0);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fragColor = vec4(finalColor, 1.0);
|
|
206
|
+
}`,d=`#version 300 es
|
|
207
|
+
|
|
208
|
+
precision highp float;
|
|
209
|
+
precision highp usampler2D;
|
|
210
|
+
|
|
211
|
+
uniform sampler2D u_texture;
|
|
212
|
+
uniform vec2 u_resolution;
|
|
213
|
+
uniform vec2 u_cellsize;
|
|
214
|
+
uniform int u_circleN;
|
|
215
|
+
uniform sampler2D u_charVectors;
|
|
216
|
+
uniform int u_numCharsInt;
|
|
217
|
+
uniform float u_shapeExponent;
|
|
218
|
+
uniform vec2 u_cropOffset;
|
|
219
|
+
uniform vec2 u_cropScale;
|
|
220
|
+
|
|
221
|
+
out uvec4 fragCharInd;
|
|
222
|
+
|
|
223
|
+
void main() {
|
|
224
|
+
|
|
225
|
+
ivec2 cellCoord = ivec2(floor(gl_FragCoord.xy));
|
|
226
|
+
|
|
227
|
+
const vec2 CIRCLES[6] = vec2[6](
|
|
228
|
+
vec2(0.25, 0.25), vec2(0.75, 0.25),
|
|
229
|
+
vec2(0.25, 0.50), vec2(0.75, 0.50),
|
|
230
|
+
vec2(0.25, 0.75), vec2(0.75, 0.75)
|
|
231
|
+
);
|
|
232
|
+
vec2 radiusUV = vec2(u_cellsize.x / 5.0) / u_resolution * u_cropScale;
|
|
233
|
+
|
|
234
|
+
float sv[6];
|
|
235
|
+
for (int ci = 0; ci < 6; ci++) {
|
|
236
|
+
|
|
237
|
+
vec2 centerUV = u_cropOffset + (vec2(cellCoord) + CIRCLES[ci]) * u_cellsize / u_resolution * u_cropScale;
|
|
238
|
+
float total = 0.0;
|
|
239
|
+
int count = 0;
|
|
240
|
+
for (int dx = -u_circleN; dx <= u_circleN; dx++) {
|
|
241
|
+
for (int dy = -u_circleN; dy <= u_circleN; dy++) {
|
|
242
|
+
if (dx*dx + dy*dy <= u_circleN*u_circleN) {
|
|
243
|
+
vec2 sampleUV = centerUV + vec2(float(dx), float(dy)) * radiusUV / float(u_circleN);
|
|
244
|
+
vec3 sc = texture(u_texture, sampleUV).rgb;
|
|
245
|
+
total += dot(sc, vec3(0.299, 0.587, 0.114));
|
|
246
|
+
count++;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
sv[ci] = count > 0 ? total / float(count) : 0.0;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
float maxVal = 0.0;
|
|
255
|
+
for (int d = 0; d < 6; d++) maxVal = max(maxVal, sv[d]);
|
|
256
|
+
if (maxVal > 0.0) {
|
|
257
|
+
for (int d = 0; d < 6; d++) sv[d] = pow(sv[d] / maxVal, u_shapeExponent) * maxVal;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
uint bestChar = 0u;
|
|
262
|
+
float bestDist = 1e10;
|
|
263
|
+
for (int i = 0; i < u_numCharsInt; i++) {
|
|
264
|
+
vec4 r0 = texelFetch(u_charVectors, ivec2(i, 0), 0);
|
|
265
|
+
vec4 r1 = texelFetch(u_charVectors, ivec2(i, 1), 0);
|
|
266
|
+
float dist = 0.0;
|
|
267
|
+
float diffs[6] = float[6](
|
|
268
|
+
sv[0] - r0.r, sv[1] - r0.g, sv[2] - r0.b, sv[3] - r0.a,
|
|
269
|
+
sv[4] - r1.r, sv[5] - r1.g
|
|
270
|
+
);
|
|
271
|
+
for (int d = 0; d < 6; d++) {
|
|
272
|
+
dist += diffs[d] * diffs[d];
|
|
273
|
+
}
|
|
274
|
+
if (dist < bestDist) {
|
|
275
|
+
bestDist = dist; bestChar = uint(i);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
fragCharInd = uvec4(bestChar, 0u, 0u, 0u);
|
|
280
|
+
}`;function f(e){let t=s(e,e.VERTEX_SHADER,l),n=s(e,e.FRAGMENT_SHADER,u);if(!t||!n)return null;let r=c(e,t,n);if(!r)return null;let i=new Float32Array([1,1,-1,1,1,-1,-1,-1,-1,1,1,-1]),a=e.createBuffer();e.bindBuffer(e.ARRAY_BUFFER,a),e.bufferData(e.ARRAY_BUFFER,i,e.STATIC_DRAW);let o=e.getAttribLocation(r,`a_position`);e.vertexAttribPointer(o,2,e.FLOAT,!1,0,0),e.enableVertexAttribArray(o),e.useProgram(r);let f=e.createTexture();e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,f),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST);let p=e.getUniformLocation(r,`u_texture`);e.uniform1i(p,0);let m=e.createTexture();e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,m),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST);let h=e.getUniformLocation(r,`u_atlas`);e.uniform1i(h,1);let g=e.createTexture();e.activeTexture(e.TEXTURE2),e.bindTexture(e.TEXTURE_2D,g),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST);let _=s(e,e.FRAGMENT_SHADER,d);if(!_)return null;let v=c(e,t,_);if(!v)return null;e.useProgram(v),e.uniform1i(e.getUniformLocation(v,`u_texture`),0),e.uniform1i(e.getUniformLocation(v,`u_charVectors`),2);let y=e.getUniformLocation(v,`u_resolution`),b=e.getUniformLocation(v,`u_cellsize`),x=e.getUniformLocation(v,`u_circleN`),S=e.getUniformLocation(v,`u_numCharsInt`),C=e.getUniformLocation(v,`u_shapeExponent`),w=e.getUniformLocation(v,`u_cropOffset`),T=e.getUniformLocation(v,`u_cropScale`),E=e.createTexture();e.activeTexture(e.TEXTURE3),e.bindTexture(e.TEXTURE_2D,E),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST);let D=e.createFramebuffer(),O=e.createTexture();e.activeTexture(e.TEXTURE4),e.bindTexture(e.TEXTURE_2D,O),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST);let k=e.createTexture();e.activeTexture(e.TEXTURE5),e.bindTexture(e.TEXTURE_2D,k),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST);let A=e.createTexture();return e.activeTexture(e.TEXTURE6),e.bindTexture(e.TEXTURE_2D,A),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_S,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_WRAP_T,e.CLAMP_TO_EDGE),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MIN_FILTER,e.NEAREST),e.texParameteri(e.TEXTURE_2D,e.TEXTURE_MAG_FILTER,e.NEAREST),e.useProgram(r),e.uniform1i(e.getUniformLocation(r,`u_fboTexture`),3),e.uniform1i(e.getUniformLocation(r,`u_scatterAtlas`),4),e.uniform1i(e.getUniformLocation(r,`u_scatterStateTexture`),5),e.uniform1i(e.getUniformLocation(r,`u_spreadStateTexture`),6),{program:r,pass1Program:v,buffer:a,vertShader:t,fragShader:n,pass1FragShader:_,texture:f,atlasTexture:m,charVectorsTexture:g,fboTexture:E,fbo:D,scatterAtlasTexture:O,scatterStateTexture:k,spreadStateTexture:A,p1ResLoc:y,p1CellsizeLoc:b,p1CircleNLoc:x,p1NumCharsLoc:S,p1ExponentLoc:C,p1CropOffsetLoc:w,p1CropScaleLoc:T,revealEffectFlagLoc:e.getUniformLocation(r,`u_revealEffectFlag`),mouseEffectFlagLoc:e.getUniformLocation(r,`u_mouseEffect`),clickEffectFlagLoc:e.getUniformLocation(r,`u_clickEffect`),shapeMatchingLoc:e.getUniformLocation(r,`u_shapeMatching`),revealProgressLoc:e.getUniformLocation(r,`u_revealProgress`),brightnessLoc:e.getUniformLocation(r,`u_brightness`),saturationLoc:e.getUniformLocation(r,`u_saturation`),bgOpacityLoc:e.getUniformLocation(r,`u_bgOpacity`),mouseBrightnessLoc:e.getUniformLocation(r,`u_mouseBrightness`),mousePositionsLoc:e.getUniformLocation(r,`u_mousePositions`),mouseLifeFracsLoc:e.getUniformLocation(r,`u_mouseLifeFracs`),mouseRadiusLoc:e.getUniformLocation(r,`u_mouseRadius`),ripplePositionsLoc:e.getUniformLocation(r,`u_ripplePositions`),rippleRadiiLoc:e.getUniformLocation(r,`u_rippleRadii`),rippleBrightnessesLoc:e.getUniformLocation(r,`u_rippleBrightnesses`),scatterEffectFlagLoc:e.getUniformLocation(r,`u_scatterEffect`),scatterNumCharsLoc:e.getUniformLocation(r,`u_scatterNumChars`),spreadEffectFlagLoc:e.getUniformLocation(r,`u_spreadEffect`),videoModeLoc:e.getUniformLocation(r,`u_videoMode`),resLoc:e.getUniformLocation(r,`u_resolution`),sizeLoc:e.getUniformLocation(r,`u_cellsize`),numLoc:e.getUniformLocation(r,`u_numChars`),gridSizeLoc:e.getUniformLocation(r,`u_gridSize`),cropOffsetLoc:e.getUniformLocation(r,`u_cropOffset`),cropScaleLoc:e.getUniformLocation(r,`u_cropScale`)}}function p({scatterEnabledRef:e,mouseRadiusRef:t,durationRef:n,scatterCharsRef:r}){let i=new Uint8Array,a=new Float32Array,o=new Float32Array,s=new Uint8Array,c=null,l=null,u=0,d=0,f=-1,p=()=>Math.floor(Math.random()*r.current.length),m=0,h=0,g=1,_=1,v=null;return{setup(e,t,n,r,f,p){m=t,h=n,g=r,_=f,v=p;let y=t*n;i=new Uint8Array(y),a=new Float32Array(y),o=new Float32Array(y),s=new Uint8Array(y),c=null,l=null,u=0,d=0,e.activeTexture(e.TEXTURE5),e.bindTexture(e.TEXTURE_2D,p),e.texImage2D(e.TEXTURE_2D,0,e.R8UI,t,n,0,e.RED_INTEGER,e.UNSIGNED_BYTE,i)},handleMouseMove(t,n,r){e.current&&(l=c,c={x:t,y:n,t:r})},handleMouseLeave(){c=null,l=null,u=0,d=0},reset(e){i.fill(0),v&&m>0&&(e.activeTexture(e.TEXTURE5),e.bindTexture(e.TEXTURE_2D,v),e.texSubImage2D(e.TEXTURE_2D,0,0,0,m,h,e.RED_INTEGER,e.UNSIGNED_BYTE,i))},tick(r,y){if(!e.current||m===0||h===0)return;let b=performance.now(),x=f<0?0:Math.min(.1,(b-f)/1e3);f=b;let S=Math.min(y.width,y.height)*t.current,C=!1;if(c&&b-c.t<80){if(l){let e=(c.t-l.t)/1e3;if(e>0&&e<.2){let t=(c.x-l.x)/e,n=(c.y-l.y)/e;u=u*.7+t*.3,d=d*.7+n*.3}}else u=0,d=0;Math.hypot(u,d)>g*2&&(C=!0)}else u*=.6,d*=.6;if(s.fill(0),C&&c&&S>0){let e=c.x,t=c.y,n=Math.max(0,Math.floor((e-S)/g)),r=Math.min(m-1,Math.floor((e+S)/g)),l=Math.max(0,Math.floor((t-S)/_)),u=Math.min(h-1,Math.floor((t+S)/_));for(let c=l;c<=u;c++)for(let l=n;l<=r;l++){let n=(l+.5)*g,r=(c+.5)*_;if(Math.hypot(n-e,r-t)>S)continue;let u=c*m+l;s[u]=1,i[u]===0&&(i[u]=1+p(),o[u]=.5+Math.random()*2),a[u]=1}}let w=n.current;if(x>0)for(let e=0;e<h;e++)for(let t=0;t<m;t++){let n=e*m+t;if(i[n]===0||s[n])continue;let r=t>0?i[n-1]!==0:!1,c=t<m-1?i[n+1]!==0:!1,l=e>0?i[n-m]!==0:!1,u=e<h-1?i[n+m]!==0:!1,d=(!r||!c||!l||!u?5:1)*o[n]/w;a[n]-=d*x,a[n]<=0&&(i[n]=0)}r.activeTexture(r.TEXTURE5),r.bindTexture(r.TEXTURE_2D,v),r.texSubImage2D(r.TEXTURE_2D,0,0,0,m,h,r.RED_INTEGER,r.UNSIGNED_BYTE,i)}}}function m({brightenEnabledRef:e,trailLenRef:t,durationRef:n,trailDecayRef:r}){let i=[];return{handleMouseMove(n,r,a){e.current&&(i.push({x:n,y:r,t:a}),i.length>t.current&&i.shift())},tick(a,o,s){if(!e.current)return;let c=performance.now(),l=new Float32Array(t.current*2),u=new Float32Array(t.current);for(let e=0;e<i.length;e++){let t=c-i[e].t,a=Math.max(0,1-t/(n.current*1e3))**r.current;l[e*2]=i[e].x,l[e*2+1]=i[e].y,u[e]=a}a.uniform2fv(o,l),a.uniform1fv(s,u)}}}var h=10;function g({clickEnabledRef:e,clickSpeedRef:t,clickBrightnessRef:n}){let r=[];return{handleClick(t,n){if(!e.current)return;let i=n.getBoundingClientRect(),a=(t.clientX-i.left)*(n.width/i.width),o=(t.clientY-i.top)*(n.height/i.height);r.push({x:a,y:o,t:performance.now()}),r.length>h&&r.shift()},tick(i,a,o,s,c){if(!e.current)return;let l=performance.now(),u=Math.hypot(a.width,a.height),d=new Float32Array(h*2),f=new Float32Array(h),p=new Float32Array(h);for(;r.length>0&&(l-r[0].t)*t.current>=u;)r.shift();for(let e=0;e<r.length;e++){let i=(l-r[e].t)*t.current,a=i/u,o=1+(n.current-1)*(1-a**2);d[e*2]=r[e].x,d[e*2+1]=r[e].y,f[e]=i,p[e]=o}i.uniform2fv(o,d),i.uniform1fv(s,f),i.uniform1fv(c,p)}}}function _({spreadEnabledRef:e,scatterCharsRef:t,spreadExpandDurationRef:n,spreadSpeedRef:r}){let i=new Uint8Array,a=new Uint8Array,o=0,s=0,c=1,l=1,u=null,d=-1,f=-1,p=()=>Math.floor(Math.random()*t.current.length),m=[[-1,0],[1,0],[0,-1],[0,1]];return{setup(e,t,n,r,p,m){o=t,s=n,c=r,l=p,u=m;let h=t*n;i=new Uint8Array(h),a=new Uint8Array(h),f=-1,d=-1,e.activeTexture(e.TEXTURE6),e.bindTexture(e.TEXTURE_2D,m),e.texImage2D(e.TEXTURE_2D,0,e.R8UI,t,n,0,e.RED_INTEGER,e.UNSIGNED_BYTE,i)},handleClick(t,r){if(!e.current||o===0)return;let u=r.getBoundingClientRect(),d=(t.clientX-u.left)*(r.width/u.width),m=(t.clientY-u.top)*(r.height/u.height),h=Math.floor(d/c),g=Math.floor(m/l);if(h<0||h>=o||g<0||g>=s)return;let _=g*o+h;i[_]=1+p(),a[_]=1,f=performance.now()+n.current*1e3},tick(t){if(!e.current||o===0||s===0)return;let n=performance.now(),c=d<0?0:Math.min(.1,(n-d)/1e3);d=n;let l=f>0&&n<f;if(!l&&f>0){for(let e=0;e<i.length;e++)a[e]===1&&(a[e]=2);f=-1}if(c>0)for(let e=0;e<s;e++)for(let t=0;t<o;t++){let n=e*o+t;if(a[n]!==0){if(l&&a[n]===1)for(let[n,l]of m){let u=e+n,d=t+l;if(u<0||u>=s||d<0||d>=o)continue;let f=u*o+d;a[f]===0&&Math.random()<r.current*c&&(i[f]=1+p(),a[f]=1)}else if(a[n]===2)for(let[l,u]of m){let d=e+l,f=t+u;if(d<0||d>=s||f<0||f>=o||i[d*o+f]===0){Math.random()<r.current*c&&(i[n]=0,a[n]=0);break}}}}t.activeTexture(t.TEXTURE6),t.bindTexture(t.TEXTURE_2D,u),t.texSubImage2D(t.TEXTURE_2D,0,0,0,o,s,t.RED_INTEGER,t.UNSIGNED_BYTE,i)}}}function v({src:e,videoMode:r=!1,numColsRaw:s=250,brightnessRaw:c=1,saturationRaw:l=1,bgOpacityRaw:u=.3,revealEffect:d=!1,chars:h=a,mouseEffect:v=!0,clickEffect:y=!0,charMode:b=`shape`,className:x}){let S=(0,t.useRef)(null),C=(0,t.useRef)(null),w=(0,t.useRef)(null),T=(0,t.useRef)(null),E=(0,t.useRef)(null),{numCols:D,brightness:O,saturation:k,bgOpacity:A,mouseEnabled:j,mouseStyle:M,brightenEnabled:N,scatterEnabled:P,scatterChars:F,trailLen:I,trailDecay:L,duration:R,mouseRadius:z,mouseBrightness:B,clickEnabled:V,clickBrightness:H,clickSpeed:U,spreadEnabled:W,spreadExpandDuration:G,spreadSpeed:K,revealEnabled:ee,revealDuration:te,revealEffectFlag:ne}=o(s,c,l,u,v,y,d),re=(0,t.useRef)(O),ie=(0,t.useRef)(k),ae=(0,t.useRef)(A),oe=(0,t.useRef)(j),se=(0,t.useRef)(M),q=(0,t.useRef)(N),J=(0,t.useRef)(P),ce=(0,t.useRef)(B),Y=(0,t.useRef)(z),le=(0,t.useRef)(I),ue=(0,t.useRef)(L),X=(0,t.useRef)(R),Z=(0,t.useRef)(F),Q=(0,t.useRef)(V),de=(0,t.useRef)(H),fe=(0,t.useRef)(U),pe=(0,t.useRef)(W),me=(0,t.useRef)(G),he=(0,t.useRef)(K),ge=(0,t.useRef)(D),_e=(0,t.useRef)(r);(0,t.useEffect)(()=>{re.current=O,ie.current=k,ae.current=A,oe.current=j,se.current=M,q.current=N,J.current=P,ce.current=B,Y.current=z,le.current=I,ue.current=L,X.current=R,Z.current!==F&&$.current?(Z.current=F,ye.current?.()):Z.current=F,Q.current=V,de.current=H,fe.current=U,pe.current=W,me.current=G,he.current=K,ge.current=D,_e.current=r},[O,k,A,j,M,N,P,B,z,I,L,R,F,V,H,U,W,G,K,D,r]);let ve=(0,t.useRef)(null),ye=(0,t.useRef)(null),$=(0,t.useRef)(!1);return(0,t.useEffect)(()=>{$.current&&ve.current?.(D)},[D]),(0,t.useEffect)(()=>{$.current=!1;let t=[],n=0,r=0,a=1,o=1,s=S.current;if(!s)return;let c=C.current;if(!c)return;let l=s.getContext(`webgl2`);if(!l)return;l.pixelStorei(l.UNPACK_ALIGNMENT,1);let u=f(l);if(!u)return;let{program:d,pass1Program:v}=u;T.current=u.atlasTexture,E.current=u.scatterAtlasTexture,l.uniform1i(u.shapeMatchingLoc,+(b===`shape`)),l.uniform1i(u.revealEffectFlagLoc,ne),l.uniform1f(u.revealProgressLoc,0);let y=p({scatterEnabledRef:J,mouseRadiusRef:Y,durationRef:X,scatterCharsRef:Z}),x=m({brightenEnabledRef:q,trailLenRef:le,durationRef:X,trailDecayRef:ue}),D=g({clickEnabledRef:Q,clickSpeedRef:fe,clickBrightnessRef:de}),O=_({spreadEnabledRef:pe,scatterCharsRef:Z,spreadExpandDurationRef:me,spreadSpeedRef:he}),k,A=-1,j=-1,M=0,N=Array.isArray(e)?e:[e],P=N.length>1,F=document.createElement(`canvas`),I=F.getContext(`2d`),L=e=>{a=Math.max(1,Math.floor(s.width/e));let c=a*2;if(I.font=`${c}px monospace`,o=Math.max(1,Math.round(c*a/I.measureText(`M`).width)),n=Math.floor(s.width/a),r=Math.floor(s.height/o),s.width=n*a,s.height=r*o,l.viewport(0,0,s.width,s.height),l.useProgram(d),l.uniform2f(u.resLoc,s.width,s.height),l.useProgram(v),l.uniform2f(u.p1ResLoc,s.width,s.height),l.useProgram(d),y.setup(l,n,r,a,o,u.scatterStateTexture),O.setup(l,n,r,a,o,u.spreadStateTexture),b===`shape`){t=i(h,a,o);let e=t.length,s=new Float32Array(e*8);for(let n=0;n<e;n++){let r=t[n].vector;s[n*4+0]=r[0],s[n*4+1]=r[1],s[n*4+2]=r[2],s[n*4+3]=r[3],s[e*4+n*4+0]=r[4],s[e*4+n*4+1]=r[5]}l.activeTexture(l.TEXTURE2),l.bindTexture(l.TEXTURE_2D,u.charVectorsTexture),l.texImage2D(l.TEXTURE_2D,0,l.RGBA32F,e,2,0,l.RGBA,l.FLOAT,s),l.activeTexture(l.TEXTURE3),l.bindTexture(l.TEXTURE_2D,u.fboTexture),l.texImage2D(l.TEXTURE_2D,0,l.R8UI,n,r,0,l.RED_INTEGER,l.UNSIGNED_BYTE,null),l.bindFramebuffer(l.FRAMEBUFFER,u.fbo),l.framebufferTexture2D(l.FRAMEBUFFER,l.COLOR_ATTACHMENT0,l.TEXTURE_2D,u.fboTexture,0),l.bindFramebuffer(l.FRAMEBUFFER,null);let c=Math.max(1,Math.round(a/5));l.useProgram(v),l.uniform2f(u.p1CellsizeLoc,a,o),l.uniform1i(u.p1CircleNLoc,c),l.uniform1i(u.p1NumCharsLoc,e),l.uniform1f(u.p1ExponentLoc,2),l.useProgram(d)}l.uniform2f(u.gridSizeLoc,n,r),F.width=h.length*a,F.height=o,I.font=`${o}px monospace`,I.fillStyle=`black`,I.fillRect(0,0,h.length*a,o),I.fillStyle=`white`,I.textBaseline=`top`;for(let e=0;e<h.length;e++)I.fillText(h[e],e*a,0);l.activeTexture(l.TEXTURE1),l.bindTexture(l.TEXTURE_2D,T.current),l.texImage2D(l.TEXTURE_2D,0,l.RGBA,l.RGBA,l.UNSIGNED_BYTE,F),l.uniform1f(u.numLoc,h.length),l.uniform2f(u.sizeLoc,a,o),R()},R=()=>{let e=Z.current;F.width=e.length*a,F.height=o,I.fillStyle=`black`,I.fillRect(0,0,F.width,o),I.fillStyle=`white`,I.textBaseline=`top`,I.font=`${o}px monospace`;for(let t=0;t<e.length;t++)I.fillText(e[t],t*a,0);l.activeTexture(l.TEXTURE4),l.bindTexture(l.TEXTURE_2D,E.current),l.texImage2D(l.TEXTURE_2D,0,l.RGBA,l.RGBA,l.UNSIGNED_BYTE,F),l.uniform1f(u.scatterNumCharsLoc,e.length),y.reset(l)};ve.current=L,ye.current=R;let z=(e,t)=>{s.width=Math.round(e),s.height=Math.round(t),L(ge.current);let n=c.videoWidth/c.videoHeight,r=s.width/s.height,i=1,a=1,o=0,f=0;r>n?(a=n/r,f=(1-a)/2):(i=r/n,o=(1-i)/2),l.uniform2f(u.cropOffsetLoc,o,f),l.uniform2f(u.cropScaleLoc,i,a),l.useProgram(v),l.uniform2f(u.p1CropOffsetLoc,o,f),l.uniform2f(u.p1CropScaleLoc,i,a),l.useProgram(d)},B=e=>{if(!oe.current)return;let t=s.getBoundingClientRect(),n=(e.clientX-t.left)*(s.width/t.width),r=(e.clientY-t.top)*(s.height/t.height),i=performance.now();x.handleMouseMove(n,r,i),y.handleMouseMove(n,r,i)};s.addEventListener(`mousemove`,B);let V=()=>y.handleMouseLeave();s.addEventListener(`mouseleave`,V);let H=e=>{D.handleClick(e,s),O.handleClick(e,s)};s.addEventListener(`click`,H);let U=()=>{if(l.uniform1i(u.videoModeLoc,+!!_e.current),l.uniform1f(u.brightnessLoc,re.current),l.uniform1f(u.saturationLoc,ie.current),l.uniform1f(u.bgOpacityLoc,ae.current),l.uniform1i(u.mouseEffectFlagLoc,+!!q.current),l.uniform1i(u.scatterEffectFlagLoc,+!!J.current),l.uniform1i(u.clickEffectFlagLoc,+!!Q.current),l.uniform1i(u.spreadEffectFlagLoc,+!!pe.current),l.uniform1f(u.mouseBrightnessLoc,ce.current),l.uniform1f(u.mouseRadiusLoc,Math.min(s.width,s.height)*Y.current),ee){let e=j<0?0:Math.min(1,(performance.now()-j)/(te*1e3));l.uniform1f(u.revealProgressLoc,e)}$.current&&c.currentTime!=A&&c.readyState>=2&&(l.activeTexture(l.TEXTURE0),l.bindTexture(l.TEXTURE_2D,u.texture),l.texSubImage2D(l.TEXTURE_2D,0,0,0,l.RGBA,l.UNSIGNED_BYTE,c),A=c.currentTime),x.tick(l,u.mousePositionsLoc,u.mouseLifeFracsLoc),y.tick(l,s),D.tick(l,s,u.ripplePositionsLoc,u.rippleRadiiLoc,u.rippleBrightnessesLoc),O.tick(l),b===`shape`&&(l.bindFramebuffer(l.FRAMEBUFFER,u.fbo),l.viewport(0,0,n,r),l.useProgram(v),l.drawArrays(l.TRIANGLES,0,6),l.bindFramebuffer(l.FRAMEBUFFER,null),l.viewport(0,0,s.width,s.height),l.useProgram(d)),l.drawArrays(l.TRIANGLES,0,6),k=requestAnimationFrame(U)},W=()=>{let e=w.current;z(e.clientWidth||c.videoWidth,e.clientHeight||c.videoHeight),l.activeTexture(l.TEXTURE0),l.bindTexture(l.TEXTURE_2D,u.texture),l.texImage2D(l.TEXTURE_2D,0,l.RGBA,l.RGBA,l.UNSIGNED_BYTE,c),c.play(),j=performance.now(),$.current=!0,k=requestAnimationFrame(U)},G=()=>{M=(M+1)%N.length,c.src=N[M],c.load(),c.addEventListener(`canplay`,()=>c.play(),{once:!0})},K=new ResizeObserver(e=>{let{width:t,height:n}=e[0].contentRect;$.current&&t>0&&n>0&&z(t,n)});return w.current&&K.observe(w.current),c.addEventListener(`loadeddata`,W,{once:!0}),P&&c.addEventListener(`ended`,G),c.readyState>=2&&c.currentSrc.endsWith(N[0])?W():c.currentSrc.endsWith(N[0])||c.load(),()=>{K.disconnect(),ve.current=null,ye.current=null,$.current=!1,cancelAnimationFrame(k),c.removeEventListener(`loadeddata`,W),P&&c.removeEventListener(`ended`,G),s.removeEventListener(`mousemove`,B),s.removeEventListener(`mouseleave`,V),s.removeEventListener(`click`,H),l.deleteTexture(u.texture),l.deleteTexture(u.charVectorsTexture),l.deleteFramebuffer(u.fbo),l.deleteTexture(u.fboTexture),l.deleteBuffer(u.buffer),l.deleteShader(u.vertShader),l.deleteShader(u.fragShader),l.deleteShader(u.pass1FragShader),l.deleteProgram(d),l.deleteProgram(v),l.deleteTexture(T.current),l.deleteTexture(E.current),l.deleteTexture(u.scatterStateTexture),l.deleteTexture(u.spreadStateTexture)}},[e,b,h,ne,te,ee]),(0,n.jsxs)(`div`,{ref:w,className:x,style:{height:`100%`,width:`100%`},children:[(0,n.jsx)(`video`,{ref:C,muted:!0,playsInline:!0,autoPlay:!0,loop:!Array.isArray(e)||e.length===1,style:{display:`none`},children:(0,n.jsx)(`source`,{src:Array.isArray(e)?e[0]:e,type:`video/mp4`})}),(0,n.jsx)(`canvas`,{ref:S,style:{width:`100%`,height:`100%`,display:`block`}})]})}e.VideoAscii=v});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "react-video-ascii",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "React component that renders videos as ASCII animations using WebGL2.",
|
|
6
|
+
"author": "Lucas Jin",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/LucasHJin/react-video-ascii.git"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/LucasHJin/react-video-ascii",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"main": "./dist/react-video-ascii.umd.cjs",
|
|
17
|
+
"module": "./dist/react-video-ascii.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/index.d.ts",
|
|
22
|
+
"import": "./dist/react-video-ascii.js",
|
|
23
|
+
"require": "./dist/react-video-ascii.umd.cjs"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -b && vite build",
|
|
28
|
+
"dev": "vite build --watch",
|
|
29
|
+
"lint": "eslint ."
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"react": ">=18",
|
|
33
|
+
"react-dom": ">=18"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@eslint/js": "^10.0.1",
|
|
37
|
+
"@types/node": "^24.0.0",
|
|
38
|
+
"@types/react": "^19.2.14",
|
|
39
|
+
"@types/react-dom": "^19.2.3",
|
|
40
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
41
|
+
"eslint": "^10.3.0",
|
|
42
|
+
"eslint-plugin-react-hooks": "^7.1.1",
|
|
43
|
+
"globals": "^17.6.0",
|
|
44
|
+
"typescript": "~6.0.3",
|
|
45
|
+
"typescript-eslint": "^8.59.1",
|
|
46
|
+
"vite": "^8.0.10",
|
|
47
|
+
"vite-plugin-dts": "^5.0.0",
|
|
48
|
+
"vite-plugin-glsl": "^1.6.0"
|
|
49
|
+
}
|
|
50
|
+
}
|