palette-shader 0.7.0 → 0.8.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 +17 -13
- package/package.json +1 -1
- package/src/index.ts +16 -47
- package/src/types.ts +2 -2
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ So if one of your palette colors only claims a tiny sliver, it lives very close
|
|
|
24
24
|
npm install palette-shader
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
No dependencies — only a browser with
|
|
27
|
+
No runtime dependencies — only a browser with WebGL2 support is required. Colors must be passed as `[r, g, b]` arrays with values in the `0–1` range (linear sRGB). Use a library such as [culori](https://culorijs.org/) to convert from CSS strings if needed.
|
|
28
28
|
|
|
29
29
|
---
|
|
30
30
|
|
|
@@ -32,17 +32,21 @@ No dependencies — only a browser with WebGL support is required.
|
|
|
32
32
|
|
|
33
33
|
```js
|
|
34
34
|
import { PaletteViz } from 'palette-shader';
|
|
35
|
+
import { converter } from 'culori';
|
|
36
|
+
|
|
37
|
+
const toSRGB = converter('srgb');
|
|
38
|
+
const toRGB = (hex) => { const c = toSRGB(hex); return [c.r, c.g, c.b]; };
|
|
35
39
|
|
|
36
40
|
// option A — pass a container, canvas is appended automatically
|
|
37
41
|
const viz = new PaletteViz({
|
|
38
|
-
palette: ['#264653', '#2a9d8f', '#e9c46a', '#f4a261', '#e76f51'],
|
|
42
|
+
palette: ['#264653', '#2a9d8f', '#e9c46a', '#f4a261', '#e76f51'].map(toRGB),
|
|
39
43
|
container: document.querySelector('#app'),
|
|
40
44
|
width: 512,
|
|
41
45
|
height: 512,
|
|
42
46
|
});
|
|
43
47
|
|
|
44
48
|
// option B — no container, place the canvas yourself
|
|
45
|
-
const viz = new PaletteViz({ palette: ['#264653', '#2a9d8f', '#e9c46a'] });
|
|
49
|
+
const viz = new PaletteViz({ palette: ['#264653', '#2a9d8f', '#e9c46a'].map(toRGB) });
|
|
46
50
|
document.querySelector('#app').appendChild(viz.canvas);
|
|
47
51
|
```
|
|
48
52
|
|
|
@@ -58,7 +62,7 @@ All options are optional. The palette defaults to a random 20-colour set.
|
|
|
58
62
|
|
|
59
63
|
| Option | Type | Default | Description |
|
|
60
64
|
| ---------------- | ------------------- | ------------------ | ------------------------------------------------------------------------------------------------------------ |
|
|
61
|
-
| `palette` | `
|
|
65
|
+
| `palette` | `[number, number, number][]` | random | sRGB colours as `[r, g, b]` arrays, each component in the `0–1` range |
|
|
62
66
|
| `container` | `HTMLElement` | `undefined` | Element the canvas is appended to. Omit and use `viz.canvas` to place it yourself |
|
|
63
67
|
| `width` | `number` | `512` | Canvas width in CSS pixels |
|
|
64
68
|
| `height` | `number` | `512` | Canvas height in CSS pixels |
|
|
@@ -78,7 +82,7 @@ All options are optional. The palette defaults to a random 20-colour set.
|
|
|
78
82
|
Every constructor option is also a live setter/getter. Assigning any of them re-renders immediately via `requestAnimationFrame`.
|
|
79
83
|
|
|
80
84
|
```js
|
|
81
|
-
viz.palette = [
|
|
85
|
+
viz.palette = [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
|
|
82
86
|
viz.position = 0.5;
|
|
83
87
|
viz.colorModel = 'okhslPolar';
|
|
84
88
|
viz.distanceMetric = 'deltaE2000';
|
|
@@ -112,7 +116,7 @@ window.addEventListener('resize', () => viz.resize(window.innerWidth * 0.5));
|
|
|
112
116
|
Update a single palette entry without rebuilding the whole texture.
|
|
113
117
|
|
|
114
118
|
```js
|
|
115
|
-
viz.setColor(
|
|
119
|
+
viz.setColor([0.902, 0.224, 0.275], 2);
|
|
116
120
|
```
|
|
117
121
|
|
|
118
122
|
### `addColor(color, index?)`
|
|
@@ -120,17 +124,17 @@ viz.setColor('#e63946', 2);
|
|
|
120
124
|
Insert a colour at `index` (appends if omitted).
|
|
121
125
|
|
|
122
126
|
```js
|
|
123
|
-
viz.addColor(
|
|
124
|
-
viz.addColor(
|
|
127
|
+
viz.addColor([0.659, 0.855, 0.863]); // append
|
|
128
|
+
viz.addColor([0.271, 0.482, 0.616], 0); // prepend
|
|
125
129
|
```
|
|
126
130
|
|
|
127
131
|
### `removeColor(index | color)`
|
|
128
132
|
|
|
129
|
-
Remove a palette entry by index or by colour
|
|
133
|
+
Remove a palette entry by index or by colour value.
|
|
130
134
|
|
|
131
135
|
```js
|
|
132
136
|
viz.removeColor(0);
|
|
133
|
-
viz.removeColor(
|
|
137
|
+
viz.removeColor([0.659, 0.855, 0.863]);
|
|
134
138
|
```
|
|
135
139
|
|
|
136
140
|
### `destroy()`
|
|
@@ -255,7 +259,7 @@ viz.canvas.style.borderRadius = '50%';
|
|
|
255
259
|
### Multiple synchronised views
|
|
256
260
|
|
|
257
261
|
```js
|
|
258
|
-
const palette = ['#264653', '#2a9d8f', '#e9c46a'];
|
|
262
|
+
const palette = ['#264653', '#2a9d8f', '#e9c46a'].map(toRGB);
|
|
259
263
|
const shared = { palette, width: 256, height: 256, container: document.querySelector('#views') };
|
|
260
264
|
|
|
261
265
|
const views = [
|
|
@@ -277,7 +281,7 @@ document.querySelector('#slider').addEventListener('input', (e) => {
|
|
|
277
281
|
|
|
278
282
|
```js
|
|
279
283
|
const viz = new PaletteViz({
|
|
280
|
-
palette,
|
|
284
|
+
palette: ['#264653', '#2a9d8f', '#e9c46a'].map(toRGB),
|
|
281
285
|
outlineWidth: 2,
|
|
282
286
|
container: document.querySelector('#app'),
|
|
283
287
|
});
|
|
@@ -300,7 +304,7 @@ import { paletteToRGBA, randomPalette, fragmentShader } from 'palette-shader';
|
|
|
300
304
|
|
|
301
305
|
// Get raw RGBA bytes (Uint8Array, sRGB, 4 bytes per color)
|
|
302
306
|
// Useful for building your own WebGL texture or processing palette data
|
|
303
|
-
const rgba = paletteToRGBA([
|
|
307
|
+
const rgba = paletteToRGBA([[1, 0, 0], [0, 1, 0], [0, 0, 1]]);
|
|
304
308
|
|
|
305
309
|
// Quick random palette for prototyping
|
|
306
310
|
const palette = randomPalette(16);
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
ColorRGB,
|
|
3
3
|
ColorList,
|
|
4
4
|
PaletteVizOptions,
|
|
5
5
|
SupportedColorModels,
|
|
@@ -203,33 +203,6 @@ void main() {
|
|
|
203
203
|
fragColor = center;
|
|
204
204
|
}`;
|
|
205
205
|
|
|
206
|
-
// ── Color parsing ──────────────────────────────────────────────────────────────
|
|
207
|
-
// Use a canvas 2D context as a free CSS color parser — handles hex, rgb(),
|
|
208
|
-
// hsl(), named colors, etc. Lazy-initialised to avoid issues at module load time.
|
|
209
|
-
|
|
210
|
-
let _colorCtx: CanvasRenderingContext2D | null = null;
|
|
211
|
-
|
|
212
|
-
function cssToSRGB(color: string): [number, number, number] {
|
|
213
|
-
if (!_colorCtx) {
|
|
214
|
-
const c = document.createElement('canvas');
|
|
215
|
-
c.width = c.height = 1;
|
|
216
|
-
_colorCtx = c.getContext('2d')!;
|
|
217
|
-
}
|
|
218
|
-
_colorCtx.fillStyle = '#000000'; // reset before setting
|
|
219
|
-
_colorCtx.fillStyle = color;
|
|
220
|
-
const v = _colorCtx.fillStyle; // browser normalises to '#rrggbb' or 'rgba(...)'
|
|
221
|
-
if (v[0] === '#') {
|
|
222
|
-
return [
|
|
223
|
-
parseInt(v.slice(1, 3), 16) / 255,
|
|
224
|
-
parseInt(v.slice(3, 5), 16) / 255,
|
|
225
|
-
parseInt(v.slice(5, 7), 16) / 255,
|
|
226
|
-
];
|
|
227
|
-
}
|
|
228
|
-
// rgba(r, g, b, a) fallback
|
|
229
|
-
const m = v.match(/[\d.]+/g)!;
|
|
230
|
-
return [+m[0] / 255, +m[1] / 255, +m[2] / 255];
|
|
231
|
-
}
|
|
232
|
-
|
|
233
206
|
// ── Palette helpers ────────────────────────────────────────────────────────────
|
|
234
207
|
|
|
235
208
|
// Returns the palette as a flat RGBA Uint8Array (sRGB, 1×N texture row).
|
|
@@ -237,15 +210,10 @@ function cssToSRGB(color: string): [number, number, number] {
|
|
|
237
210
|
export const paletteToRGBA = (palette: ColorList): Uint8Array => {
|
|
238
211
|
const data = new Uint8Array(palette.length * 4);
|
|
239
212
|
palette.forEach((color, i) => {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
data[i * 4 + 2] = Math.round(b * 255);
|
|
245
|
-
data[i * 4 + 3] = 255;
|
|
246
|
-
} catch {
|
|
247
|
-
console.error(`Invalid color: ${color}`);
|
|
248
|
-
}
|
|
213
|
+
data[i * 4 + 0] = Math.round(color[0] * 255);
|
|
214
|
+
data[i * 4 + 1] = Math.round(color[1] * 255);
|
|
215
|
+
data[i * 4 + 2] = Math.round(color[2] * 255);
|
|
216
|
+
data[i * 4 + 3] = 255;
|
|
249
217
|
});
|
|
250
218
|
return data;
|
|
251
219
|
};
|
|
@@ -254,11 +222,7 @@ export const paletteToRGBA = (palette: ColorList): Uint8Array => {
|
|
|
254
222
|
export const paletteToTexture = paletteToRGBA;
|
|
255
223
|
|
|
256
224
|
export const randomPalette = (size = 20): ColorList =>
|
|
257
|
-
Array.from(
|
|
258
|
-
{ length: size },
|
|
259
|
-
() =>
|
|
260
|
-
`rgb(${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)}, ${Math.floor(Math.random() * 256)})`,
|
|
261
|
-
);
|
|
225
|
+
Array.from({ length: size }, () => [Math.random(), Math.random(), Math.random()] as ColorRGB);
|
|
262
226
|
|
|
263
227
|
// ── WebGL helpers ──────────────────────────────────────────────────────────────
|
|
264
228
|
|
|
@@ -631,24 +595,29 @@ export class PaletteViz {
|
|
|
631
595
|
return this.#palette;
|
|
632
596
|
}
|
|
633
597
|
|
|
634
|
-
setColor(color:
|
|
598
|
+
setColor(color: ColorRGB, index: number): void {
|
|
635
599
|
if (index < 0 || index >= this.#palette.length) throw new Error(`Index ${index} out of range`);
|
|
636
600
|
this.#palette[index] = color;
|
|
637
601
|
uploadPaletteTexture(this.#gl, this.#texture!, this.#palette);
|
|
638
602
|
this.#paint();
|
|
639
603
|
}
|
|
640
604
|
|
|
641
|
-
addColor(color:
|
|
605
|
+
addColor(color: ColorRGB, index?: number): void {
|
|
642
606
|
this.#palette.splice(index ?? this.#palette.length, 0, color);
|
|
643
607
|
uploadPaletteTexture(this.#gl, this.#texture!, this.#palette);
|
|
644
608
|
this.#paint();
|
|
645
609
|
}
|
|
646
610
|
|
|
647
611
|
removeColor(index: number): void;
|
|
648
|
-
removeColor(color:
|
|
649
|
-
removeColor(indexOrColor: number |
|
|
612
|
+
removeColor(color: ColorRGB): void;
|
|
613
|
+
removeColor(indexOrColor: number | ColorRGB): void {
|
|
650
614
|
const index =
|
|
651
|
-
typeof indexOrColor === 'number'
|
|
615
|
+
typeof indexOrColor === 'number'
|
|
616
|
+
? indexOrColor
|
|
617
|
+
: this.#palette.findIndex(
|
|
618
|
+
(c) =>
|
|
619
|
+
c[0] === indexOrColor[0] && c[1] === indexOrColor[1] && c[2] === indexOrColor[2],
|
|
620
|
+
);
|
|
652
621
|
if (index === -1) throw new Error('Color not found in palette');
|
|
653
622
|
if (index < 0 || index >= this.#palette.length) throw new Error(`Index ${index} out of range`);
|
|
654
623
|
this.#palette.splice(index, 1);
|
package/src/types.ts
CHANGED