motion-library 0.0.0-stage → 0.1.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.
Files changed (146) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +37 -2
  3. package/anims/basics/anticipation.js +56 -0
  4. package/anims/basics/card-flip.js +39 -0
  5. package/anims/basics/easing-race.js +57 -0
  6. package/anims/basics/fade-scale.js +44 -0
  7. package/anims/basics/stagger-grid.js +43 -0
  8. package/anims/chrome/black-hole.js +81 -0
  9. package/anims/chrome/cloth-reveal.js +156 -0
  10. package/anims/chrome/dna-helix.js +52 -0
  11. package/anims/chrome/dust-bust.js +109 -0
  12. package/anims/chrome/extruded-type.js +64 -0
  13. package/anims/chrome/fractal-tree.js +75 -0
  14. package/anims/chrome/gem.js +111 -0
  15. package/anims/chrome/geode.js +102 -0
  16. package/anims/chrome/glass-lens.js +73 -0
  17. package/anims/chrome/glass-prism.js +95 -0
  18. package/anims/chrome/gyroid.js +69 -0
  19. package/anims/chrome/holo-card.js +52 -0
  20. package/anims/chrome/infinite-tunnel.js +81 -0
  21. package/anims/chrome/inflated-type.js +141 -0
  22. package/anims/chrome/isometric-city.js +97 -0
  23. package/anims/chrome/jelly-cube.js +329 -0
  24. package/anims/chrome/klein-bottle.js +80 -0
  25. package/anims/chrome/liquid-metal.js +94 -0
  26. package/anims/chrome/low-poly-planet.js +163 -0
  27. package/anims/chrome/mandelbulb.js +79 -0
  28. package/anims/chrome/melting-logo.js +126 -0
  29. package/anims/chrome/menger-sponge.js +79 -0
  30. package/anims/chrome/neon-geodesic.js +149 -0
  31. package/anims/chrome/paper-fold.js +135 -0
  32. package/anims/chrome/path-spheres.js +112 -0
  33. package/anims/chrome/point-globe.js +81 -0
  34. package/anims/chrome/portal.js +105 -0
  35. package/anims/chrome/rubiks-cube.js +74 -0
  36. package/anims/chrome/sdf-morph.js +129 -0
  37. package/anims/chrome/shatter.js +120 -0
  38. package/anims/chrome/soap-bubble.js +86 -0
  39. package/anims/chrome/terrain-flyover.js +78 -0
  40. package/anims/chrome/tesseract.js +148 -0
  41. package/anims/chrome/torus-knot.js +89 -0
  42. package/anims/chrome/volumetric-clouds.js +70 -0
  43. package/anims/flow/bio-shore.js +140 -0
  44. package/anims/flow/calligraphy.js +116 -0
  45. package/anims/flow/chladni.js +152 -0
  46. package/anims/flow/cloud-chamber.js +101 -0
  47. package/anims/flow/confetti.js +46 -0
  48. package/anims/flow/curl-flow.js +52 -0
  49. package/anims/flow/dandelion.js +80 -0
  50. package/anims/flow/dla-crystal.js +57 -0
  51. package/anims/flow/drone-show.js +357 -0
  52. package/anims/flow/embers.js +63 -0
  53. package/anims/flow/firefly-sync.js +107 -0
  54. package/anims/flow/galaxy.js +69 -0
  55. package/anims/flow/galton-board.js +155 -0
  56. package/anims/flow/gpu-particles.js +73 -0
  57. package/anims/flow/harmonograph.js +61 -0
  58. package/anims/flow/holi.js +153 -0
  59. package/anims/flow/iron-filings.js +112 -0
  60. package/anims/flow/lightning.js +72 -0
  61. package/anims/flow/murmuration.js +105 -0
  62. package/anims/flow/orbitals.js +195 -0
  63. package/anims/flow/particle-life.js +87 -0
  64. package/anims/flow/physarum.js +171 -0
  65. package/anims/flow/pin-art.js +181 -0
  66. package/anims/flow/rainbow.js +127 -0
  67. package/anims/flow/roche-limit.js +161 -0
  68. package/anims/flow/sand-mandala.js +121 -0
  69. package/anims/flow/scramble-crossing.js +152 -0
  70. package/anims/flow/snowfall.js +84 -0
  71. package/anims/flow/sparks.js +71 -0
  72. package/anims/flow/spider-web.js +123 -0
  73. package/anims/flow/starry-night.js +121 -0
  74. package/anims/flow/states-of-matter.js +113 -0
  75. package/anims/flow/strange-attractor.js +54 -0
  76. package/anims/flow/wheat-field.js +186 -0
  77. package/anims/flow/wind-tunnel.js +227 -0
  78. package/anims/fluid/bubbles.js +152 -0
  79. package/anims/fluid/caustics.js +45 -0
  80. package/anims/fluid/dripping-text.js +53 -0
  81. package/anims/fluid/ferrofluid.js +56 -0
  82. package/anims/fluid/fire-smoke.js +83 -0
  83. package/anims/fluid/honey-drip.js +167 -0
  84. package/anims/fluid/ink-drop.js +50 -0
  85. package/anims/fluid/ink-fluid.js +125 -0
  86. package/anims/fluid/lava-lamp.js +56 -0
  87. package/anims/fluid/liquid-distort.js +64 -0
  88. package/anims/fluid/liquid-glass-fill.js +53 -0
  89. package/anims/fluid/marbling.js +53 -0
  90. package/anims/fluid/mercury.js +177 -0
  91. package/anims/fluid/ocean-waves.js +50 -0
  92. package/anims/fluid/paint-swirl.js +153 -0
  93. package/anims/fluid/raindrops-glass.js +60 -0
  94. package/anims/fluid/ripples.js +57 -0
  95. package/anims/fluid/sph-water.js +75 -0
  96. package/anims/fluid/underwater.js +55 -0
  97. package/anims/fluid/water-surface-3d.js +98 -0
  98. package/anims/hover/cursor-trail.js +37 -0
  99. package/anims/hover/magnetic.js +41 -0
  100. package/anims/hover/ripple.js +31 -0
  101. package/anims/hover/spotlight.js +31 -0
  102. package/anims/hover/tilt.js +36 -0
  103. package/anims/micro/arc-spinner.js +22 -0
  104. package/anims/micro/like-burst.js +42 -0
  105. package/anims/micro/skeleton.js +36 -0
  106. package/anims/micro/submit-success.js +45 -0
  107. package/anims/micro/toggle.js +35 -0
  108. package/anims/morph/gooey.js +36 -0
  109. package/anims/morph/icon-morph.js +45 -0
  110. package/anims/morph/line-draw.js +34 -0
  111. package/anims/morph/liquid-button.js +53 -0
  112. package/anims/morph/shape-morph.js +61 -0
  113. package/anims/registry-more.js +327 -0
  114. package/anims/registry.js +294 -0
  115. package/anims/scroll/parallax.js +50 -0
  116. package/anims/scroll/pinned-track.js +40 -0
  117. package/anims/scroll/scrub-reveal.js +34 -0
  118. package/anims/scroll/velocity-skew.js +38 -0
  119. package/anims/scroll/zoom-through.js +41 -0
  120. package/anims/shaders/aurora.js +47 -0
  121. package/anims/shaders/dither.js +69 -0
  122. package/anims/shaders/domain-warp.js +52 -0
  123. package/anims/shaders/guilloche.js +42 -0
  124. package/anims/shaders/kaleidoscope.js +64 -0
  125. package/anims/shaders/reaction-diffusion.js +85 -0
  126. package/anims/shaders/rgb-glitch.js +56 -0
  127. package/anims/shaders/truchet.js +57 -0
  128. package/anims/shaders/voronoi.js +50 -0
  129. package/anims/springs/ball-collisions.js +47 -0
  130. package/anims/springs/pendulum-wave.js +35 -0
  131. package/anims/springs/rubber-band.js +50 -0
  132. package/anims/springs/squash-bounce.js +50 -0
  133. package/anims/springs/verlet-rope.js +46 -0
  134. package/anims/text/letter-rise.js +40 -0
  135. package/anims/text/line-mask.js +26 -0
  136. package/anims/text/scramble.js +32 -0
  137. package/anims/text/typewriter.js +35 -0
  138. package/anims/text/vf-wave.js +26 -0
  139. package/anims/transitions/circle-reveal.js +42 -0
  140. package/anims/transitions/push-slide.js +40 -0
  141. package/anims/transitions/shared-expand.js +43 -0
  142. package/anims/transitions/staircase-wipe.js +41 -0
  143. package/anims/transitions/view-transition.js +43 -0
  144. package/bin.mjs +155 -0
  145. package/js/code.js +122 -0
  146. package/package.json +10 -4
