{ signal: AbortSignal }event listener option — follows the DOMaddEventListenerpattern. Pass anAbortController's signal toon()orsubscribe()to remove one or many listeners with a singleabort()call.
- Codebase terminology cleanup (remove legacy "scene" naming, rename
scrollOffset→activeRange). - ContainerProxy: separate
size/positiongetters replace combinedrect. - Unit test coverage expanded from 84 to 164 tests.
- Options renamed —
scrollParent→container,triggerStart→containerStart,triggerEnd→containerEnd. This groups container-related options under a shared prefix and avoids confusion with GSAP'strigger(which refers to the element, not the container). TheresolvedBoundsgetter now returns{ element, container }instead of{ element, scrollParent }.
- Multi-match selector warning (dev mode) — when a CSS selector passed to
elementorcontainermatches more than one DOM element, a warning advises creating one instance per element instead.
- Original sources embedded in source maps — published
.mapfiles now contain the actual TypeScript source viainlineSources, making them useful for debugging without thesrc/directory.
destroy()callsonDestroyinstead ofonRemoveon plugins —onRemovenow only fires viaremovePlugin(). Plugins that usedonRemovefor destroy cleanup should addonDestroy(or assign the same function to both).computedOptionsremoved — replaced byresolvedBounds, which returns{ element: ElementBounds, container: ContainerBounds }(cached layout bounds only, no longer leaks the full internal options structure).- Getter return types narrowed —
element,container, andverticalgetters now return resolved types (Element,Window | Element,boolean) instead of the raw public input union. Setters still accept the full public types.
- Plugin lifecycle hooks:
onEnable,onDisable,onDestroy— plugins can react to enable/disable transitions and distinguish manual removal from instance teardown.destroy()on an enabled instance firesonDisable→onDestroyin sequence. scrollmagic/utilsubpath export — exposesagnosticValuesandagnosticPropsviaimport { ... } from 'scrollmagic/util'for plugin authors working with direction-agnostic bounds.ElementBounds,ContainerBounds,ResolvedBoundstypes exported — available from the main entry point for plugin and integration authors.
scrollVelocitygetter — per-container scroll velocity in px/s, shared across all instances on the same container via ContainerProxy. Returns 0 when disabled, destroyed, or idle (100ms staleness decay).enable()/disable()— temporarily disconnect all observers without destroying the instance. Progress freezes at its current value;modify(),on()/off(), plugins, and most getters remain functional. Re-enabling reconnects everything and schedules a full recalculation.{ once: true }event listener option — follows the DOMaddEventListeneroptions bag pattern. Works with both.on()and.subscribe().refresh()/refreshAll()/destroyAll()— force bounds recalculation after layout changes invisible to ResizeObserver (position shifts, class toggles, sibling DOM mutations, font loading, etc.).- Post-destroy and non-browser guards — all public methods now warn in dev mode and bail cleanly instead of producing undefined behavior when called after
destroy()or outside a browser environment. - Element–container ancestry validation (dev mode) —
console.errorwhen the tracked element isn't a descendant of its container, catching silent IntersectionObserver misconfiguration.
- Container position not initialized synchronously — non-window containers defaulted to
{top:0,left:0}until the first scroll/resize event, producing wrong initial progress for containers offset from the viewport top. - Zero-size container guard — when a scroll container collapses to 0px,
updateProgress()no longer produces incorrect values (division by near-zero) andupdateViewportObserver()no longer passes broken margins to the IntersectionObserver. - Direction change not invalidating elementBoundsCache — changing
verticalviamodify()left stale axis-dependent bounds in the cache. - containerBounds not rescheduled on option changes —
containerStart,containerEnd, andverticalchanges viamodify()didn't trigger a container bounds recalculation, causing wrong progress and viewport margins. - Stale closure in
onElementResize—updateElementBoundsCache()replaced the entire bounds object, but the resize handler's destructured reference pointed to the old one, so size comparisons always returned false and progress never recalculated after element resize. destroy()skipping pluginonRemovecallbacks — plugin cleanup was routed throughremovePlugin(), which hit theguardInert()check (destroyed was already true) and silently skipped allonRemovecallbacks.
- Replace debounce with
throttleRaffor container resize — removes the arbitrary 100ms debounce delay. Both window and element resize paths now use rAF-batched throttling for consistent, responsive behavior. - Cache PixelConverter results —
elementStart/elementEndconverters are skipped when element size is unchanged (common during scroll). Bounds caches mutated in-place viaObject.assigninstead of allocating new objects each frame.
- Explicit
typekeyword on type-only imports for better tree-shaking. - New
Vectortype for{x, y}pairs, replacing the oldScrollDeltashape. - E2e tests reorganized from origin-based to feature-based structure. 13 regression tests covering v2-reported edge cases added.
- Added MAINTAINING.md and ROADMAP.md.