pictoguys 0.1.1 → 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/README.md +171 -3
- package/dist/canvas.cjs +979 -0
- package/dist/canvas.d.cts +93 -0
- package/dist/canvas.d.ts +93 -0
- package/dist/canvas.js +3 -0
- package/dist/{character-DxUH8H-l.d.cts → character-DO9lMb0P.d.cts} +90 -1
- package/dist/{character-DxUH8H-l.d.ts → character-DO9lMb0P.d.ts} +90 -1
- package/dist/chunk-3S3S2E62.js +773 -0
- package/dist/{chunk-ETGCETAD.js → chunk-5GA5OHYB.js} +2 -1
- package/dist/chunk-FTJLHGGV.js +296 -0
- package/dist/chunk-J7URCGNY.js +94 -0
- package/dist/{chunk-7CLVRJRG.js → chunk-O4NM6K7R.js} +210 -89
- package/dist/chunk-WWB5SI2V.js +247 -0
- package/dist/core.cjs +239 -25
- package/dist/core.d.cts +2 -2
- package/dist/core.d.ts +2 -2
- package/dist/core.js +3 -2
- package/dist/index.cjs +1418 -77
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +6 -3
- package/dist/react-canvas.cjs +1243 -0
- package/dist/react-canvas.d.cts +62 -0
- package/dist/react-canvas.d.ts +62 -0
- package/dist/react-canvas.js +4 -0
- package/dist/react.cjs +380 -66
- package/dist/react.d.cts +6 -1
- package/dist/react.d.ts +6 -1
- package/dist/react.js +3 -2
- package/package.json +13 -2
- package/dist/chunk-OF2R5NOW.js +0 -182
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
19
|
<p align="center">
|
|
20
|
-
<img src="https://img.shields.io/badge/version-0.
|
|
20
|
+
<img src="https://img.shields.io/badge/version-0.2.0-ff69b4" alt="version" />
|
|
21
21
|
<img src="https://img.shields.io/badge/TypeScript-ready-3178c6?logo=typescript&logoColor=white" alt="typescript" />
|
|
22
22
|
<img src="https://img.shields.io/badge/React-%E2%89%A5%2017-61dafb?logo=react&logoColor=white" alt="react" />
|
|
23
23
|
<img src="https://img.shields.io/badge/runtime%20deps-0-22c55e" alt="zero deps" />
|
|
@@ -72,8 +72,10 @@ runtime dependencies.
|
|
|
72
72
|
|
|
73
73
|
| Import path | Use it for |
|
|
74
74
|
| ----------- | ---------- |
|
|
75
|
-
| `pictoguys` | React projects that want `<Picto
|
|
76
|
-
| `pictoguys/react` | Only the React component and its props |
|
|
75
|
+
| `pictoguys` | React projects that want `<Picto />`, `<PictoField />`, plus the core helpers |
|
|
76
|
+
| `pictoguys/react` | Only the single-picto React component `<Picto />` and its props |
|
|
77
|
+
| `pictoguys/react-canvas` | Only `<PictoField />` (the canvas batch renderer) and its props |
|
|
78
|
+
| `pictoguys/canvas` | The framework-agnostic batch renderer core, no React |
|
|
77
79
|
| `pictoguys/core` | SVG strings, characters, presets, and catalog helpers without React |
|
|
78
80
|
| `pictoguys/rng` | The tiny deterministic RNG only |
|
|
79
81
|
|
|
@@ -296,6 +298,168 @@ guy.stop() // freeze
|
|
|
296
298
|
> on screen. If that picto is not currently rendered, the call simply does
|
|
297
299
|
> nothing (no crash, no error, just a no-op). So render it first, then animate it.
|
|
298
300
|
|
|
301
|
+
## Rendering many pictos
|
|
302
|
+
|
|
303
|
+
One picto? Reach for `<Picto>`. A whole wall of them (a leaderboard, a member
|
|
304
|
+
directory, a sticker sheet, hundreds or thousands of avatars)? That is where
|
|
305
|
+
`<PictoField>` comes in.
|
|
306
|
+
|
|
307
|
+
`<PictoField>` draws **many pictos onto a single `<canvas>`**, and it can keep
|
|
308
|
+
hundreds to thousands of them moving at 60fps. It is the **recommended way to
|
|
309
|
+
render multiple pictos**. `<Picto>` is not going anywhere and is not deprecated;
|
|
310
|
+
it is simply the right tool for one or a few pictos (or when you specifically
|
|
311
|
+
want real DOM nodes). Both draw the *same* art and play the *same* animations, so
|
|
312
|
+
you can mix them freely.
|
|
313
|
+
|
|
314
|
+
Hand it an array of characters and it lays them out for you:
|
|
315
|
+
|
|
316
|
+
```tsx
|
|
317
|
+
import { picto, PictoField } from 'pictoguys'
|
|
318
|
+
|
|
319
|
+
// build as many little guys as you like
|
|
320
|
+
const chars = React.useMemo(
|
|
321
|
+
() => Array.from({ length: 500 }, (_, i) => picto.character(i)),
|
|
322
|
+
[],
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
export default function Wall() {
|
|
326
|
+
return <PictoField chars={chars} size={64} height="70vh" />
|
|
327
|
+
}
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
That is a 500-picto grid that scrolls smoothly. By default `<PictoField>` owns
|
|
331
|
+
its own scroll viewport (an `overflow:auto` box sized by `height`, default
|
|
332
|
+
`'70vh'`) and only ever draws the pictos you can actually see, so the count
|
|
333
|
+
barely matters.
|
|
334
|
+
|
|
335
|
+
**Lay them out your way.** By default they auto-flow into a grid. Pass `cols` to
|
|
336
|
+
fix the column count, or pass an explicit array of top-left positions:
|
|
337
|
+
|
|
338
|
+
```tsx
|
|
339
|
+
<PictoField chars={chars} cols={10} gap={16} />
|
|
340
|
+
<PictoField chars={chars} layout={[{ x: 0, y: 0 }, { x: 80, y: 0 }, /* ... */]} />
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
**Animate the whole field at once.** The easy way is the declarative `animate`
|
|
344
|
+
prop, which plays one animation on *every* picto:
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
<PictoField chars={chars} animate="breath" /> {/* the whole crowd breathes */}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
The hands-on way is the imperative handle. `<PictoField>` forwards a ref to a
|
|
351
|
+
renderer you can poke directly. The animation target is either a single
|
|
352
|
+
`Character` from your `chars` array or the literal `'all'`:
|
|
353
|
+
|
|
354
|
+
```tsx
|
|
355
|
+
import { picto, PictoField } from 'pictoguys'
|
|
356
|
+
import type { PictoRenderer } from 'pictoguys'
|
|
357
|
+
|
|
358
|
+
function Crowd() {
|
|
359
|
+
const ref = React.useRef<PictoRenderer>(null)
|
|
360
|
+
const chars = React.useMemo(
|
|
361
|
+
() => Array.from({ length: 300 }, (_, i) => picto.character(i)),
|
|
362
|
+
[],
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
return (
|
|
366
|
+
<>
|
|
367
|
+
<PictoField ref={ref} chars={chars} size={64} />
|
|
368
|
+
<button onClick={() => ref.current?.blink('all')}>everyone blink</button>
|
|
369
|
+
<button onClick={() => ref.current?.dance(chars[0])}>just the first one dances</button>
|
|
370
|
+
<button onClick={() => ref.current?.stop('all')}>chill</button>
|
|
371
|
+
</>
|
|
372
|
+
)
|
|
373
|
+
}
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
The handle exposes `blink`, `jump`, `breath`, `dance`, `sleep`, `stop`, and a
|
|
377
|
+
general `play(target, name)` (pass `name: null` to stop), plus `start()`,
|
|
378
|
+
`dispose()`, and a `metrics()` peek. (Note: the looping animation string is
|
|
379
|
+
`'sleeping'`, but the method is `sleep()`.)
|
|
380
|
+
|
|
381
|
+
### Flat or fancy: the `variant` prop
|
|
382
|
+
|
|
383
|
+
Both `<Picto>` and `<PictoField>` take a `variant` prop:
|
|
384
|
+
|
|
385
|
+
| Variant | Look |
|
|
386
|
+
| --------- | -------------------------------------------------------------- |
|
|
387
|
+
| `'fancy'` | The original look: gradient body plus soft shadows. **Default.** |
|
|
388
|
+
| `'flat'` | Drops the body gradient and the body shadow (the eye shadows stay). Cheaper to paint, which is handy across a big grid. |
|
|
389
|
+
|
|
390
|
+
```tsx
|
|
391
|
+
<Picto seed="Bloop" variant="flat" />
|
|
392
|
+
<PictoField chars={chars} variant="flat" /> {/* lighter paint for huge fields */}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
`fancy` is the default everywhere, so leave it off if you want the classic look.
|
|
396
|
+
|
|
397
|
+
### Best practices for many pictos
|
|
398
|
+
|
|
399
|
+
- **100+ pictos? Use `<PictoField>` (canvas), not a pile of `<Picto>`s.** One
|
|
400
|
+
canvas with culling beats hundreds of DOM nodes.
|
|
401
|
+
- **Reach for `variant="flat"` on very large grids.** It skips the gradient and
|
|
402
|
+
shadow, so each tile is cheaper to paint.
|
|
403
|
+
- **Reuse seeds.** Identical characters share one cached sprite, so a grid full of
|
|
404
|
+
repeats is nearly free to draw.
|
|
405
|
+
- **Let `<PictoField>` own its scroller** via the `height` prop (default `'70vh'`).
|
|
406
|
+
Only pass `scrollParentRef` when the field must scroll inside an existing scroll
|
|
407
|
+
container you already control.
|
|
408
|
+
- **Animate via the ref (`'all'` or a single character) or the `animate` prop.**
|
|
409
|
+
Both routes share the renderer's per-character clock.
|
|
410
|
+
- **The canvas pixels match the SVG** at the rendered size, so a `<PictoField>`
|
|
411
|
+
tile and a `<Picto>` of the same `size` look identical.
|
|
412
|
+
- **Keep `<Picto>` (DOM/SVG) for single avatars** or anywhere you need real DOM
|
|
413
|
+
nodes, CSS styling, or accessibility hooks on the element itself.
|
|
414
|
+
|
|
415
|
+
### Without React (custom layouts, other frameworks)
|
|
416
|
+
|
|
417
|
+
The batch renderer has a framework-agnostic core under `pictoguys/canvas`. Give
|
|
418
|
+
`createPictoRenderer` a `<canvas>` and drive it yourself:
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
import { createPictoRenderer, canvasSupported } from 'pictoguys/canvas'
|
|
422
|
+
import { picto } from 'pictoguys/core'
|
|
423
|
+
|
|
424
|
+
if (canvasSupported) {
|
|
425
|
+
const canvas = document.querySelector('canvas')!
|
|
426
|
+
const renderer = createPictoRenderer({ canvas, size: 64, variant: 'flat' })
|
|
427
|
+
|
|
428
|
+
renderer.setItems([
|
|
429
|
+
{ char: picto.character('Bloop'), x: 0, y: 0 },
|
|
430
|
+
{ char: picto.character('Mochi'), x: 80, y: 0 },
|
|
431
|
+
])
|
|
432
|
+
renderer.start()
|
|
433
|
+
renderer.breath('all')
|
|
434
|
+
// ...later: renderer.dispose()
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
`createPictoRenderer` is always safe to call: on the server or anywhere without a
|
|
439
|
+
canvas it returns a harmless no-op, and `canvasSupported` lets you fall back to
|
|
440
|
+
`<Picto>` when you need to.
|
|
441
|
+
|
|
442
|
+
## PictoField props
|
|
443
|
+
|
|
444
|
+
`<PictoField>` accepts these. Only `chars` is required.
|
|
445
|
+
|
|
446
|
+
| Prop | Type | Default | What it does |
|
|
447
|
+
| ----------------- | ----------------------------- | -------- | ------------------------------------------------------- |
|
|
448
|
+
| `chars` | `Character[]` | — | The pictos to draw, one tile each, in order. Required. |
|
|
449
|
+
| `size` | `number` | `64` | Tile width and height, in pixels. |
|
|
450
|
+
| `variant` | `'fancy' \| 'flat'` | `'fancy'`| Body look (see above). |
|
|
451
|
+
| `background` | `boolean` | `false` | Draw a background tile behind each picto. |
|
|
452
|
+
| `layout` | `'grid' \| {x,y}[]` | `'grid'` | Auto-flow grid, or explicit top-left positions. |
|
|
453
|
+
| `cols` | `number` | auto | Grid columns. Omit to derive from the canvas width. |
|
|
454
|
+
| `gap` | `number` | `12` | Gap between grid tiles, in pixels. |
|
|
455
|
+
| `animate` | `"blink" \| "jump" \| "breath" \| "dance" \| "sleeping" \| null` | `null` | Play one animation on every picto. |
|
|
456
|
+
| `height` | `number \| string` | `'70vh'` | Height of the self-owned scroll viewport. |
|
|
457
|
+
| `scrollParentRef` | `RefObject<HTMLElement>` | none | Advanced: scroll inside your own container instead. |
|
|
458
|
+
| `dpr` | `number` | auto | Device-pixel-ratio override. |
|
|
459
|
+
| `maxCacheBytes` | `number` | 256 MB | Soft sprite-cache size cap. |
|
|
460
|
+
| `style` | `CSSProperties` | none | Applied to the inner `<canvas>`. |
|
|
461
|
+
| `className` | `string` | none | Applied to the outer wrapper `<div>`. |
|
|
462
|
+
|
|
299
463
|
## All the props
|
|
300
464
|
|
|
301
465
|
`<Picto>` accepts these. Everything is optional.
|
|
@@ -308,10 +472,14 @@ guy.stop() // freeze
|
|
|
308
472
|
| `size` | `number` | `120` | Width and height, in pixels. |
|
|
309
473
|
| `background` | `boolean` | `false` | Set `true` to add a background tile. |
|
|
310
474
|
| `animate` | `"blink" \| "jump" \| "breath" \| "dance" \| "sleeping"` | none | Play an animation on loop or once. |
|
|
475
|
+
| `variant` | `"fancy" \| "flat"` | `"fancy"`| Body look: `flat` drops the gradient/shadow. |
|
|
311
476
|
|
|
312
477
|
Any normal `<span>` prop works too (`className`, `style`, `onClick`, and so on),
|
|
313
478
|
because that is what `<Picto>` renders into.
|
|
314
479
|
|
|
480
|
+
> Rendering a crowd of pictos? See [Rendering many pictos](#rendering-many-pictos)
|
|
481
|
+
> for `<PictoField>`, the canvas batch renderer.
|
|
482
|
+
|
|
315
483
|
Pictos are see-through by default, so they sit nicely on top of anything. Want a
|
|
316
484
|
colored tile behind one instead? Flip one switch:
|
|
317
485
|
|