@outpacelabs/gradient-avatars-react 0.2.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/LICENSE +22 -0
- package/README.md +37 -0
- package/dist/index.cjs +261 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +252 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) Outpace Studios
|
|
2
|
+
|
|
3
|
+
This work is licensed under the Creative Commons Attribution 4.0
|
|
4
|
+
International License (CC BY 4.0). To view a copy of this license, visit
|
|
5
|
+
https://creativecommons.org/licenses/by/4.0/.
|
|
6
|
+
|
|
7
|
+
You are free to:
|
|
8
|
+
|
|
9
|
+
- Share — copy and redistribute the material in any medium or format
|
|
10
|
+
- Adapt — remix, transform, and build upon the material for any
|
|
11
|
+
purpose, even commercially
|
|
12
|
+
|
|
13
|
+
Under the following terms:
|
|
14
|
+
|
|
15
|
+
- Attribution — You must give appropriate credit, provide a link to
|
|
16
|
+
the license, and indicate if changes were made. You may do so in
|
|
17
|
+
any reasonable manner, but not in any way that suggests the
|
|
18
|
+
licensor endorses you or your use.
|
|
19
|
+
|
|
20
|
+
- No additional restrictions — You may not apply legal terms or
|
|
21
|
+
technological measures that legally restrict others from doing
|
|
22
|
+
anything the license permits.
|
package/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# @outpacelabs/gradient-avatars-react
|
|
2
|
+
|
|
3
|
+
React component for [`@outpacelabs/gradient-avatars`](https://www.npmjs.com/package/@outpacelabs/gradient-avatars).
|
|
4
|
+
Renders a deterministic mesh-gradient avatar for any seed on a `<canvas>` —
|
|
5
|
+
the same seed always yields the same gradient.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm i @outpacelabs/gradient-avatars-react
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { GradientAvatar } from "@outpacelabs/gradient-avatars-react";
|
|
15
|
+
|
|
16
|
+
<GradientAvatar seed={user.id} size={40} />
|
|
17
|
+
<GradientAvatar seed="jane@example.com" size={96} className="ring-2 ring-white/10" />
|
|
18
|
+
<GradientAvatar seed="square" size={64} radius={12} />
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Props
|
|
22
|
+
|
|
23
|
+
| Prop | Type | Default | Description |
|
|
24
|
+
|------|------|---------|-------------|
|
|
25
|
+
| `seed` | `string \| number` | — | Any value; each unique seed is a unique gradient. |
|
|
26
|
+
| `size` | `number` | `32` | Rendered size in pixels. |
|
|
27
|
+
| `radius` | `number \| string` | `"9999px"` | Corner radius. Number = pixels, string = any CSS length. Defaults to a full circle; pass `0` for a square. |
|
|
28
|
+
| `className` | `string` | — | Extra classes on the wrapper `<span>`. |
|
|
29
|
+
| `style` | `CSSProperties` | — | Extra inline styles merged onto the wrapper. |
|
|
30
|
+
|
|
31
|
+
The engine helpers (`renderGradient`, `gradientToDataURL`, `gradientToBlob`,
|
|
32
|
+
`generatePalette`, …) are re-exported for convenience — e.g. to offer a
|
|
33
|
+
download/copy of the full-resolution image.
|
|
34
|
+
|
|
35
|
+
## License
|
|
36
|
+
|
|
37
|
+
[CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) — free to use with attribution. By [Outpace Studios](https://outpacestudios.com).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
5
|
+
|
|
6
|
+
// ../gradient-avatars/dist/index.js
|
|
7
|
+
var HARMONY_TYPES = [
|
|
8
|
+
"analogous",
|
|
9
|
+
"triadic",
|
|
10
|
+
"splitComplementary",
|
|
11
|
+
"tetradic",
|
|
12
|
+
"complementary"
|
|
13
|
+
];
|
|
14
|
+
var GOLDEN_ANGLE = 137.5;
|
|
15
|
+
var DEFAULT_BLUR_FRACTION = 0.06;
|
|
16
|
+
function seededRandom(seed) {
|
|
17
|
+
let s = seed;
|
|
18
|
+
return () => {
|
|
19
|
+
s += 1831565813;
|
|
20
|
+
let t = s;
|
|
21
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
22
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
23
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function hslToHex(h, s, l) {
|
|
27
|
+
h = (h % 360 + 360) % 360;
|
|
28
|
+
s = Math.max(0, Math.min(100, s)) / 100;
|
|
29
|
+
l = Math.max(0, Math.min(100, l)) / 100;
|
|
30
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
31
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
32
|
+
const m = l - c / 2;
|
|
33
|
+
let r = 0;
|
|
34
|
+
let g = 0;
|
|
35
|
+
let b = 0;
|
|
36
|
+
if (h < 60) {
|
|
37
|
+
r = c;
|
|
38
|
+
g = x;
|
|
39
|
+
} else if (h < 120) {
|
|
40
|
+
r = x;
|
|
41
|
+
g = c;
|
|
42
|
+
} else if (h < 180) {
|
|
43
|
+
g = c;
|
|
44
|
+
b = x;
|
|
45
|
+
} else if (h < 240) {
|
|
46
|
+
g = x;
|
|
47
|
+
b = c;
|
|
48
|
+
} else if (h < 300) {
|
|
49
|
+
r = x;
|
|
50
|
+
b = c;
|
|
51
|
+
} else {
|
|
52
|
+
r = c;
|
|
53
|
+
b = x;
|
|
54
|
+
}
|
|
55
|
+
const toHex = (n) => {
|
|
56
|
+
const hex = Math.round((n + m) * 255).toString(16);
|
|
57
|
+
return hex.length === 1 ? `0${hex}` : hex;
|
|
58
|
+
};
|
|
59
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
|
|
60
|
+
}
|
|
61
|
+
function harmonyHues(baseHue, harmony) {
|
|
62
|
+
switch (harmony) {
|
|
63
|
+
case "analogous":
|
|
64
|
+
return [baseHue, baseHue + 30, baseHue + 60, baseHue - 30];
|
|
65
|
+
case "triadic":
|
|
66
|
+
return [baseHue, baseHue + 120, baseHue + 240];
|
|
67
|
+
case "splitComplementary":
|
|
68
|
+
return [baseHue, baseHue + 150, baseHue + 210];
|
|
69
|
+
case "tetradic":
|
|
70
|
+
return [baseHue, baseHue + 90, baseHue + 180, baseHue + 270];
|
|
71
|
+
case "complementary":
|
|
72
|
+
return [baseHue, baseHue + 180, baseHue + 20, baseHue + 200];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function seedFromString(input) {
|
|
76
|
+
let h = 2166136261 >>> 0;
|
|
77
|
+
for (let i = 0; i < input.length; i++) {
|
|
78
|
+
h ^= input.charCodeAt(i);
|
|
79
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
80
|
+
}
|
|
81
|
+
h ^= h >>> 16;
|
|
82
|
+
h = Math.imul(h, 2146121005) >>> 0;
|
|
83
|
+
h ^= h >>> 15;
|
|
84
|
+
h = Math.imul(h, 2221713035) >>> 0;
|
|
85
|
+
h ^= h >>> 16;
|
|
86
|
+
return h >>> 0;
|
|
87
|
+
}
|
|
88
|
+
function toSeed(seed) {
|
|
89
|
+
if (typeof seed === "number") return seed;
|
|
90
|
+
return seedFromString(seed);
|
|
91
|
+
}
|
|
92
|
+
function generatePalette(seed) {
|
|
93
|
+
const s = toSeed(seed);
|
|
94
|
+
const random = seededRandom(s);
|
|
95
|
+
const baseHue = s * GOLDEN_ANGLE % 360;
|
|
96
|
+
const harmonyIndex = Math.floor(random() * HARMONY_TYPES.length);
|
|
97
|
+
const harmony = HARMONY_TYPES[harmonyIndex];
|
|
98
|
+
const hues = harmonyHues(baseHue, harmony);
|
|
99
|
+
const colors = hues.map((hue) => {
|
|
100
|
+
const saturation = 75 + random() * 25;
|
|
101
|
+
const lightness = 50 + random() * 20;
|
|
102
|
+
return hslToHex(hue, saturation, lightness);
|
|
103
|
+
});
|
|
104
|
+
return { seed: s, colors, harmony };
|
|
105
|
+
}
|
|
106
|
+
function drawMeshGradient(ctx, seed, size) {
|
|
107
|
+
const s = toSeed(seed);
|
|
108
|
+
const { colors } = generatePalette(s);
|
|
109
|
+
const random = seededRandom(s * 12345);
|
|
110
|
+
ctx.fillStyle = colors[0];
|
|
111
|
+
ctx.fillRect(0, 0, size, size);
|
|
112
|
+
const numSpots = 8 + Math.floor(random() * 5);
|
|
113
|
+
const spots = [];
|
|
114
|
+
for (let i = 0; i < numSpots; i++) {
|
|
115
|
+
const angle = random() * Math.PI * 2;
|
|
116
|
+
const distance = random() * size * 0.4;
|
|
117
|
+
const centerX = size / 2 + Math.cos(angle) * distance;
|
|
118
|
+
const centerY = size / 2 + Math.sin(angle) * distance;
|
|
119
|
+
spots.push({
|
|
120
|
+
x: centerX + (random() - 0.5) * size * 0.3,
|
|
121
|
+
y: centerY + (random() - 0.5) * size * 0.3,
|
|
122
|
+
radius: size * (0.3 + random() * 0.4),
|
|
123
|
+
color: colors[i % colors.length]
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
spots.sort((a, b) => b.radius - a.radius);
|
|
127
|
+
ctx.globalCompositeOperation = "source-over";
|
|
128
|
+
for (const spot of spots) {
|
|
129
|
+
const g = ctx.createRadialGradient(
|
|
130
|
+
spot.x,
|
|
131
|
+
spot.y,
|
|
132
|
+
0,
|
|
133
|
+
spot.x,
|
|
134
|
+
spot.y,
|
|
135
|
+
spot.radius
|
|
136
|
+
);
|
|
137
|
+
g.addColorStop(0, `${spot.color}FF`);
|
|
138
|
+
g.addColorStop(0.3, `${spot.color}DD`);
|
|
139
|
+
g.addColorStop(0.6, `${spot.color}88`);
|
|
140
|
+
g.addColorStop(1, `${spot.color}00`);
|
|
141
|
+
ctx.fillStyle = g;
|
|
142
|
+
ctx.fillRect(0, 0, size, size);
|
|
143
|
+
}
|
|
144
|
+
const hx = size * 0.3 + random() * size * 0.2;
|
|
145
|
+
const hy = size * 0.3 + random() * size * 0.2;
|
|
146
|
+
const hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, size * 0.3);
|
|
147
|
+
hg.addColorStop(0, "rgba(255,255,255,0.15)");
|
|
148
|
+
hg.addColorStop(1, "rgba(255,255,255,0)");
|
|
149
|
+
ctx.fillStyle = hg;
|
|
150
|
+
ctx.fillRect(0, 0, size, size);
|
|
151
|
+
}
|
|
152
|
+
function blurFor(size, blur) {
|
|
153
|
+
if (blur === 0) return 0;
|
|
154
|
+
return blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);
|
|
155
|
+
}
|
|
156
|
+
function renderGradient(canvas, seed, options = {}) {
|
|
157
|
+
const size = canvas.width;
|
|
158
|
+
const blur = blurFor(size, options.blur);
|
|
159
|
+
const ctx = canvas.getContext("2d");
|
|
160
|
+
if (!ctx) return;
|
|
161
|
+
if (blur <= 0) {
|
|
162
|
+
ctx.clearRect(0, 0, size, size);
|
|
163
|
+
drawMeshGradient(ctx, seed, size);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
const scratch = createCanvas(size, size);
|
|
167
|
+
const sctx = scratch.getContext("2d");
|
|
168
|
+
if (!sctx) return;
|
|
169
|
+
drawMeshGradient(sctx, seed, size);
|
|
170
|
+
const scaleUp = 1 + blur / size * 4;
|
|
171
|
+
const dw = size * scaleUp;
|
|
172
|
+
const offset = (dw - size) / 2;
|
|
173
|
+
ctx.clearRect(0, 0, size, size);
|
|
174
|
+
ctx.filter = `blur(${blur}px)`;
|
|
175
|
+
ctx.drawImage(scratch, -offset, -offset, dw, dw);
|
|
176
|
+
ctx.filter = "none";
|
|
177
|
+
}
|
|
178
|
+
function createCanvas(w, h) {
|
|
179
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
180
|
+
return new OffscreenCanvas(w, h);
|
|
181
|
+
}
|
|
182
|
+
const c = document.createElement("canvas");
|
|
183
|
+
c.width = w;
|
|
184
|
+
c.height = h;
|
|
185
|
+
return c;
|
|
186
|
+
}
|
|
187
|
+
function gradientToDataURL(seed, options = {}) {
|
|
188
|
+
const { size = 512, type = "image/png", quality = 0.92 } = options;
|
|
189
|
+
const canvas = document.createElement("canvas");
|
|
190
|
+
canvas.width = size;
|
|
191
|
+
canvas.height = size;
|
|
192
|
+
renderGradient(canvas, seed, options);
|
|
193
|
+
return canvas.toDataURL(type, quality);
|
|
194
|
+
}
|
|
195
|
+
function gradientToBlob(seed, options = {}) {
|
|
196
|
+
const { size = 512, type = "image/png", quality = 0.92 } = options;
|
|
197
|
+
const canvas = document.createElement("canvas");
|
|
198
|
+
canvas.width = size;
|
|
199
|
+
canvas.height = size;
|
|
200
|
+
renderGradient(canvas, seed, options);
|
|
201
|
+
return new Promise((resolve) => canvas.toBlob(resolve, type, quality));
|
|
202
|
+
}
|
|
203
|
+
var RENDER_SIZE = 256;
|
|
204
|
+
var BLUR_FRACTION = 0.06;
|
|
205
|
+
function GradientAvatar({
|
|
206
|
+
seed,
|
|
207
|
+
size = 32,
|
|
208
|
+
radius = "9999px",
|
|
209
|
+
className,
|
|
210
|
+
style
|
|
211
|
+
}) {
|
|
212
|
+
const canvasRef = react.useRef(null);
|
|
213
|
+
react.useEffect(() => {
|
|
214
|
+
const canvas = canvasRef.current;
|
|
215
|
+
if (!canvas) return;
|
|
216
|
+
const ctx = canvas.getContext("2d");
|
|
217
|
+
if (!ctx) return;
|
|
218
|
+
ctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);
|
|
219
|
+
drawMeshGradient(ctx, seed, RENDER_SIZE);
|
|
220
|
+
}, [seed]);
|
|
221
|
+
const blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));
|
|
222
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
223
|
+
"span",
|
|
224
|
+
{
|
|
225
|
+
className,
|
|
226
|
+
style: {
|
|
227
|
+
display: "inline-block",
|
|
228
|
+
overflow: "hidden",
|
|
229
|
+
borderRadius: radius,
|
|
230
|
+
width: size,
|
|
231
|
+
height: size,
|
|
232
|
+
...style
|
|
233
|
+
},
|
|
234
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
235
|
+
"canvas",
|
|
236
|
+
{
|
|
237
|
+
ref: canvasRef,
|
|
238
|
+
width: RENDER_SIZE,
|
|
239
|
+
height: RENDER_SIZE,
|
|
240
|
+
style: {
|
|
241
|
+
width: "100%",
|
|
242
|
+
height: "100%",
|
|
243
|
+
display: "block",
|
|
244
|
+
filter: `blur(${blurPx}px)`
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
exports.GradientAvatar = GradientAvatar;
|
|
253
|
+
exports.drawMeshGradient = drawMeshGradient;
|
|
254
|
+
exports.generatePalette = generatePalette;
|
|
255
|
+
exports.gradientToBlob = gradientToBlob;
|
|
256
|
+
exports.gradientToDataURL = gradientToDataURL;
|
|
257
|
+
exports.renderGradient = renderGradient;
|
|
258
|
+
exports.seedFromString = seedFromString;
|
|
259
|
+
exports.toSeed = toSeed;
|
|
260
|
+
//# sourceMappingURL=index.cjs.map
|
|
261
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../gradient-avatars/src/index.ts","../src/index.tsx"],"names":["useRef","useEffect","jsx"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @outpacelabs/gradient-avatars\n *\n * Framework-agnostic mesh-gradient avatar generator. Every seed (string or\n * number) deterministically produces a unique gradient — no stored images,\n * no network. Pure palette/RNG core plus optional Canvas2D render helpers.\n */\n\nexport type Harmony =\n\t| \"analogous\"\n\t| \"triadic\"\n\t| \"splitComplementary\"\n\t| \"tetradic\"\n\t| \"complementary\";\n\nexport interface GradientPalette {\n\t/** The numeric seed the palette was derived from. */\n\tseed: number;\n\t/** Hex color stops used to paint the mesh. */\n\tcolors: string[];\n\t/** Which color-harmony rule produced the hues. */\n\tharmony: Harmony;\n}\n\nconst HARMONY_TYPES: Harmony[] = [\n\t\"analogous\",\n\t\"triadic\",\n\t\"splitComplementary\",\n\t\"tetradic\",\n\t\"complementary\",\n];\n\nconst GOLDEN_ANGLE = 137.5;\n\n/** Default blur radius as a fraction of the rendered dimension. */\nexport const DEFAULT_BLUR_FRACTION = 0.06;\n\nfunction seededRandom(seed: number): () => number {\n\tlet s = seed;\n\treturn () => {\n\t\ts += 0x6d2b79f5;\n\t\tlet t = s;\n\t\tt = Math.imul(t ^ (t >>> 15), t | 1);\n\t\tt ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\nfunction hslToHex(h: number, s: number, l: number): string {\n\th = ((h % 360) + 360) % 360;\n\ts = Math.max(0, Math.min(100, s)) / 100;\n\tl = Math.max(0, Math.min(100, l)) / 100;\n\n\tconst c = (1 - Math.abs(2 * l - 1)) * s;\n\tconst x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n\tconst m = l - c / 2;\n\n\tlet r = 0;\n\tlet g = 0;\n\tlet b = 0;\n\tif (h < 60) {\n\t\tr = c;\n\t\tg = x;\n\t} else if (h < 120) {\n\t\tr = x;\n\t\tg = c;\n\t} else if (h < 180) {\n\t\tg = c;\n\t\tb = x;\n\t} else if (h < 240) {\n\t\tg = x;\n\t\tb = c;\n\t} else if (h < 300) {\n\t\tr = x;\n\t\tb = c;\n\t} else {\n\t\tr = c;\n\t\tb = x;\n\t}\n\n\tconst toHex = (n: number) => {\n\t\tconst hex = Math.round((n + m) * 255).toString(16);\n\t\treturn hex.length === 1 ? `0${hex}` : hex;\n\t};\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n\nfunction harmonyHues(baseHue: number, harmony: Harmony): number[] {\n\tswitch (harmony) {\n\t\tcase \"analogous\":\n\t\t\treturn [baseHue, baseHue + 30, baseHue + 60, baseHue - 30];\n\t\tcase \"triadic\":\n\t\t\treturn [baseHue, baseHue + 120, baseHue + 240];\n\t\tcase \"splitComplementary\":\n\t\t\treturn [baseHue, baseHue + 150, baseHue + 210];\n\t\tcase \"tetradic\":\n\t\t\treturn [baseHue, baseHue + 90, baseHue + 180, baseHue + 270];\n\t\tcase \"complementary\":\n\t\t\treturn [baseHue, baseHue + 180, baseHue + 20, baseHue + 200];\n\t}\n}\n\n/**\n * Stable string → 32-bit unsigned hash (FNV-1a + bit-mixing avalanche).\n * Uses the full uint32 range as a seed so similar strings diverge fully.\n */\nexport function seedFromString(input: string): number {\n\tlet h = 2166136261 >>> 0;\n\tfor (let i = 0; i < input.length; i++) {\n\t\th ^= input.charCodeAt(i);\n\t\th = Math.imul(h, 16777619) >>> 0;\n\t}\n\th ^= h >>> 16;\n\th = Math.imul(h, 0x7feb352d) >>> 0;\n\th ^= h >>> 15;\n\th = Math.imul(h, 0x846ca68b) >>> 0;\n\th ^= h >>> 16;\n\treturn h >>> 0;\n}\n\n/** Normalize a string or number seed to the numeric seed used internally. */\nexport function toSeed(seed: number | string): number {\n\tif (typeof seed === \"number\") return seed;\n\treturn seedFromString(seed);\n}\n\n/** Derive the deterministic color palette for a seed. */\nexport function generatePalette(seed: number | string): GradientPalette {\n\tconst s = toSeed(seed);\n\tconst random = seededRandom(s);\n\tconst baseHue = (s * GOLDEN_ANGLE) % 360;\n\tconst harmonyIndex = Math.floor(random() * HARMONY_TYPES.length);\n\tconst harmony = HARMONY_TYPES[harmonyIndex];\n\tconst hues = harmonyHues(baseHue, harmony);\n\tconst colors = hues.map((hue) => {\n\t\tconst saturation = 75 + random() * 25;\n\t\tconst lightness = 50 + random() * 20;\n\t\treturn hslToHex(hue, saturation, lightness);\n\t});\n\treturn { seed: s, colors, harmony };\n}\n\n/**\n * Minimal Canvas2D context surface the renderer needs. Both\n * `HTMLCanvasElement` and `OffscreenCanvas` 2D contexts satisfy it.\n */\nexport type GradientContext = {\n\tfillStyle: string | CanvasGradient | CanvasPattern;\n\tglobalCompositeOperation: GlobalCompositeOperation;\n\tfillRect(x: number, y: number, w: number, h: number): void;\n\tcreateRadialGradient(\n\t\tx0: number,\n\t\ty0: number,\n\t\tr0: number,\n\t\tx1: number,\n\t\ty1: number,\n\t\tr1: number,\n\t): CanvasGradient;\n};\n\n/**\n * Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.\n * The caller is responsible for any blur — apply `filter: blur(…)` on the\n * displayed canvas (≈6% of the rendered dimension) for the signature look,\n * or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.\n */\nexport function drawMeshGradient(\n\tctx: GradientContext,\n\tseed: number | string,\n\tsize: number,\n): void {\n\tconst s = toSeed(seed);\n\tconst { colors } = generatePalette(s);\n\tconst random = seededRandom(s * 12345);\n\n\tctx.fillStyle = colors[0];\n\tctx.fillRect(0, 0, size, size);\n\n\tconst numSpots = 8 + Math.floor(random() * 5);\n\tconst spots: Array<{ x: number; y: number; radius: number; color: string }> =\n\t\t[];\n\n\tfor (let i = 0; i < numSpots; i++) {\n\t\tconst angle = random() * Math.PI * 2;\n\t\tconst distance = random() * size * 0.4;\n\t\tconst centerX = size / 2 + Math.cos(angle) * distance;\n\t\tconst centerY = size / 2 + Math.sin(angle) * distance;\n\t\tspots.push({\n\t\t\tx: centerX + (random() - 0.5) * size * 0.3,\n\t\t\ty: centerY + (random() - 0.5) * size * 0.3,\n\t\t\tradius: size * (0.3 + random() * 0.4),\n\t\t\tcolor: colors[i % colors.length],\n\t\t});\n\t}\n\n\tspots.sort((a, b) => b.radius - a.radius);\n\n\tctx.globalCompositeOperation = \"source-over\";\n\tfor (const spot of spots) {\n\t\tconst g = ctx.createRadialGradient(\n\t\t\tspot.x,\n\t\t\tspot.y,\n\t\t\t0,\n\t\t\tspot.x,\n\t\t\tspot.y,\n\t\t\tspot.radius,\n\t\t);\n\t\tg.addColorStop(0, `${spot.color}FF`);\n\t\tg.addColorStop(0.3, `${spot.color}DD`);\n\t\tg.addColorStop(0.6, `${spot.color}88`);\n\t\tg.addColorStop(1, `${spot.color}00`);\n\t\tctx.fillStyle = g;\n\t\tctx.fillRect(0, 0, size, size);\n\t}\n\n\tconst hx = size * 0.3 + random() * size * 0.2;\n\tconst hy = size * 0.3 + random() * size * 0.2;\n\tconst hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, size * 0.3);\n\thg.addColorStop(0, \"rgba(255,255,255,0.15)\");\n\thg.addColorStop(1, \"rgba(255,255,255,0)\");\n\tctx.fillStyle = hg;\n\tctx.fillRect(0, 0, size, size);\n}\n\nexport interface RenderOptions {\n\t/**\n\t * Blur radius in pixels. Defaults to ~6% of the canvas size for the\n\t * signature soft look. Pass `0` to disable.\n\t */\n\tblur?: number;\n}\n\nfunction blurFor(size: number, blur?: number): number {\n\tif (blur === 0) return 0;\n\treturn blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);\n}\n\n/**\n * Render a seed's gradient into an existing canvas, baking in the soft blur.\n * Draws at the canvas's current `width`/`height`. Browser/OffscreenCanvas only.\n */\nexport function renderGradient(\n\tcanvas: HTMLCanvasElement | OffscreenCanvas,\n\tseed: number | string,\n\toptions: RenderOptions = {},\n): void {\n\tconst size = canvas.width;\n\tconst blur = blurFor(size, options.blur);\n\n\tconst ctx = canvas.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!ctx) return;\n\n\tif (blur <= 0) {\n\t\tctx.clearRect(0, 0, size, size);\n\t\tdrawMeshGradient(ctx, seed, size);\n\t\treturn;\n\t}\n\n\t// Draw the raw mesh on a scratch canvas, then composite it back with blur\n\t// scaled up slightly so the soft edges fall outside the frame (no ring).\n\tconst scratch = createCanvas(size, size);\n\tconst sctx = scratch.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!sctx) return;\n\tdrawMeshGradient(sctx, seed, size);\n\n\tconst scaleUp = 1 + (blur / size) * 4;\n\tconst dw = size * scaleUp;\n\tconst offset = (dw - size) / 2;\n\tctx.clearRect(0, 0, size, size);\n\tctx.filter = `blur(${blur}px)`;\n\tctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n\tctx.filter = \"none\";\n}\n\nfunction createCanvas(\n\tw: number,\n\th: number,\n): HTMLCanvasElement | OffscreenCanvas {\n\tif (typeof OffscreenCanvas !== \"undefined\") {\n\t\treturn new OffscreenCanvas(w, h);\n\t}\n\tconst c = document.createElement(\"canvas\");\n\tc.width = w;\n\tc.height = h;\n\treturn c;\n}\n\nexport interface ExportOptions extends RenderOptions {\n\t/** Output pixel dimensions (square). Default: 512. */\n\tsize?: number;\n\t/** Image MIME type. Default: \"image/png\". */\n\ttype?: string;\n\t/** Quality 0–1 for lossy types. Default: 0.92. */\n\tquality?: number;\n}\n\n/** Render a seed's gradient and return it as a data URL. Browser only. */\nexport function gradientToDataURL(\n\tseed: number | string,\n\toptions: ExportOptions = {},\n): string {\n\tconst { size = 512, type = \"image/png\", quality = 0.92 } = options;\n\tconst canvas = document.createElement(\"canvas\");\n\tcanvas.width = size;\n\tcanvas.height = size;\n\trenderGradient(canvas, seed, options);\n\treturn canvas.toDataURL(type, quality);\n}\n\n/** Render a seed's gradient and resolve a Blob (or null). Browser only. */\nexport function gradientToBlob(\n\tseed: number | string,\n\toptions: ExportOptions = {},\n): Promise<Blob | null> {\n\tconst { size = 512, type = \"image/png\", quality = 0.92 } = options;\n\tconst canvas = document.createElement(\"canvas\");\n\tcanvas.width = size;\n\tcanvas.height = size;\n\trenderGradient(canvas, seed, options);\n\treturn new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n","import { drawMeshGradient } from \"@outpacelabs/gradient-avatars\";\nimport type { CSSProperties } from \"react\";\nimport { useEffect, useRef } from \"react\";\n\nexport interface GradientAvatarProps {\n\t/** Any string or number — each unique seed produces a unique gradient. */\n\tseed: number | string;\n\t/** Rendered size in pixels. Default: 32. */\n\tsize?: number;\n\t/**\n\t * Corner radius. Number = pixels, string = any CSS length.\n\t * Defaults to a full circle; pass `0` for a square or e.g. `12` for a\n\t * rounded square. Default: \"9999px\".\n\t */\n\tradius?: number | string;\n\t/** Additional CSS classes on the wrapper. */\n\tclassName?: string;\n\t/** Extra inline styles merged onto the wrapper. */\n\tstyle?: CSSProperties;\n}\n\n/** Internal render resolution. Higher than display size so the CSS blur is smooth. */\nconst RENDER_SIZE = 256;\n/** Blur radius as a fraction of display size. */\nconst BLUR_FRACTION = 0.06;\n\n/**\n * Renders a deterministic mesh-gradient avatar on a `<canvas>`.\n * The same seed always produces the same gradient.\n */\nexport function GradientAvatar({\n\tseed,\n\tsize = 32,\n\tradius = \"9999px\",\n\tclassName,\n\tstyle,\n}: GradientAvatarProps) {\n\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\n\tuseEffect(() => {\n\t\tconst canvas = canvasRef.current;\n\t\tif (!canvas) return;\n\t\tconst ctx = canvas.getContext(\"2d\");\n\t\tif (!ctx) return;\n\t\tctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);\n\t\tdrawMeshGradient(ctx, seed, RENDER_SIZE);\n\t}, [seed]);\n\n\tconst blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));\n\n\treturn (\n\t\t<span\n\t\t\tclassName={className}\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"inline-block\",\n\t\t\t\toverflow: \"hidden\",\n\t\t\t\tborderRadius: radius,\n\t\t\t\twidth: size,\n\t\t\t\theight: size,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<canvas\n\t\t\t\tref={canvasRef}\n\t\t\t\twidth={RENDER_SIZE}\n\t\t\t\theight={RENDER_SIZE}\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\theight: \"100%\",\n\t\t\t\t\tdisplay: \"block\",\n\t\t\t\t\tfilter: `blur(${blurPx}px)`,\n\t\t\t\t}}\n\t\t\t/>\n\t\t</span>\n\t);\n}\n\nexport type {\n\tExportOptions,\n\tGradientPalette,\n\tHarmony,\n\tRenderOptions,\n} from \"@outpacelabs/gradient-avatars\";\nexport {\n\tdrawMeshGradient,\n\tgeneratePalette,\n\tgradientToBlob,\n\tgradientToDataURL,\n\trenderGradient,\n\tseedFromString,\n\ttoSeed,\n} from \"@outpacelabs/gradient-avatars\";\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { CSSProperties } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @outpacelabs/gradient-avatars
|
|
6
|
+
*
|
|
7
|
+
* Framework-agnostic mesh-gradient avatar generator. Every seed (string or
|
|
8
|
+
* number) deterministically produces a unique gradient — no stored images,
|
|
9
|
+
* no network. Pure palette/RNG core plus optional Canvas2D render helpers.
|
|
10
|
+
*/
|
|
11
|
+
type Harmony = "analogous" | "triadic" | "splitComplementary" | "tetradic" | "complementary";
|
|
12
|
+
interface GradientPalette {
|
|
13
|
+
/** The numeric seed the palette was derived from. */
|
|
14
|
+
seed: number;
|
|
15
|
+
/** Hex color stops used to paint the mesh. */
|
|
16
|
+
colors: string[];
|
|
17
|
+
/** Which color-harmony rule produced the hues. */
|
|
18
|
+
harmony: Harmony;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Stable string → 32-bit unsigned hash (FNV-1a + bit-mixing avalanche).
|
|
22
|
+
* Uses the full uint32 range as a seed so similar strings diverge fully.
|
|
23
|
+
*/
|
|
24
|
+
declare function seedFromString(input: string): number;
|
|
25
|
+
/** Normalize a string or number seed to the numeric seed used internally. */
|
|
26
|
+
declare function toSeed(seed: number | string): number;
|
|
27
|
+
/** Derive the deterministic color palette for a seed. */
|
|
28
|
+
declare function generatePalette(seed: number | string): GradientPalette;
|
|
29
|
+
/**
|
|
30
|
+
* Minimal Canvas2D context surface the renderer needs. Both
|
|
31
|
+
* `HTMLCanvasElement` and `OffscreenCanvas` 2D contexts satisfy it.
|
|
32
|
+
*/
|
|
33
|
+
type GradientContext = {
|
|
34
|
+
fillStyle: string | CanvasGradient | CanvasPattern;
|
|
35
|
+
globalCompositeOperation: GlobalCompositeOperation;
|
|
36
|
+
fillRect(x: number, y: number, w: number, h: number): void;
|
|
37
|
+
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.
|
|
41
|
+
* The caller is responsible for any blur — apply `filter: blur(…)` on the
|
|
42
|
+
* displayed canvas (≈6% of the rendered dimension) for the signature look,
|
|
43
|
+
* or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.
|
|
44
|
+
*/
|
|
45
|
+
declare function drawMeshGradient(ctx: GradientContext, seed: number | string, size: number): void;
|
|
46
|
+
interface RenderOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Blur radius in pixels. Defaults to ~6% of the canvas size for the
|
|
49
|
+
* signature soft look. Pass `0` to disable.
|
|
50
|
+
*/
|
|
51
|
+
blur?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Render a seed's gradient into an existing canvas, baking in the soft blur.
|
|
55
|
+
* Draws at the canvas's current `width`/`height`. Browser/OffscreenCanvas only.
|
|
56
|
+
*/
|
|
57
|
+
declare function renderGradient(canvas: HTMLCanvasElement | OffscreenCanvas, seed: number | string, options?: RenderOptions): void;
|
|
58
|
+
interface ExportOptions extends RenderOptions {
|
|
59
|
+
/** Output pixel dimensions (square). Default: 512. */
|
|
60
|
+
size?: number;
|
|
61
|
+
/** Image MIME type. Default: "image/png". */
|
|
62
|
+
type?: string;
|
|
63
|
+
/** Quality 0–1 for lossy types. Default: 0.92. */
|
|
64
|
+
quality?: number;
|
|
65
|
+
}
|
|
66
|
+
/** Render a seed's gradient and return it as a data URL. Browser only. */
|
|
67
|
+
declare function gradientToDataURL(seed: number | string, options?: ExportOptions): string;
|
|
68
|
+
/** Render a seed's gradient and resolve a Blob (or null). Browser only. */
|
|
69
|
+
declare function gradientToBlob(seed: number | string, options?: ExportOptions): Promise<Blob | null>;
|
|
70
|
+
|
|
71
|
+
interface GradientAvatarProps {
|
|
72
|
+
/** Any string or number — each unique seed produces a unique gradient. */
|
|
73
|
+
seed: number | string;
|
|
74
|
+
/** Rendered size in pixels. Default: 32. */
|
|
75
|
+
size?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Corner radius. Number = pixels, string = any CSS length.
|
|
78
|
+
* Defaults to a full circle; pass `0` for a square or e.g. `12` for a
|
|
79
|
+
* rounded square. Default: "9999px".
|
|
80
|
+
*/
|
|
81
|
+
radius?: number | string;
|
|
82
|
+
/** Additional CSS classes on the wrapper. */
|
|
83
|
+
className?: string;
|
|
84
|
+
/** Extra inline styles merged onto the wrapper. */
|
|
85
|
+
style?: CSSProperties;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Renders a deterministic mesh-gradient avatar on a `<canvas>`.
|
|
89
|
+
* The same seed always produces the same gradient.
|
|
90
|
+
*/
|
|
91
|
+
declare function GradientAvatar({ seed, size, radius, className, style, }: GradientAvatarProps): react_jsx_runtime.JSX.Element;
|
|
92
|
+
|
|
93
|
+
export { type ExportOptions, GradientAvatar, type GradientAvatarProps, type GradientPalette, type Harmony, type RenderOptions, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
import { CSSProperties } from 'react';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @outpacelabs/gradient-avatars
|
|
6
|
+
*
|
|
7
|
+
* Framework-agnostic mesh-gradient avatar generator. Every seed (string or
|
|
8
|
+
* number) deterministically produces a unique gradient — no stored images,
|
|
9
|
+
* no network. Pure palette/RNG core plus optional Canvas2D render helpers.
|
|
10
|
+
*/
|
|
11
|
+
type Harmony = "analogous" | "triadic" | "splitComplementary" | "tetradic" | "complementary";
|
|
12
|
+
interface GradientPalette {
|
|
13
|
+
/** The numeric seed the palette was derived from. */
|
|
14
|
+
seed: number;
|
|
15
|
+
/** Hex color stops used to paint the mesh. */
|
|
16
|
+
colors: string[];
|
|
17
|
+
/** Which color-harmony rule produced the hues. */
|
|
18
|
+
harmony: Harmony;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Stable string → 32-bit unsigned hash (FNV-1a + bit-mixing avalanche).
|
|
22
|
+
* Uses the full uint32 range as a seed so similar strings diverge fully.
|
|
23
|
+
*/
|
|
24
|
+
declare function seedFromString(input: string): number;
|
|
25
|
+
/** Normalize a string or number seed to the numeric seed used internally. */
|
|
26
|
+
declare function toSeed(seed: number | string): number;
|
|
27
|
+
/** Derive the deterministic color palette for a seed. */
|
|
28
|
+
declare function generatePalette(seed: number | string): GradientPalette;
|
|
29
|
+
/**
|
|
30
|
+
* Minimal Canvas2D context surface the renderer needs. Both
|
|
31
|
+
* `HTMLCanvasElement` and `OffscreenCanvas` 2D contexts satisfy it.
|
|
32
|
+
*/
|
|
33
|
+
type GradientContext = {
|
|
34
|
+
fillStyle: string | CanvasGradient | CanvasPattern;
|
|
35
|
+
globalCompositeOperation: GlobalCompositeOperation;
|
|
36
|
+
fillRect(x: number, y: number, w: number, h: number): void;
|
|
37
|
+
createRadialGradient(x0: number, y0: number, r0: number, x1: number, y1: number, r1: number): CanvasGradient;
|
|
38
|
+
};
|
|
39
|
+
/**
|
|
40
|
+
* Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.
|
|
41
|
+
* The caller is responsible for any blur — apply `filter: blur(…)` on the
|
|
42
|
+
* displayed canvas (≈6% of the rendered dimension) for the signature look,
|
|
43
|
+
* or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.
|
|
44
|
+
*/
|
|
45
|
+
declare function drawMeshGradient(ctx: GradientContext, seed: number | string, size: number): void;
|
|
46
|
+
interface RenderOptions {
|
|
47
|
+
/**
|
|
48
|
+
* Blur radius in pixels. Defaults to ~6% of the canvas size for the
|
|
49
|
+
* signature soft look. Pass `0` to disable.
|
|
50
|
+
*/
|
|
51
|
+
blur?: number;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Render a seed's gradient into an existing canvas, baking in the soft blur.
|
|
55
|
+
* Draws at the canvas's current `width`/`height`. Browser/OffscreenCanvas only.
|
|
56
|
+
*/
|
|
57
|
+
declare function renderGradient(canvas: HTMLCanvasElement | OffscreenCanvas, seed: number | string, options?: RenderOptions): void;
|
|
58
|
+
interface ExportOptions extends RenderOptions {
|
|
59
|
+
/** Output pixel dimensions (square). Default: 512. */
|
|
60
|
+
size?: number;
|
|
61
|
+
/** Image MIME type. Default: "image/png". */
|
|
62
|
+
type?: string;
|
|
63
|
+
/** Quality 0–1 for lossy types. Default: 0.92. */
|
|
64
|
+
quality?: number;
|
|
65
|
+
}
|
|
66
|
+
/** Render a seed's gradient and return it as a data URL. Browser only. */
|
|
67
|
+
declare function gradientToDataURL(seed: number | string, options?: ExportOptions): string;
|
|
68
|
+
/** Render a seed's gradient and resolve a Blob (or null). Browser only. */
|
|
69
|
+
declare function gradientToBlob(seed: number | string, options?: ExportOptions): Promise<Blob | null>;
|
|
70
|
+
|
|
71
|
+
interface GradientAvatarProps {
|
|
72
|
+
/** Any string or number — each unique seed produces a unique gradient. */
|
|
73
|
+
seed: number | string;
|
|
74
|
+
/** Rendered size in pixels. Default: 32. */
|
|
75
|
+
size?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Corner radius. Number = pixels, string = any CSS length.
|
|
78
|
+
* Defaults to a full circle; pass `0` for a square or e.g. `12` for a
|
|
79
|
+
* rounded square. Default: "9999px".
|
|
80
|
+
*/
|
|
81
|
+
radius?: number | string;
|
|
82
|
+
/** Additional CSS classes on the wrapper. */
|
|
83
|
+
className?: string;
|
|
84
|
+
/** Extra inline styles merged onto the wrapper. */
|
|
85
|
+
style?: CSSProperties;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Renders a deterministic mesh-gradient avatar on a `<canvas>`.
|
|
89
|
+
* The same seed always produces the same gradient.
|
|
90
|
+
*/
|
|
91
|
+
declare function GradientAvatar({ seed, size, radius, className, style, }: GradientAvatarProps): react_jsx_runtime.JSX.Element;
|
|
92
|
+
|
|
93
|
+
export { type ExportOptions, GradientAvatar, type GradientAvatarProps, type GradientPalette, type Harmony, type RenderOptions, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { useRef, useEffect } from 'react';
|
|
2
|
+
import { jsx } from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
// ../gradient-avatars/dist/index.js
|
|
5
|
+
var HARMONY_TYPES = [
|
|
6
|
+
"analogous",
|
|
7
|
+
"triadic",
|
|
8
|
+
"splitComplementary",
|
|
9
|
+
"tetradic",
|
|
10
|
+
"complementary"
|
|
11
|
+
];
|
|
12
|
+
var GOLDEN_ANGLE = 137.5;
|
|
13
|
+
var DEFAULT_BLUR_FRACTION = 0.06;
|
|
14
|
+
function seededRandom(seed) {
|
|
15
|
+
let s = seed;
|
|
16
|
+
return () => {
|
|
17
|
+
s += 1831565813;
|
|
18
|
+
let t = s;
|
|
19
|
+
t = Math.imul(t ^ t >>> 15, t | 1);
|
|
20
|
+
t ^= t + Math.imul(t ^ t >>> 7, t | 61);
|
|
21
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function hslToHex(h, s, l) {
|
|
25
|
+
h = (h % 360 + 360) % 360;
|
|
26
|
+
s = Math.max(0, Math.min(100, s)) / 100;
|
|
27
|
+
l = Math.max(0, Math.min(100, l)) / 100;
|
|
28
|
+
const c = (1 - Math.abs(2 * l - 1)) * s;
|
|
29
|
+
const x = c * (1 - Math.abs(h / 60 % 2 - 1));
|
|
30
|
+
const m = l - c / 2;
|
|
31
|
+
let r = 0;
|
|
32
|
+
let g = 0;
|
|
33
|
+
let b = 0;
|
|
34
|
+
if (h < 60) {
|
|
35
|
+
r = c;
|
|
36
|
+
g = x;
|
|
37
|
+
} else if (h < 120) {
|
|
38
|
+
r = x;
|
|
39
|
+
g = c;
|
|
40
|
+
} else if (h < 180) {
|
|
41
|
+
g = c;
|
|
42
|
+
b = x;
|
|
43
|
+
} else if (h < 240) {
|
|
44
|
+
g = x;
|
|
45
|
+
b = c;
|
|
46
|
+
} else if (h < 300) {
|
|
47
|
+
r = x;
|
|
48
|
+
b = c;
|
|
49
|
+
} else {
|
|
50
|
+
r = c;
|
|
51
|
+
b = x;
|
|
52
|
+
}
|
|
53
|
+
const toHex = (n) => {
|
|
54
|
+
const hex = Math.round((n + m) * 255).toString(16);
|
|
55
|
+
return hex.length === 1 ? `0${hex}` : hex;
|
|
56
|
+
};
|
|
57
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();
|
|
58
|
+
}
|
|
59
|
+
function harmonyHues(baseHue, harmony) {
|
|
60
|
+
switch (harmony) {
|
|
61
|
+
case "analogous":
|
|
62
|
+
return [baseHue, baseHue + 30, baseHue + 60, baseHue - 30];
|
|
63
|
+
case "triadic":
|
|
64
|
+
return [baseHue, baseHue + 120, baseHue + 240];
|
|
65
|
+
case "splitComplementary":
|
|
66
|
+
return [baseHue, baseHue + 150, baseHue + 210];
|
|
67
|
+
case "tetradic":
|
|
68
|
+
return [baseHue, baseHue + 90, baseHue + 180, baseHue + 270];
|
|
69
|
+
case "complementary":
|
|
70
|
+
return [baseHue, baseHue + 180, baseHue + 20, baseHue + 200];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function seedFromString(input) {
|
|
74
|
+
let h = 2166136261 >>> 0;
|
|
75
|
+
for (let i = 0; i < input.length; i++) {
|
|
76
|
+
h ^= input.charCodeAt(i);
|
|
77
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
78
|
+
}
|
|
79
|
+
h ^= h >>> 16;
|
|
80
|
+
h = Math.imul(h, 2146121005) >>> 0;
|
|
81
|
+
h ^= h >>> 15;
|
|
82
|
+
h = Math.imul(h, 2221713035) >>> 0;
|
|
83
|
+
h ^= h >>> 16;
|
|
84
|
+
return h >>> 0;
|
|
85
|
+
}
|
|
86
|
+
function toSeed(seed) {
|
|
87
|
+
if (typeof seed === "number") return seed;
|
|
88
|
+
return seedFromString(seed);
|
|
89
|
+
}
|
|
90
|
+
function generatePalette(seed) {
|
|
91
|
+
const s = toSeed(seed);
|
|
92
|
+
const random = seededRandom(s);
|
|
93
|
+
const baseHue = s * GOLDEN_ANGLE % 360;
|
|
94
|
+
const harmonyIndex = Math.floor(random() * HARMONY_TYPES.length);
|
|
95
|
+
const harmony = HARMONY_TYPES[harmonyIndex];
|
|
96
|
+
const hues = harmonyHues(baseHue, harmony);
|
|
97
|
+
const colors = hues.map((hue) => {
|
|
98
|
+
const saturation = 75 + random() * 25;
|
|
99
|
+
const lightness = 50 + random() * 20;
|
|
100
|
+
return hslToHex(hue, saturation, lightness);
|
|
101
|
+
});
|
|
102
|
+
return { seed: s, colors, harmony };
|
|
103
|
+
}
|
|
104
|
+
function drawMeshGradient(ctx, seed, size) {
|
|
105
|
+
const s = toSeed(seed);
|
|
106
|
+
const { colors } = generatePalette(s);
|
|
107
|
+
const random = seededRandom(s * 12345);
|
|
108
|
+
ctx.fillStyle = colors[0];
|
|
109
|
+
ctx.fillRect(0, 0, size, size);
|
|
110
|
+
const numSpots = 8 + Math.floor(random() * 5);
|
|
111
|
+
const spots = [];
|
|
112
|
+
for (let i = 0; i < numSpots; i++) {
|
|
113
|
+
const angle = random() * Math.PI * 2;
|
|
114
|
+
const distance = random() * size * 0.4;
|
|
115
|
+
const centerX = size / 2 + Math.cos(angle) * distance;
|
|
116
|
+
const centerY = size / 2 + Math.sin(angle) * distance;
|
|
117
|
+
spots.push({
|
|
118
|
+
x: centerX + (random() - 0.5) * size * 0.3,
|
|
119
|
+
y: centerY + (random() - 0.5) * size * 0.3,
|
|
120
|
+
radius: size * (0.3 + random() * 0.4),
|
|
121
|
+
color: colors[i % colors.length]
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
spots.sort((a, b) => b.radius - a.radius);
|
|
125
|
+
ctx.globalCompositeOperation = "source-over";
|
|
126
|
+
for (const spot of spots) {
|
|
127
|
+
const g = ctx.createRadialGradient(
|
|
128
|
+
spot.x,
|
|
129
|
+
spot.y,
|
|
130
|
+
0,
|
|
131
|
+
spot.x,
|
|
132
|
+
spot.y,
|
|
133
|
+
spot.radius
|
|
134
|
+
);
|
|
135
|
+
g.addColorStop(0, `${spot.color}FF`);
|
|
136
|
+
g.addColorStop(0.3, `${spot.color}DD`);
|
|
137
|
+
g.addColorStop(0.6, `${spot.color}88`);
|
|
138
|
+
g.addColorStop(1, `${spot.color}00`);
|
|
139
|
+
ctx.fillStyle = g;
|
|
140
|
+
ctx.fillRect(0, 0, size, size);
|
|
141
|
+
}
|
|
142
|
+
const hx = size * 0.3 + random() * size * 0.2;
|
|
143
|
+
const hy = size * 0.3 + random() * size * 0.2;
|
|
144
|
+
const hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, size * 0.3);
|
|
145
|
+
hg.addColorStop(0, "rgba(255,255,255,0.15)");
|
|
146
|
+
hg.addColorStop(1, "rgba(255,255,255,0)");
|
|
147
|
+
ctx.fillStyle = hg;
|
|
148
|
+
ctx.fillRect(0, 0, size, size);
|
|
149
|
+
}
|
|
150
|
+
function blurFor(size, blur) {
|
|
151
|
+
if (blur === 0) return 0;
|
|
152
|
+
return blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);
|
|
153
|
+
}
|
|
154
|
+
function renderGradient(canvas, seed, options = {}) {
|
|
155
|
+
const size = canvas.width;
|
|
156
|
+
const blur = blurFor(size, options.blur);
|
|
157
|
+
const ctx = canvas.getContext("2d");
|
|
158
|
+
if (!ctx) return;
|
|
159
|
+
if (blur <= 0) {
|
|
160
|
+
ctx.clearRect(0, 0, size, size);
|
|
161
|
+
drawMeshGradient(ctx, seed, size);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const scratch = createCanvas(size, size);
|
|
165
|
+
const sctx = scratch.getContext("2d");
|
|
166
|
+
if (!sctx) return;
|
|
167
|
+
drawMeshGradient(sctx, seed, size);
|
|
168
|
+
const scaleUp = 1 + blur / size * 4;
|
|
169
|
+
const dw = size * scaleUp;
|
|
170
|
+
const offset = (dw - size) / 2;
|
|
171
|
+
ctx.clearRect(0, 0, size, size);
|
|
172
|
+
ctx.filter = `blur(${blur}px)`;
|
|
173
|
+
ctx.drawImage(scratch, -offset, -offset, dw, dw);
|
|
174
|
+
ctx.filter = "none";
|
|
175
|
+
}
|
|
176
|
+
function createCanvas(w, h) {
|
|
177
|
+
if (typeof OffscreenCanvas !== "undefined") {
|
|
178
|
+
return new OffscreenCanvas(w, h);
|
|
179
|
+
}
|
|
180
|
+
const c = document.createElement("canvas");
|
|
181
|
+
c.width = w;
|
|
182
|
+
c.height = h;
|
|
183
|
+
return c;
|
|
184
|
+
}
|
|
185
|
+
function gradientToDataURL(seed, options = {}) {
|
|
186
|
+
const { size = 512, type = "image/png", quality = 0.92 } = options;
|
|
187
|
+
const canvas = document.createElement("canvas");
|
|
188
|
+
canvas.width = size;
|
|
189
|
+
canvas.height = size;
|
|
190
|
+
renderGradient(canvas, seed, options);
|
|
191
|
+
return canvas.toDataURL(type, quality);
|
|
192
|
+
}
|
|
193
|
+
function gradientToBlob(seed, options = {}) {
|
|
194
|
+
const { size = 512, type = "image/png", quality = 0.92 } = options;
|
|
195
|
+
const canvas = document.createElement("canvas");
|
|
196
|
+
canvas.width = size;
|
|
197
|
+
canvas.height = size;
|
|
198
|
+
renderGradient(canvas, seed, options);
|
|
199
|
+
return new Promise((resolve) => canvas.toBlob(resolve, type, quality));
|
|
200
|
+
}
|
|
201
|
+
var RENDER_SIZE = 256;
|
|
202
|
+
var BLUR_FRACTION = 0.06;
|
|
203
|
+
function GradientAvatar({
|
|
204
|
+
seed,
|
|
205
|
+
size = 32,
|
|
206
|
+
radius = "9999px",
|
|
207
|
+
className,
|
|
208
|
+
style
|
|
209
|
+
}) {
|
|
210
|
+
const canvasRef = useRef(null);
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
const canvas = canvasRef.current;
|
|
213
|
+
if (!canvas) return;
|
|
214
|
+
const ctx = canvas.getContext("2d");
|
|
215
|
+
if (!ctx) return;
|
|
216
|
+
ctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);
|
|
217
|
+
drawMeshGradient(ctx, seed, RENDER_SIZE);
|
|
218
|
+
}, [seed]);
|
|
219
|
+
const blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));
|
|
220
|
+
return /* @__PURE__ */ jsx(
|
|
221
|
+
"span",
|
|
222
|
+
{
|
|
223
|
+
className,
|
|
224
|
+
style: {
|
|
225
|
+
display: "inline-block",
|
|
226
|
+
overflow: "hidden",
|
|
227
|
+
borderRadius: radius,
|
|
228
|
+
width: size,
|
|
229
|
+
height: size,
|
|
230
|
+
...style
|
|
231
|
+
},
|
|
232
|
+
children: /* @__PURE__ */ jsx(
|
|
233
|
+
"canvas",
|
|
234
|
+
{
|
|
235
|
+
ref: canvasRef,
|
|
236
|
+
width: RENDER_SIZE,
|
|
237
|
+
height: RENDER_SIZE,
|
|
238
|
+
style: {
|
|
239
|
+
width: "100%",
|
|
240
|
+
height: "100%",
|
|
241
|
+
display: "block",
|
|
242
|
+
filter: `blur(${blurPx}px)`
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
)
|
|
246
|
+
}
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export { GradientAvatar, drawMeshGradient, generatePalette, gradientToBlob, gradientToDataURL, renderGradient, seedFromString, toSeed };
|
|
251
|
+
//# sourceMappingURL=index.js.map
|
|
252
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../gradient-avatars/src/index.ts","../src/index.tsx"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @outpacelabs/gradient-avatars\n *\n * Framework-agnostic mesh-gradient avatar generator. Every seed (string or\n * number) deterministically produces a unique gradient — no stored images,\n * no network. Pure palette/RNG core plus optional Canvas2D render helpers.\n */\n\nexport type Harmony =\n\t| \"analogous\"\n\t| \"triadic\"\n\t| \"splitComplementary\"\n\t| \"tetradic\"\n\t| \"complementary\";\n\nexport interface GradientPalette {\n\t/** The numeric seed the palette was derived from. */\n\tseed: number;\n\t/** Hex color stops used to paint the mesh. */\n\tcolors: string[];\n\t/** Which color-harmony rule produced the hues. */\n\tharmony: Harmony;\n}\n\nconst HARMONY_TYPES: Harmony[] = [\n\t\"analogous\",\n\t\"triadic\",\n\t\"splitComplementary\",\n\t\"tetradic\",\n\t\"complementary\",\n];\n\nconst GOLDEN_ANGLE = 137.5;\n\n/** Default blur radius as a fraction of the rendered dimension. */\nexport const DEFAULT_BLUR_FRACTION = 0.06;\n\nfunction seededRandom(seed: number): () => number {\n\tlet s = seed;\n\treturn () => {\n\t\ts += 0x6d2b79f5;\n\t\tlet t = s;\n\t\tt = Math.imul(t ^ (t >>> 15), t | 1);\n\t\tt ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n\t\treturn ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n\t};\n}\n\nfunction hslToHex(h: number, s: number, l: number): string {\n\th = ((h % 360) + 360) % 360;\n\ts = Math.max(0, Math.min(100, s)) / 100;\n\tl = Math.max(0, Math.min(100, l)) / 100;\n\n\tconst c = (1 - Math.abs(2 * l - 1)) * s;\n\tconst x = c * (1 - Math.abs(((h / 60) % 2) - 1));\n\tconst m = l - c / 2;\n\n\tlet r = 0;\n\tlet g = 0;\n\tlet b = 0;\n\tif (h < 60) {\n\t\tr = c;\n\t\tg = x;\n\t} else if (h < 120) {\n\t\tr = x;\n\t\tg = c;\n\t} else if (h < 180) {\n\t\tg = c;\n\t\tb = x;\n\t} else if (h < 240) {\n\t\tg = x;\n\t\tb = c;\n\t} else if (h < 300) {\n\t\tr = x;\n\t\tb = c;\n\t} else {\n\t\tr = c;\n\t\tb = x;\n\t}\n\n\tconst toHex = (n: number) => {\n\t\tconst hex = Math.round((n + m) * 255).toString(16);\n\t\treturn hex.length === 1 ? `0${hex}` : hex;\n\t};\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n\nfunction harmonyHues(baseHue: number, harmony: Harmony): number[] {\n\tswitch (harmony) {\n\t\tcase \"analogous\":\n\t\t\treturn [baseHue, baseHue + 30, baseHue + 60, baseHue - 30];\n\t\tcase \"triadic\":\n\t\t\treturn [baseHue, baseHue + 120, baseHue + 240];\n\t\tcase \"splitComplementary\":\n\t\t\treturn [baseHue, baseHue + 150, baseHue + 210];\n\t\tcase \"tetradic\":\n\t\t\treturn [baseHue, baseHue + 90, baseHue + 180, baseHue + 270];\n\t\tcase \"complementary\":\n\t\t\treturn [baseHue, baseHue + 180, baseHue + 20, baseHue + 200];\n\t}\n}\n\n/**\n * Stable string → 32-bit unsigned hash (FNV-1a + bit-mixing avalanche).\n * Uses the full uint32 range as a seed so similar strings diverge fully.\n */\nexport function seedFromString(input: string): number {\n\tlet h = 2166136261 >>> 0;\n\tfor (let i = 0; i < input.length; i++) {\n\t\th ^= input.charCodeAt(i);\n\t\th = Math.imul(h, 16777619) >>> 0;\n\t}\n\th ^= h >>> 16;\n\th = Math.imul(h, 0x7feb352d) >>> 0;\n\th ^= h >>> 15;\n\th = Math.imul(h, 0x846ca68b) >>> 0;\n\th ^= h >>> 16;\n\treturn h >>> 0;\n}\n\n/** Normalize a string or number seed to the numeric seed used internally. */\nexport function toSeed(seed: number | string): number {\n\tif (typeof seed === \"number\") return seed;\n\treturn seedFromString(seed);\n}\n\n/** Derive the deterministic color palette for a seed. */\nexport function generatePalette(seed: number | string): GradientPalette {\n\tconst s = toSeed(seed);\n\tconst random = seededRandom(s);\n\tconst baseHue = (s * GOLDEN_ANGLE) % 360;\n\tconst harmonyIndex = Math.floor(random() * HARMONY_TYPES.length);\n\tconst harmony = HARMONY_TYPES[harmonyIndex];\n\tconst hues = harmonyHues(baseHue, harmony);\n\tconst colors = hues.map((hue) => {\n\t\tconst saturation = 75 + random() * 25;\n\t\tconst lightness = 50 + random() * 20;\n\t\treturn hslToHex(hue, saturation, lightness);\n\t});\n\treturn { seed: s, colors, harmony };\n}\n\n/**\n * Minimal Canvas2D context surface the renderer needs. Both\n * `HTMLCanvasElement` and `OffscreenCanvas` 2D contexts satisfy it.\n */\nexport type GradientContext = {\n\tfillStyle: string | CanvasGradient | CanvasPattern;\n\tglobalCompositeOperation: GlobalCompositeOperation;\n\tfillRect(x: number, y: number, w: number, h: number): void;\n\tcreateRadialGradient(\n\t\tx0: number,\n\t\ty0: number,\n\t\tr0: number,\n\t\tx1: number,\n\t\ty1: number,\n\t\tr1: number,\n\t): CanvasGradient;\n};\n\n/**\n * Draw the mesh gradient for `seed` into `ctx` at `size` x `size`.\n * The caller is responsible for any blur — apply `filter: blur(…)` on the\n * displayed canvas (≈6% of the rendered dimension) for the signature look,\n * or use {@link renderGradient} / {@link gradientToDataURL} which bake it in.\n */\nexport function drawMeshGradient(\n\tctx: GradientContext,\n\tseed: number | string,\n\tsize: number,\n): void {\n\tconst s = toSeed(seed);\n\tconst { colors } = generatePalette(s);\n\tconst random = seededRandom(s * 12345);\n\n\tctx.fillStyle = colors[0];\n\tctx.fillRect(0, 0, size, size);\n\n\tconst numSpots = 8 + Math.floor(random() * 5);\n\tconst spots: Array<{ x: number; y: number; radius: number; color: string }> =\n\t\t[];\n\n\tfor (let i = 0; i < numSpots; i++) {\n\t\tconst angle = random() * Math.PI * 2;\n\t\tconst distance = random() * size * 0.4;\n\t\tconst centerX = size / 2 + Math.cos(angle) * distance;\n\t\tconst centerY = size / 2 + Math.sin(angle) * distance;\n\t\tspots.push({\n\t\t\tx: centerX + (random() - 0.5) * size * 0.3,\n\t\t\ty: centerY + (random() - 0.5) * size * 0.3,\n\t\t\tradius: size * (0.3 + random() * 0.4),\n\t\t\tcolor: colors[i % colors.length],\n\t\t});\n\t}\n\n\tspots.sort((a, b) => b.radius - a.radius);\n\n\tctx.globalCompositeOperation = \"source-over\";\n\tfor (const spot of spots) {\n\t\tconst g = ctx.createRadialGradient(\n\t\t\tspot.x,\n\t\t\tspot.y,\n\t\t\t0,\n\t\t\tspot.x,\n\t\t\tspot.y,\n\t\t\tspot.radius,\n\t\t);\n\t\tg.addColorStop(0, `${spot.color}FF`);\n\t\tg.addColorStop(0.3, `${spot.color}DD`);\n\t\tg.addColorStop(0.6, `${spot.color}88`);\n\t\tg.addColorStop(1, `${spot.color}00`);\n\t\tctx.fillStyle = g;\n\t\tctx.fillRect(0, 0, size, size);\n\t}\n\n\tconst hx = size * 0.3 + random() * size * 0.2;\n\tconst hy = size * 0.3 + random() * size * 0.2;\n\tconst hg = ctx.createRadialGradient(hx, hy, 0, hx, hy, size * 0.3);\n\thg.addColorStop(0, \"rgba(255,255,255,0.15)\");\n\thg.addColorStop(1, \"rgba(255,255,255,0)\");\n\tctx.fillStyle = hg;\n\tctx.fillRect(0, 0, size, size);\n}\n\nexport interface RenderOptions {\n\t/**\n\t * Blur radius in pixels. Defaults to ~6% of the canvas size for the\n\t * signature soft look. Pass `0` to disable.\n\t */\n\tblur?: number;\n}\n\nfunction blurFor(size: number, blur?: number): number {\n\tif (blur === 0) return 0;\n\treturn blur ?? Math.round(size * DEFAULT_BLUR_FRACTION);\n}\n\n/**\n * Render a seed's gradient into an existing canvas, baking in the soft blur.\n * Draws at the canvas's current `width`/`height`. Browser/OffscreenCanvas only.\n */\nexport function renderGradient(\n\tcanvas: HTMLCanvasElement | OffscreenCanvas,\n\tseed: number | string,\n\toptions: RenderOptions = {},\n): void {\n\tconst size = canvas.width;\n\tconst blur = blurFor(size, options.blur);\n\n\tconst ctx = canvas.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!ctx) return;\n\n\tif (blur <= 0) {\n\t\tctx.clearRect(0, 0, size, size);\n\t\tdrawMeshGradient(ctx, seed, size);\n\t\treturn;\n\t}\n\n\t// Draw the raw mesh on a scratch canvas, then composite it back with blur\n\t// scaled up slightly so the soft edges fall outside the frame (no ring).\n\tconst scratch = createCanvas(size, size);\n\tconst sctx = scratch.getContext(\"2d\") as CanvasRenderingContext2D | null;\n\tif (!sctx) return;\n\tdrawMeshGradient(sctx, seed, size);\n\n\tconst scaleUp = 1 + (blur / size) * 4;\n\tconst dw = size * scaleUp;\n\tconst offset = (dw - size) / 2;\n\tctx.clearRect(0, 0, size, size);\n\tctx.filter = `blur(${blur}px)`;\n\tctx.drawImage(scratch as CanvasImageSource, -offset, -offset, dw, dw);\n\tctx.filter = \"none\";\n}\n\nfunction createCanvas(\n\tw: number,\n\th: number,\n): HTMLCanvasElement | OffscreenCanvas {\n\tif (typeof OffscreenCanvas !== \"undefined\") {\n\t\treturn new OffscreenCanvas(w, h);\n\t}\n\tconst c = document.createElement(\"canvas\");\n\tc.width = w;\n\tc.height = h;\n\treturn c;\n}\n\nexport interface ExportOptions extends RenderOptions {\n\t/** Output pixel dimensions (square). Default: 512. */\n\tsize?: number;\n\t/** Image MIME type. Default: \"image/png\". */\n\ttype?: string;\n\t/** Quality 0–1 for lossy types. Default: 0.92. */\n\tquality?: number;\n}\n\n/** Render a seed's gradient and return it as a data URL. Browser only. */\nexport function gradientToDataURL(\n\tseed: number | string,\n\toptions: ExportOptions = {},\n): string {\n\tconst { size = 512, type = \"image/png\", quality = 0.92 } = options;\n\tconst canvas = document.createElement(\"canvas\");\n\tcanvas.width = size;\n\tcanvas.height = size;\n\trenderGradient(canvas, seed, options);\n\treturn canvas.toDataURL(type, quality);\n}\n\n/** Render a seed's gradient and resolve a Blob (or null). Browser only. */\nexport function gradientToBlob(\n\tseed: number | string,\n\toptions: ExportOptions = {},\n): Promise<Blob | null> {\n\tconst { size = 512, type = \"image/png\", quality = 0.92 } = options;\n\tconst canvas = document.createElement(\"canvas\");\n\tcanvas.width = size;\n\tcanvas.height = size;\n\trenderGradient(canvas, seed, options);\n\treturn new Promise((resolve) => canvas.toBlob(resolve, type, quality));\n}\n","import { drawMeshGradient } from \"@outpacelabs/gradient-avatars\";\nimport type { CSSProperties } from \"react\";\nimport { useEffect, useRef } from \"react\";\n\nexport interface GradientAvatarProps {\n\t/** Any string or number — each unique seed produces a unique gradient. */\n\tseed: number | string;\n\t/** Rendered size in pixels. Default: 32. */\n\tsize?: number;\n\t/**\n\t * Corner radius. Number = pixels, string = any CSS length.\n\t * Defaults to a full circle; pass `0` for a square or e.g. `12` for a\n\t * rounded square. Default: \"9999px\".\n\t */\n\tradius?: number | string;\n\t/** Additional CSS classes on the wrapper. */\n\tclassName?: string;\n\t/** Extra inline styles merged onto the wrapper. */\n\tstyle?: CSSProperties;\n}\n\n/** Internal render resolution. Higher than display size so the CSS blur is smooth. */\nconst RENDER_SIZE = 256;\n/** Blur radius as a fraction of display size. */\nconst BLUR_FRACTION = 0.06;\n\n/**\n * Renders a deterministic mesh-gradient avatar on a `<canvas>`.\n * The same seed always produces the same gradient.\n */\nexport function GradientAvatar({\n\tseed,\n\tsize = 32,\n\tradius = \"9999px\",\n\tclassName,\n\tstyle,\n}: GradientAvatarProps) {\n\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\n\tuseEffect(() => {\n\t\tconst canvas = canvasRef.current;\n\t\tif (!canvas) return;\n\t\tconst ctx = canvas.getContext(\"2d\");\n\t\tif (!ctx) return;\n\t\tctx.clearRect(0, 0, RENDER_SIZE, RENDER_SIZE);\n\t\tdrawMeshGradient(ctx, seed, RENDER_SIZE);\n\t}, [seed]);\n\n\tconst blurPx = Math.max(1, Math.round(size * BLUR_FRACTION));\n\n\treturn (\n\t\t<span\n\t\t\tclassName={className}\n\t\t\tstyle={{\n\t\t\t\tdisplay: \"inline-block\",\n\t\t\t\toverflow: \"hidden\",\n\t\t\t\tborderRadius: radius,\n\t\t\t\twidth: size,\n\t\t\t\theight: size,\n\t\t\t\t...style,\n\t\t\t}}\n\t\t>\n\t\t\t<canvas\n\t\t\t\tref={canvasRef}\n\t\t\t\twidth={RENDER_SIZE}\n\t\t\t\theight={RENDER_SIZE}\n\t\t\t\tstyle={{\n\t\t\t\t\twidth: \"100%\",\n\t\t\t\t\theight: \"100%\",\n\t\t\t\t\tdisplay: \"block\",\n\t\t\t\t\tfilter: `blur(${blurPx}px)`,\n\t\t\t\t}}\n\t\t\t/>\n\t\t</span>\n\t);\n}\n\nexport type {\n\tExportOptions,\n\tGradientPalette,\n\tHarmony,\n\tRenderOptions,\n} from \"@outpacelabs/gradient-avatars\";\nexport {\n\tdrawMeshGradient,\n\tgeneratePalette,\n\tgradientToBlob,\n\tgradientToDataURL,\n\trenderGradient,\n\tseedFromString,\n\ttoSeed,\n} from \"@outpacelabs/gradient-avatars\";\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@outpacelabs/gradient-avatars-react",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "React component for @outpacelabs/gradient-avatars — deterministic mesh-gradient avatars, one per seed.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"avatars",
|
|
7
|
+
"gradient",
|
|
8
|
+
"mesh-gradient",
|
|
9
|
+
"generative",
|
|
10
|
+
"react",
|
|
11
|
+
"profile-pictures"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://avatars.outpacestudios.com",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/outpacelabs/avatars.git",
|
|
17
|
+
"directory": "packages/gradient-avatars-react"
|
|
18
|
+
},
|
|
19
|
+
"bugs": {
|
|
20
|
+
"url": "https://github.com/outpacelabs/avatars/issues"
|
|
21
|
+
},
|
|
22
|
+
"author": "Outpace Studios (https://outpacestudios.com)",
|
|
23
|
+
"license": "CC-BY-4.0",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"sideEffects": false,
|
|
26
|
+
"main": "./dist/index.cjs",
|
|
27
|
+
"module": "./dist/index.js",
|
|
28
|
+
"types": "./dist/index.d.ts",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js",
|
|
33
|
+
"require": "./dist/index.cjs"
|
|
34
|
+
},
|
|
35
|
+
"./package.json": "./package.json"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"dist",
|
|
39
|
+
"README.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"peerDependencies": {
|
|
43
|
+
"react": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/react": "^19.2.10",
|
|
47
|
+
"react": "^19.2.4",
|
|
48
|
+
"tsup": "^8.3.5",
|
|
49
|
+
"typescript": "^5.9.3",
|
|
50
|
+
"@outpacelabs/gradient-avatars": "0.1.0"
|
|
51
|
+
},
|
|
52
|
+
"publishConfig": {
|
|
53
|
+
"access": "public",
|
|
54
|
+
"registry": "https://registry.npmjs.org/"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"dev": "tsup --watch",
|
|
59
|
+
"typecheck": "tsc --noEmit"
|
|
60
|
+
}
|
|
61
|
+
}
|