package/js/code.js ADDED
@@ -0,0 +1,122 @@
1
+ // Fetching snippet sources, reading and rewriting their PARAMS line, and wrapping them as standalone pages.
2
+ import { srcOf } from '../anims/registry.js';
3
+
4
+ const cache = new Map();
5
+ export async function source(anim) {
6
+ if (!cache.has(anim.id)) cache.set(anim.id, fetch(srcOf(anim)).then(r => { if (!r.ok) throw new Error(`Could not load ${srcOf(anim)}`); return r.text(); }));
7
+ return cache.get(anim.id);
8
+ }
9
+
10
+ const PARAMS_RE = /^const PARAMS = \{([^\n]*)\};/m;
11
+ const PAIR_RE = /(\w+)\s*:\s*(-?\d*\.?\d+(?:e-?\d+)?)/g;
12
+
13
+ export function readParams(src) {
14
+ const m = PARAMS_RE.exec(src); if (!m) return {};
15
+ const out = {}; for (const [, k, v] of m[1].matchAll(PAIR_RE)) out[k] = parseFloat(v);
16
+ return out;
17
+ }
18
+ export function paramsLineIndex(src) {
19
+ const m = PARAMS_RE.exec(src); return m ? src.slice(0, m.index).split('\n').length - 1 : -1;
20
+ }
21
+ const decimals = step => { const s = String(step); return s.includes('e-') ? +s.split('e-')[1] : (s.split('.')[1] || '').length; };
22
+ export const formatValue = (v, step) => String(+Number(v).toFixed(decimals(step)));
23
+
24
+ export function writeParams(src, values, ranges) {
25
+ return src.replace(PARAMS_RE, (line, body) => {
26
+ // only touch numbers that actually changed, so untouched values keep their original spelling
27
+ const next = body.replace(PAIR_RE, (pair, k, v) => {
28
+ if (!(k in values)) return pair;
29
+ const step = ranges[k]?.step ?? 0.01, out = formatValue(values[k], step);
30
+ return out === formatValue(parseFloat(v), step) ? pair : `${k}: ${out}`;
31
+ });
32
+ return `const PARAMS = {${next}};`;
33
+ });
34
+ }
35
+
36
+ // Share links carry tuned values as "pieces=300&gravity=0.6": only the keys that differ from the defaults.
37
+ export function encodeParams(values, defaults, ranges) {
38
+ return Object.keys(ranges).filter(k => k in values && formatValue(values[k], ranges[k].step) !== formatValue(defaults[k], ranges[k].step))
39
+ .map(k => `${k}=${formatValue(values[k], ranges[k].step)}`).join('&');
40
+ }
41
+ // The safety gate: URL values end up in code that runs. Only the animation's own slider keys pass, only finite
42
+ // numbers, clamped to the slider's range and snapped to its step, so nothing but a number can reach the PARAMS line.
43
+ export function parseParams(query, ranges) {
44
+ const out = {};
45
+ for (const [k, raw] of new URLSearchParams(query || '')) {
46
+ const r = Object.hasOwn(ranges, k) ? ranges[k] : null;
47
+ if (!r || !/^-?\d*\.?\d+(e-?\d+)?$/i.test(raw.trim())) continue;
48
+ const v = Number(raw);
49
+ if (!Number.isFinite(v)) continue;
50
+ const snapped = r.min + Math.round((Math.min(r.max, Math.max(r.min, v)) - r.min) / r.step) * r.step;
51
+ out[k] = +formatValue(Math.min(r.max, Math.max(r.min, snapped)), r.step);
52
+ }
53
+ return out;
54
+ }
55
+
56
+ // "Copy as prompt": everything an AI coding assistant needs to fit the effect into someone's project: what it is, why
57
+ // it works, the numbers worth tuning (with their current values) and the exact code, plus the mount/cleanup contract.
58
+ const KIND = { dom: 'DOM elements and CSS/WAAPI', canvas: 'a 2D canvas', webgl: 'a WebGL canvas' };
59
+ export function promptFor(anim, code, catName = anim.cat) {
60
+ const now = readParams(code);
61
+ const settings = Object.entries(anim.params).filter(([k]) => k in now)
62
+ .map(([k, r]) => `- ${k}: ${formatValue(now[k], r.step)} (range ${r.min}–${r.max}, step ${r.step}) · ${r.label}`);
63
+ return [
64
+ `Help me fit this animation into my project. Adapt it to my stack and my design; keep the motion itself as it is unless I ask.`,
65
+ ``,
66
+ `Animation: ${anim.title} (Motion Library, ${catName})`,
67
+ `The trick: ${anim.trick}`,
68
+ `Draws with: ${KIND[anim.kind] || anim.kind}`,
69
+ ``,
70
+ `How it works:`,
71
+ ...anim.notes.map(n => `- ${n}`),
72
+ ...(settings.length ? [``, `Settings in the PARAMS line (current value):`, ...settings] : []),
73
+ ``,
74
+ `How the code is built:`,
75
+ `- Plain JavaScript, no libraries. The default export takes a container element, draws into it, sizes itself to it, and returns a cleanup function.`,
76
+ `- Keep that contract: mount it when the component mounts (useEffect, onMounted, connectedCallback…) and call the cleanup when it unmounts.`,
77
+ `- Give the container a size. Respect prefers-reduced-motion in my project (show a still frame or skip it).`,
78
+ `- Licence: MIT-0, free to use and change without credit.`,
79
+ ``,
80
+ `My project: [say where it goes and what it should fit, e.g. "behind the hero heading of my Next.js landing page, in our brand colours"]`,
81
+ ``,
82
+ '```js',
83
+ code.trimEnd(),
84
+ '```',
85
+ ``,
86
+ ].join('\n');
87
+ }
88
+
89
+ // A runnable single-file page: the module's default export becomes a plain function called on #stage.
90
+ export function standalone(src, title) {
91
+ const body = src.replace(/export default function\s*\(/, 'function mount(');
92
+ return `<!doctype html>
93
+ <html lang="en">
94
+ <head>
95
+ <meta charset="utf-8">
96
+ <meta name="viewport" content="width=device-width, initial-scale=1">
97
+ <title>${title.replace(/</g, '&lt;')}</title>
98
+ <link rel="preconnect" href="https://fonts.googleapis.com"><link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
99
+ <link href="https://fonts.googleapis.com/css2?family=Mona+Sans:wdth,wght@75..125,200..900&display=swap" rel="stylesheet">
100
+ <style>
101
+ html, body { margin: 0; height: 100%; overflow: hidden; }
102
+ body { font-family: "Mona Sans", system-ui, sans-serif; }
103
+ #stage { position: fixed; inset: 0; overflow: hidden; }
104
+ </style>
105
+ </head>
106
+ <body>
107
+ <div id="stage"></div>
108
+ <script type="module">
109
+ ${body}
110
+ document.fonts.ready.then(() => mount(document.getElementById('stage')));
111
+ </script>
112
+ </body>
113
+ </html>
114
+ `;
115
+ }
116
+
117
+ export function download(filename, text) {
118
+ const url = URL.createObjectURL(new Blob([text], { type: 'text/html' }));
119
+ const a = Object.assign(document.createElement('a'), { href: url, download: filename });
120
+ document.body.append(a); a.click(); a.remove();
121
+ setTimeout(() => URL.revokeObjectURL(url), 2000);
122
+ }
package/package.json CHANGED
@@ -1,6 +1,12 @@
1
1
  {
2
2
  "name": "motion-library",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Hand-made web animations, one plain-JS file each with no dependencies. Copy one into your project, tuned: npx motion-library add confetti --pieces 300",
5
+ "type": "module",
6
+ "bin": { "motion-library": "bin.mjs" },
7
+ "files": ["bin.mjs", "anims/", "js/code.js", "LICENSE", "README.md"],
8
+ "scripts": { "prepack": "node build.mjs" },
9
+ "engines": { "node": ">=18" },
10
+ "license": "MIT-0",
11
+ "keywords": ["animation", "motion", "webgl", "canvas", "css", "transitions", "particles", "shaders", "snippets"]
12
+ }