Skip to content

Releases: yoannmoinet/nipplejs

v1.0.4

Choose a tag to compare

@yoannmoinet yoannmoinet released this 26 May 15:17
ea425b3

NippleJS v1.0.4

A small patch release fixing a regression in multi-manager pointer event handling. Thanks again @akromann for the follow-up in #246.

🌐 Documentation · 🎮 Interactive Demos · 📦 npm


🐛 Bug Fixes

  • Multiple managers closed each other when using pointer eventsFactory.cleanInactiveTouches() treated [evt.identifier] as the full set of active pointers for non-touch events, causing every joystick with a different pointerId to be force-ended on each pointerdown/pointerup. The cleanup now runs only for TouchEvents (which carry the complete touches list); per-pointer pointerup/pointercancel already handles its own cleanup. Also snapshots the joystick map before iterating to avoid mutation during the loop. (2c087f9)
  • TouchList iteration tightenedprocessEvents() now wraps evt.changedTouches with Array.from() for broader TypeScript/iterator compatibility. (653b81d)

✅ Tests

  • Added regression coverage for cleanInactiveTouches ensuring pointer events don't force-end sibling joysticks, and that TouchEvent cleanup still removes truly inactive joysticks. (2721cf4)

Full Changelog: v1.0.3...v1.0.4

v1.0.3

Choose a tag to compare

@yoannmoinet yoannmoinet released this 15 May 14:58
b8b5d22

NippleJS v1.0.3

A small patch release fixing TypeScript event handler overload inference and a multi-manager DOM containment bug. Thanks @akromann for the report and fixes in #245.

🌐 Documentation · 🎮 Interactive Demos · 📦 npm


🐛 Bug Fixes

  • Multiple managers couldn't open joysticks simultaneouslyJoystick.start() checked document.body.contains() to decide whether to re-add the element, which returned true for any joystick already mounted anywhere in the DOM. This caused a manager to skip mounting its joystick when another manager already had one active. Now uses zone.contains() so each manager checks its own zone. (6418df2)
  • TypeScript on() handler type was unusable without as anySuperEventType resolved to a union of all possible event payloads instead of narrowing based on the event name, forcing users to cast handlers. Added a generic parameter so the handler type narrows correctly to the matching EventDataMap entry. (2ff4c51)

Full Changelog: v1.0.2...v1.0.3

v1.0.2

Choose a tag to compare

@yoannmoinet yoannmoinet released this 11 May 19:17
ae6cb47

NippleJS v1.0.2

A small patch release focused on fixing static-mode joystick reuse on touch devices.

🌐 Documentation · 🎮 Interactive Demos · 📦 npm


🐛 Bug Fixes

  • Static joysticks spawning duplicates on rapid taps — In mode: 'static' (and the latent equivalent in semi mode and dataOnly static), tapping the joystick repeatedly on touch devices could spawn more than one joystick because the recovery index waited for the fade-out to finish before marking the joystick as reusable. The collection now reframes idles as "available for reuse" and populates it on end instead of hidden, with start clearing any stale resting entry from an interrupted cycle. Thanks @evannorton for the report and original fix in #244. (25c0496, eafdca2)

🏗️ Infrastructure

  • Release workflow rewritten with channel support and OIDC publishing
  • Root README.md is now copied into the nipplejs package before publish
  • Upgraded all workflows to Node 24 and Yarn 4.10
  • Playwright browsers installed before e2e tests in the release workflow

Full Changelog: v1.0.0...v1.0.2

v1.0.0

Choose a tag to compare

@yoannmoinet yoannmoinet released this 21 Mar 21:19
ecada8f

NippleJS v1.0.0

A complete ground-up rewrite of NippleJS — now TypeScript-first, zero dependencies, with a modern build pipeline and a brand new documentation website.

🌐 Documentation · 🎮 Interactive Demos · 📦 npm


✨ New Features

color: { front, back } — Full visual control

The color option now accepts a string or a { front, back } object. Since it sets the CSS background property, gradients, images, and any CSS background value work out of the box. (f244978)

nipplejs.create({
    color: {
        front: 'linear-gradient(135deg, #818cf8, #38bdf8)',
        back: 'radial-gradient(circle, rgba(99,102,241,0.15) 40%, transparent)',
    },
});

collection.reposition() + automatic ResizeObserver

Manually recalculate the zone bounding box and joystick positions after layout changes. The zone is also automatically watched with a ResizeObserver, so most resize scenarios are handled without any code. See the API docs. (075ab9b)

manager.reposition();

baseDelta in move event data (follow: true)

When the joystick base follows the thumb past the radius, the move event now includes baseDelta: { x, y } — the per-frame displacement of the base. Use vector for fine control and baseDelta for camera/world panning. See it in action in the Space Observatory demo. (f9e2095, 6b94695)

