{"_id":"@andromanpro/lava-orb","_rev":"3-09d5c691f376ccfb0e1eb5b55c211b2c","name":"@andromanpro/lava-orb","dist-tags":{"latest":"0.1.2"},"versions":{"0.1.0":{"name":"@andromanpro/lava-orb","version":"0.1.0","keywords":["canvas","canvas2d","orb","fire","frost","ice","liquid","particles","range","slider","input","visual","effect","reactive","temperature","animation","interactive","survey","form","no-deps"],"author":{"name":"andromanpro","email":"andromanpro@gmail.com"},"license":"MIT","_id":"@andromanpro/lava-orb@0.1.0","maintainers":[{"name":"andromanch","email":"andromanpro@gmail.com"}],"homepage":"https://andromanpro.github.io/lava-orb/","bugs":{"url":"https://github.com/andromanpro/lava-orb/issues"},"dist":{"shasum":"13014fcedd8427dd72655f6dadc24c7a224484bb","tarball":"https://registry.npmjs.org/@andromanpro/lava-orb/-/lava-orb-0.1.0.tgz","fileCount":18,"integrity":"sha512-+3IU57ngXV/nvHAET1i7rlCz7r4LB+SCHDRDIcVgk17Ci7ZvcTJnjf2JYSsLoMa00ZPmN2a00XATpTbATzbh7g==","signatures":[{"sig":"MEQCIF5UQ2XVdDrMOFKL8Q+eJs8yDnFg4QR5jmuOpxwAKR/FAiAd5GJovEEbY9c79kyuTSlqp5M+oTJFbE8Jqu8H9oIXug==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":225814},"main":"./dist/lava-orb.js","type":"module","module":"./src/index.js","engines":{"node":">=14"},"exports":{".":{"import":"./src/index.js","default":"./dist/lava-orb.js"},"./src/*":"./src/*","./dist/lava-orb.js":"./dist/lava-orb.js"},"gitHead":"8d776d716c06ef801e576b307d27587c9eae1bcf","scripts":{"test":"node tests/run-node.cjs || echo 'open tests/runner.html in browser for full suite'","build":"node build.cjs"},"_npmUser":{"name":"andromanch","email":"andromanpro@gmail.com"},"repository":{"url":"git+https://github.com/andromanpro/lava-orb.git","type":"git"},"_npmVersion":"11.6.2","description":"Temperature-reactive liquid orb attached to HTML range inputs. Canvas2D, zero dependencies. Ice at min, fire at max, smooth gradient in between.","directories":{},"sideEffects":["./src/index.js"],"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/lava-orb_0.1.0_1776712854832_0.8394232195731324","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@andromanpro/lava-orb","version":"0.1.1","keywords":["canvas","canvas2d","orb","fire","frost","ice","liquid","particles","range","slider","input","visual","effect","reactive","temperature","animation","interactive","survey","form","no-deps"],"author":{"name":"andromanpro","email":"andromanpro@gmail.com"},"license":"MIT","_id":"@andromanpro/lava-orb@0.1.1","maintainers":[{"name":"andromanch","email":"andromanpro@gmail.com"}],"homepage":"https://andromanpro.github.io/lava-orb/","bugs":{"url":"https://github.com/andromanpro/lava-orb/issues"},"dist":{"shasum":"80d14dfb6117b8f6dc0ff4539217bc6e07943ae1","tarball":"https://registry.npmjs.org/@andromanpro/lava-orb/-/lava-orb-0.1.1.tgz","fileCount":18,"integrity":"sha512-ewUFkH2Wt53swfYDIzGMfoUb9IXa/Y4qtYHeyQv6AThImGKKD89SCdxogdsnCmaMP5zP2k5lqI2azKggLk94AQ==","signatures":[{"sig":"MEYCIQDS39GK74i8S9qinRrqSA1c5WywcDPqig0wetj6lPxs9AIhAKtMjQNMzDWigJexL5BJQy51QVw20ZQi+sS3xpuajXRd","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":226617},"main":"./dist/lava-orb.js","type":"module","module":"./src/index.js","engines":{"node":">=14"},"exports":{".":{"import":"./src/index.js","default":"./dist/lava-orb.js"},"./src/*":"./src/*","./dist/lava-orb.js":"./dist/lava-orb.js"},"gitHead":"628b4e49b2885269494c7235ad09376d079be90e","scripts":{"test":"node tests/run-node.cjs || echo 'open tests/runner.html in browser for full suite'","build":"node build.cjs"},"_npmUser":{"name":"andromanch","email":"andromanpro@gmail.com"},"repository":{"url":"git+https://github.com/andromanpro/lava-orb.git","type":"git"},"_npmVersion":"11.6.2","description":"Temperature-reactive liquid orb attached to HTML range inputs. Canvas2D, zero dependencies. Ice at min, fire at max, smooth gradient in between.","directories":{},"sideEffects":["./src/index.js"],"_nodeVersion":"24.12.0","_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/lava-orb_0.1.1_1776715604680_0.9608057638211247","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@andromanpro/lava-orb","version":"0.1.2","description":"Temperature-reactive liquid orb attached to HTML range inputs. Canvas2D, zero dependencies. Ice at min, fire at max, smooth gradient in between.","type":"module","main":"./dist/lava-orb.js","module":"./src/index.js","exports":{".":{"import":"./src/index.js","default":"./dist/lava-orb.js"},"./dist/lava-orb.js":"./dist/lava-orb.js","./src/*":"./src/*"},"scripts":{"build":"node build.cjs","test":"node tests/run-node.cjs || echo 'open tests/runner.html in browser for full suite'"},"keywords":["canvas","canvas2d","orb","fire","frost","ice","liquid","particles","range","slider","input","visual","effect","reactive","temperature","animation","interactive","survey","form","no-deps"],"author":{"name":"andromanpro","email":"andromanpro@gmail.com"},"license":"MIT","homepage":"https://andromanpro.github.io/lava-orb/","repository":{"type":"git","url":"git+https://github.com/andromanpro/lava-orb.git"},"bugs":{"url":"https://github.com/andromanpro/lava-orb/issues"},"engines":{"node":">=14"},"sideEffects":["./src/index.js"],"gitHead":"ecb327510a5d6a1a710854e386885af0e5a459b6","_id":"@andromanpro/lava-orb@0.1.2","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-S2OR/eHwhii3yfxX+MpD66ReulgwM8NslyJi/Xlyciou7T6mMsrX14OTlNO13G70+1a67gcinLXiOjLf4k5Jlw==","shasum":"10a58f9a72210259205e61b07d2d10de5cf60ade","tarball":"https://registry.npmjs.org/@andromanpro/lava-orb/-/lava-orb-0.1.2.tgz","fileCount":18,"unpackedSize":230400,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDGJjKioipU47VhiWEVNPtXDJONqspBIYEBeyK7jzriFAIgTNlIOSItKrDwnV9yjFLft2ePjn+5Roc0AI5xnmno6UY="}]},"_npmUser":{"name":"andromanch","email":"andromanpro@gmail.com"},"directories":{},"maintainers":[{"name":"andromanch","email":"andromanpro@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/lava-orb_0.1.2_1777372484031_0.8332608942192905"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-20T19:20:54.732Z","modified":"2026-04-28T10:34:44.354Z","0.1.0":"2026-04-20T19:20:55.005Z","0.1.1":"2026-04-20T20:06:44.827Z","0.1.2":"2026-04-28T10:34:44.220Z"},"bugs":{"url":"https://github.com/andromanpro/lava-orb/issues"},"author":{"name":"andromanpro","email":"andromanpro@gmail.com"},"license":"MIT","homepage":"https://andromanpro.github.io/lava-orb/","keywords":["canvas","canvas2d","orb","fire","frost","ice","liquid","particles","range","slider","input","visual","effect","reactive","temperature","animation","interactive","survey","form","no-deps"],"repository":{"type":"git","url":"git+https://github.com/andromanpro/lava-orb.git"},"description":"Temperature-reactive liquid orb attached to HTML range inputs. Canvas2D, zero dependencies. Ice at min, fire at max, smooth gradient in between.","maintainers":[{"name":"andromanch","email":"andromanpro@gmail.com"}],"readme":"# lava-orb\r\n\r\n**Температурно-реактивный жидкий шар, привязанный к `<input type=\"range\">`.**\r\nCanvas 2D · zero dependencies · MIT\r\n\r\n![hero](article/gifs/01-hero.gif)\r\n\r\nНа минимуме значения — лёд и снег. На максимуме — огонь, молнии, взрывы. Между ними — плавный градиент цвета и вязкости жидкости. Работает как drop-in-замена стандартного ползунка: орб движется вместе с thumb, столкновения между орбами одной группы просчитываются, поведение настраивается через мощный TUNE-объект.\r\n\r\n---\r\n\r\n## Зачем\r\n\r\nСтандартные ползунки — скучные. Цифра \"7\" ничего не говорит о том, насколько \"жарко\" респонденту по этому вопросу. `lava-orb` превращает ввод числа в визуальный сигнал: ты **видишь**, что выбрано больше, а не считаешь деления на шкале.\r\n\r\nПодходит для:\r\n\r\n- **Опросников и анкет** — приоритезация задач, оценка интереса, NPS\r\n- **Настроечных панелей** — любой слайдер, где важна \"температура\" значения\r\n- **Демо и арт-проектов** — готовая физика жидкости, огня, льда\r\n\r\n---\r\n\r\n## Возможности\r\n\r\n- 🌊 **Жидкая симуляция** — волны на поверхности, гравитация при наклоне, инерция при броске\r\n- 🔥 **Два движка огня** — particle-based (реалистичные искры) и DOOM-fire (ретро-клеточный автомат)\r\n- ❄️ **Эффекты холода** — иней, падающий снег, ледяные частицы\r\n- ⚡ **Молнии** на максимуме значения\r\n- 💥 **Кинематографичные взрывы** — файрболы, ударная волна, bloom, дрожание экрана\r\n- 🧲 **Detach & throw** — резким броском слайдера орб отрывается, летит по физике и возвращается\r\n- 👥 **Группы орбов** — орбы из одной группы сталкиваются между собой\r\n- 🎨 **10-уровневые палитры** — от холодного голубого к горячему красному\r\n- 🧩 **Модульные mixin'ы** — подключай только нужные эффекты\r\n- 🪶 **Zero dependencies** — ни React, ни Vue, ни jQuery. Чистый ванильный JS.\r\n\r\n---\r\n\r\n## Установка\r\n\r\n### npm\r\n\r\n```bash\r\nnpm install @andromanpro/lava-orb\r\n```\r\n\r\n```js\r\nimport { attach } from '@andromanpro/lava-orb';\r\n\r\nconst slider = document.querySelector('input[type=range]');\r\nconst handle = attach(slider, { size: 60 });\r\n```\r\n\r\n### CDN / `<script>`\r\n\r\n```html\r\n<script src=\"https://unpkg.com/@andromanpro/lava-orb/dist/lava-orb.js\"></script>\r\n<script>\r\n  const handle = LavaOrb.attach(document.getElementById('mySlider'));\r\n</script>\r\n```\r\n\r\n### Локально без сборщика\r\n\r\nСкачай `dist/lava-orb.js` (~106 KB) и подключи через `<script>`. Никаких build-шагов.\r\n\r\n---\r\n\r\n## Quick start\r\n\r\n```html\r\n<input type=\"range\" id=\"q1\" min=\"1\" max=\"10\" value=\"5\" />\r\n\r\n<script src=\"https://unpkg.com/@andromanpro/lava-orb/dist/lava-orb.js\"></script>\r\n<script>\r\n  LavaOrb.attach(document.getElementById('q1'), {\r\n    size: 60,        // диаметр орба в пикселях\r\n    detach: true,    // разрешить отрыв при резком броске\r\n    onChange: v => console.log('значение:', v)\r\n  });\r\n</script>\r\n```\r\n\r\nГотово. Вокруг ползунка появится обёртка, на неё ляжет орб, который движется вместе с thumb и меняет цвет/эффекты от 1 до 10.\r\n\r\n---\r\n\r\n## Галерея эффектов\r\n\r\n| | |\r\n|---|---|\r\n| ![cold-to-hot](article/gifs/02-cold-to-hot.gif) | **Смена палитры 1 → 10.** От ледяного голубого через зелёный к огненно-красному. |\r\n| ![frost](article/gifs/03-frost.gif) | **Лёд (val 1-2).** Иней на орбе, падающий снег, ледяные частицы на отрыве. |\r\n| ![fire](article/gifs/04-fire-particles.gif) | **Огонь (val 9-10).** Particle-based с искрами, дымом и wrap-around через `mix-blend-mode: screen`. |\r\n| ![detach](article/gifs/05-detach.gif) | **Detach.** Бросок слайдера > 25% за 150 мс → орб отрывается, летит, возвращается к thumb. |\r\n| ![explode](article/gifs/06-explosion.gif) | **Explosion.** `handle.explode()` — файрболы, ударная волна, shake-камера. |\r\n| ![multi](article/gifs/07-multi-orbs.gif) | **Группы.** Орбы из одной группы сталкиваются между собой в общем rAF-цикле. |\r\n\r\n---\r\n\r\n## API\r\n\r\n### `LavaOrb.attach(input, options) → handle`\r\n\r\nПривязывает орб к существующему `<input type=\"range\">`.\r\n\r\n**Options:**\r\n\r\n| Параметр | Тип | По умолчанию | Описание |\r\n|---|---|---|---|\r\n| `size` | number | `60` | Диаметр орба в пикселях |\r\n| `group` | OrbGroup | `default` | Группа для столкновений |\r\n| `palette` | string \\| function | `'default'` | Имя палитры или `(val, isDark) => [c1, c2, c3, c4]` |\r\n| `detach` | boolean | `true` | Разрешить отрыв при резком броске |\r\n| `onChange` | function | — | Колбэк `(value) => void` на каждом изменении |\r\n| `fire` | object | — | Настройки огня (intensity, style, …) |\r\n| `liquid` | object | — | Настройки жидкости (surface, gravity, …) |\r\n| `tune` | object | — | Полный TUNE-объект с низкоуровневыми параметрами |\r\n\r\n**Handle (возвращаемый объект):**\r\n\r\n```js\r\nhandle.setVal(v)         // задать значение (1-10)\r\nhandle.setColors(pal)    // задать палитру вручную\r\nhandle.setFireStyle(s)   // 'particles' | 'doom'\r\nhandle.explode()         // взорвать орб 💥\r\nhandle.isAlive()         // жив ли handle\r\nhandle.destroy()         // удалить орб, снять listeners\r\n\r\nhandle.orb               // прямой доступ к экземпляру LavaOrb\r\nhandle.input             // ссылка на исходный input\r\nhandle.wrapper           // DOM-обёртка\r\n```\r\n\r\n### `LavaOrb.createGroup(opts) → OrbGroup`\r\n\r\nСоздать изолированную группу. Орбы в разных группах не сталкиваются.\r\n\r\n```js\r\nconst groupA = LavaOrb.createGroup({ name: 'survey-A' });\r\nLavaOrb.attach(input1, { group: groupA });\r\nLavaOrb.attach(input2, { group: groupA });\r\n```\r\n\r\n### `LavaOrb.getByInput(input) → handle | null`\r\n\r\nВернуть handle, ранее привязанный к `input`. `null` если не привязан или уже уничтожен.\r\n\r\n---\r\n\r\n## Интеграция: подводные камни\r\n\r\n> Если орб не появляется или off-position — почти всегда одна из этих причин.\r\n\r\n### 1. CSS требования к input\r\n\r\nLibrary читает thumb's `getBoundingClientRect()` для расчёта позиции орба. Минимальный набор стилей:\r\n\r\n```css\r\ninput[type=\"range\"] {\r\n  height: 12px;\r\n  background: linear-gradient(to right, #3b82f6, #06b6d4 25%, #fbbf24 60%, #ef4444);\r\n  border-radius: 6px;\r\n  appearance: none; -webkit-appearance: none;\r\n}\r\ninput[type=\"range\"]::-webkit-slider-thumb {\r\n  -webkit-appearance: none;\r\n  width: 60px; height: 60px;       /* критично — library читает эти размеры */\r\n  background: transparent;\r\n  opacity: 0;                       /* физически 60×60, визуально невидим */\r\n  cursor: grab;\r\n}\r\ninput[type=\"range\"]::-moz-range-thumb {\r\n  width: 60px; height: 60px;\r\n  background: transparent; border: 0;\r\n  opacity: 0; cursor: grab;\r\n}\r\n```\r\n\r\n**Не работает:** `::-webkit-slider-runnable-track` для styling — library ждёт background на самом input, не на pseudo-track.\r\n\r\n### 2. Layout: input в block-контексте\r\n\r\nInput должен быть в block-контексте. `display: flex` с siblings рядом ломает позиционирование орба — library рассчитывает rect по input, но flex может смещать его относительно ожидаемой точки. Также `display: block; margin: 0 auto` на input может ломать кэшированный rect.\r\n\r\n❌ **Не работает** (orb уезжает off-position):\r\n\r\n```html\r\n<div style=\"display: flex; align-items: center; gap: 24px;\">\r\n  <input type=\"range\" ...>\r\n  <span>5</span>\r\n</div>\r\n```\r\n\r\n✅ **Работает** (как в `examples/01-minimal.html`):\r\n\r\n```html\r\n<div style=\"max-width: 480px; margin: 0 auto; padding: 0 40px;\">\r\n  <input type=\"range\" ... style=\"width: 100%;\">\r\n  <div style=\"text-align: center;\">значение: <span>5</span></div>\r\n</div>\r\n```\r\n\r\n### 3. Collision-explosion: `group` — это объект, не строка\r\n\r\nЧерез общий `OrbGroup`, созданный фабрикой `LavaOrb.createGroup({ name })`:\r\n\r\n```js\r\n// строка — orbs init silently fails или не collide\r\nLavaOrb.attach(input1, { group: 'demo' });\r\n\r\n// объект через createGroup — правильно\r\nconst group = LavaOrb.createGroup({ name: 'demo' });\r\nLavaOrb.attach(input1, { group, detach: true, onChange: v => ... });\r\nLavaOrb.attach(input2, { group, detach: true, onChange: v => ... });\r\n// Теперь при сведении орбов друг с другом срабатывает взрыв\r\n```\r\n\r\n### 4. Динамический label вокруг орба\r\n\r\n`onChange` стреляет на каждое изменение value. Удобно для синхронизации текста и цвета вокруг орба:\r\n\r\n```js\r\nfunction temp(v) {\r\n  const n = +v;\r\n  if (n <= 1)  return { label: 'мороз',       color: '#3b82f6' };\r\n  if (n <= 3)  return { label: 'холодный',    color: '#06b6d4' };\r\n  if (n <= 5)  return { label: 'прохладный',  color: '#84cc16' };\r\n  if (n <= 7)  return { label: 'тёплый',      color: '#fbbf24' };\r\n  if (n <= 9)  return { label: 'горячий',     color: '#f97316' };\r\n  return        { label: 'огонь',          color: '#ef4444' };\r\n}\r\n\r\nLavaOrb.attach(input, {\r\n  onChange: v => {\r\n    const t = temp(v);\r\n    labelEl.textContent = t.label;\r\n    labelEl.style.color = t.color;\r\n    valEl.textContent = v;\r\n  }\r\n});\r\n```\r\n\r\n---\r\n\r\n## Разработка\r\n\r\n```bash\r\n# Запустить локальный сервер (Python 3)\r\npython -m http.server 8877\r\n\r\n# Открыть главное демо\r\n# → http://localhost:8877/examples/03-full.html\r\n\r\n# Пересобрать IIFE-бандл после правок в src/\r\nnode build.cjs\r\n\r\n# Тесты (19 шт.)\r\n# → http://localhost:8877/tests/runner.html\r\n```\r\n\r\nСтруктура:\r\n\r\n```\r\nsrc/\r\n├── core/       # палитры, TUNE-параметры, helpers\r\n├── orb/        # класс LavaOrb + mixin'ы (fire, frost, particles)\r\n├── fx/         # детач и взрыв — отдельные модули\r\n├── public/     # attach(), OrbGroup — публичный API\r\n└── index.js    # default export + window.LavaOrb\r\n```\r\n\r\n`build.cjs` склеивает `src/` в один IIFE-бандл (`dist/lava-orb.js`). Никакого rollup/webpack/tsup — собственный простой bundler в ~150 строк.\r\n\r\n---\r\n\r\n## Связанные инструменты\r\n\r\nОрб создавался с помощью двух отдельных редакторов — они тоже open source:\r\n\r\n- [**liquid-orb-editor**](https://github.com/andromanpro/liquid-orb-editor) — тонкая настройка жидкости (волны, гравитация, вязкость, сплэши)\r\n- [**fire-particle-editor**](https://github.com/andromanpro/fire-particle-editor) — ручная лепка пламени (искры, дым, конус, трейлы)\r\n\r\n---\r\n\r\n## Лицензия\r\n\r\n[MIT](LICENSE) © 2026 andromanpro\r\n\r\n---\r\n\r\n## English\r\n\r\n> **Temperature-reactive liquid orb attached to HTML range inputs.** Canvas 2D · zero deps · MIT.\r\n\r\n### Install\r\n\r\n```bash\r\nnpm install @andromanpro/lava-orb\r\n```\r\n\r\n```js\r\nimport { attach } from '@andromanpro/lava-orb';\r\nattach(document.querySelector('input[type=range]'), { size: 60 });\r\n```\r\n\r\n### Or via CDN\r\n\r\n```html\r\n<script src=\"https://unpkg.com/@andromanpro/lava-orb/dist/lava-orb.js\"></script>\r\n<script>LavaOrb.attach(document.getElementById('slider'));</script>\r\n```\r\n\r\n### Features\r\n\r\n- Liquid physics (waves, gravity, inertia)\r\n- Two fire engines: particle-based + DOOM-fire cellular automaton\r\n- Frost & snow effects on low values\r\n- Electric arcs on max\r\n- Cinematic explosions (`handle.explode()`)\r\n- Detach physics (throw the orb via fast slider drag)\r\n- Orb groups with inter-orb collisions\r\n- 10-step palette (`cold → hot`)\r\n- ~106 KB IIFE bundle, zero dependencies\r\n\r\nFull API reference and examples — see Russian section above or the [`examples/`](examples/) folder.\r\n","readmeFilename":"README.md"}