manager.on('move', (evt) => {
    const { vector, baseDelta } = evt.data;
    camera.x += baseDelta.x * panSpeed;
});

logLevel API

Control the library's console output globally. See the Logging docs. (f6b6ca8)

nipplejs.setLogLevel('debug');   // all logs
nipplejs.setLogLevel('warning'); // warnings and errors (default)
nipplejs.setLogLevel('none');    // silent
nipplejs.getLogLevel();          // returns current level

Zone position warning

The library now warns at creation time if the zone element has position: static, which causes joysticks to be positioned incorrectly. (f84205d)

move event fires continuously

The move event now fires on every pointermove, not only when the direction changes. This was a long-standing bug in v0. See the Events reference. (c438f2b)


🐛 Bug Fixes

  • Multitouch broken on mobilepreventDefault() on pointerdown interfered with dual-stick setups. Now only called on move events; touch-action: none on the zone handles the rest. (ca330c4)
  • _domHandlers_ memory leakunbindEvt never removed entries from the internal Map. (ce66ba0)
  • Duplicate added eventaddToDom() fired the event redundantly on every start(). (d94f3bd)
  • cleanInactiveTouches spreading native events — prototype properties were lost, producing NaN positions. (d94f3bd)
  • Throttle closure leak — Factory resize/scroll handlers created new closures per event, leaking timers Map entries. (d94f3bd)
  • trigger() Set mutation — handlers modifying the Set during iteration could skip callbacks. Now snapshots before iterating. (d94f3bd)
  • applyPosition zero handlingundefined coordinates were silently treated as 0. (d94f3bd)
  • createJoystick crash on invalid position — missing return after error log caused undefined variable access. (d94f3bd)
  • Semi mode infinite recursion — added depth guard to processOnStart. (d94f3bd)
  • display: 'hidden' invalid CSS — was display instead of visibility. (ce66ba0)

🏗️ Infrastructure

  • TypeScript throughout — branded types (Uid, Identifier), strict mode, full .d.ts exports
  • Yarn 4 monorepo with workspaces: nipplejs, docs, tests, tools, assets
  • Rollup bundler — ESM (.mjs) + CJS (.js) + type declarations (.d.ts)
  • ESLint flat config + Prettier (e35ca20)
  • 196 unit tests (Jest) + 50 e2e tests (Playwright) (291649e, 4235b4d, fc0e27f)
  • CI — unit tests, e2e tests, linting, typecheck (including docs) on every PR (e08fed6, c5ce457)
  • Release workflow — publish to NPM with OIDC provenance on GitHub release or manual dispatch (e08fed6)
  • SSR-safewindow access guarded with typeof checks, removed IE MSPointer dead code (d94f3bd)

🌐 Documentation Website

A brand new documentation site built with Astro 6 + Tailwind CSS 4, featuring an "Aurora Neon" dark theme with animated gradient blobs, frosted glass surfaces, and a cursor-following grid highlight.

5 Interactive Game Demos

Each game demo showcases a specific nipplejs option with a live code pane and debug overlay:

Game Option Docs
🐍 Neon Snake mode: 'static' mode
☄️ Asteroid Dodge lockX: true lockX
🎯 Dual-Stick Arena multitouch Getting Started
🔭 Space Observatory follow: true + baseDelta follow
🚀 Space Drift restJoystick: false restJoystick

Carousel with fullscreen mode

Games are displayed in a flex-based horizontal carousel with perspective tilt, blur effects (with Firefox detection fallback), dot indicators, and keyboard/arrow navigation. Each game supports fullscreen mode. (d8defa1, c99bd81)

Mobile optimized

Logo particles and grid highlight disabled on touch devices, game loops paused on non-active carousel slides, simplified transforms. (695bb8a)

GitHub Pages

Auto-deployed on push to master. (011e880)


⚠️ Breaking Changes (v0 → v1)

v0 v1
maxNumberOfNipples maxNumberOfJoysticks
destroyed event joystickDestroyed
joystick.el joystick.ui.el
show(), hide(), add(), remove() Removed
manager.get() manager.getJoystickByUid() or manager.all
manager.id manager.uid
manager.ids Removed
(evt, data) => {} (evt) => { evt.data }
move fires on direction change only move fires on every pointermove

See the full migration guide for details.

multi-pressure pleasure

Choose a tag to compare

@yoannmoinet yoannmoinet released this 15 Sep 04:22
  • support pressure on touch events
  • correctly handle multitouch
  • allow usage of the mouse and the touch at the same time for capable interfaces
  • allow for multiple instances in the same zone
  • give more control over instances
  • various bug-fixes