Utilities
Small standalone helpers used internally by the engines above, and exported for reuse in your own effects.
Range Mapping
mapRange() — linearly maps a value from one range into another.
function mapRange(
value: number,
from: [number, number],
to: [number, number],
clampResult?: boolean,
): numberParameters
value
number · default: —
The value to map, expressed in the from range.
from
[number, number] · default: —
The input range value is currently expressed in.
to
[number, number] · default: —
The output range to map into.
clampResult
boolean · default: false
When true, keeps the result within to at the edges instead of extrapolating past them — handles an inverted to range (e.g. [100, -100]) correctly too.
Return value
number — the mapped value.
Example:
import { mapRange } from '@macrulez/inview-core'
mapRange(0.5, [0, 1], [0, 200]) // 100
mapRange(1.2, [0, 1], [0, 200], true) // 200 (clamped)CSS Variable Binding
bindCSSVar() — writes a numeric progress value into a CSS custom property on an element, so the resulting visual effect can be composed in plain CSS.
function bindCSSVar(element: HTMLElement, name: string, value: number | string): voidParameters
element
HTMLElement · default: —
name
string · default: —
The custom property name. A leading -- is added automatically if name doesn't already have one.
value
number | string · default: —
Return value
void
Example:
import { bindCSSVar } from '@macrulez/inview-core'
bindCSSVar(el, 'progress', 0.42) // sets el.style.setProperty('--progress', '0.42').card {
opacity: var(--progress);
}Reduced Motion Preference
prefersReducedMotion() — reads the prefers-reduced-motion media query.
function prefersReducedMotion(): booleanReturn value
boolean — false when window/matchMedia isn't available (SSR), otherwise window.matchMedia('(prefers-reduced-motion: reduce)').matches.
A one-shot check — it doesn't notice the OS setting changing mid-session unless something else happens to re-invoke it. For a reactive value that updates on the media query's own change event, use usePrefersReducedMotion() instead:
function usePrefersReducedMotion(): Ref<boolean> // Vue
function usePrefersReducedMotion(): boolean // React<script setup lang="ts">
import { usePrefersReducedMotion } from '@macrulez/inview-vue'
const reduced = usePrefersReducedMotion()
</script>The Vue composable subscribes to the media query's own change event and unsubscribes on unmount, returning a Ref<boolean>; see React Hooks for the React hook (built on useSyncExternalStore instead).
Scroll To Element
scrollToElement() — smooth-scrolls a container (default window) so an element lands at its edge, offset by a px amount — the "scroll to an element" case ScrollEngine.scrollTo doesn't cover on its own (that one only takes a raw x/y position, not an element).
function scrollToElement(el: Element, options?: ScrollToElementOptions): voidParameters
el
Element · default: —
options
ScrollToElementOptions · default: {}
| Field | Type | Default |
|---|---|---|
target | Window | HTMLElement | window |
offset | number | 0 — px gap kept between the element's edge and the target's edge |
axis | 'x' | 'y' | 'y' |
behavior | 'auto' | 'smooth' | 'smooth', unless prefersReducedMotion() is true — then 'auto' |
Return value
void
Uses the browser's own native smooth-scroll animation rather than a hand-rolled rAF loop, so there's no duration/easing to configure — pass behavior: 'auto' for an instant jump instead.
Example:
import { scrollToElement } from '@macrulez/inview-core'
scrollToElement(sectionEl, { offset: 80 }) // e.g. clearing a sticky headeruseScrollTo (Vue)
function useScrollTo(
target?: MaybeRefOrGetter<Window | HTMLElement | null | undefined>,
): (el: Element | null | undefined, options?: UseScrollToOptions) => voidReturns a scrollTo(el, options?) callback bound to target (default window). target accepts a ref/getter and is re-read via toValue() on every call, so a ref that later points at a different container is picked up automatically — no need to call useScrollTo() again.
<script setup lang="ts">
import { useScrollTo } from '@macrulez/inview-vue'
const scrollTo = useScrollTo()
</script>
<template>
<button @click="scrollTo(sectionEl, { offset: 80 })">Jump</button>
</template>See React Hooks for the React hook — there target is a plain value captured in a useCallback dependency array instead, so a new target gets a fresh callback rather than being re-read live.
Value Clamping
clamp() — restricts a value to a [min, max] range.
function clamp(value: number, min: number, max: number): numberParameters
value
number · default: —
min
number · default: —
max
number · default: —
Return value
number — value restricted to the [min, max] range.
Stagger Delay
staggerDelay() — computes a CSS transition-delay for the index-th element in a staggered reveal (grid/list items fading in one after another), instead of hand-rolling Math.min(i, max) * step at every call site.
function staggerDelay(index: number, options?: StaggerDelayOptions): stringParameters
index
number · default: —
Position of this element in the sequence (0-based).
options
StaggerDelayOptions · default: {}
| Field | Type | Default |
|---|---|---|
step | number | 60 — ms of delay per step |
max | number | Infinity — caps the effective index, so item 50 in a long list doesn't wait almost a second |
unit | 'ms' | 's' | 'ms' |
mode | 'linear' | 'cycle' | 'linear' — flattens at max. 'cycle' wraps back to 0 past max, for a repeating wave instead |
Return value
string — a CSS <time> value (e.g. '180ms', '1s').
Example:
import { staggerDelay } from '@macrulez/inview-core'
staggerDelay(3) // '180ms'
staggerDelay(10, { step: 70, max: 4 }) // '280ms' — index capped at 4
staggerDelay(2, { step: 500, unit: 's' }) // '1s'
// mode: 'cycle' wraps back to 0 past max instead of flattening — a
// repeating wave (0, 70, 140, 210, 0, 70, 140, 210, ...) for a long
// list/grid instead of every item past index 3 sharing one delay:
staggerDelay(5, { step: 70, max: 3, mode: 'cycle' }) // '70ms' — index 5 wraps to the same slot as index 1createRevealController's stagger option is built on this — call it directly for a v-for/ .map() reveal effect written by hand instead of through the controller.
useStagger (Vue)
function useStagger(
index: MaybeRefOrGetter<number>,
options?: MaybeRefOrGetter<StaggerDelayOptions | undefined>,
): ComputedRef<string>A computed wrapper that recomputes whenever index/options change (as a ref/getter), instead of calling staggerDelay() by hand at every call site of a hand-rolled v-for reveal effect.
<script setup lang="ts">
import { useStagger } from '@macrulez/inview-vue'
const delay = useStagger(index, { step: 70, max: 4 })
</script>
<template>
<div class="reveal" :style="{ transitionDelay: delay }" />
</template>See React Hooks for the React hook — a thin, non-memoized wrapper, since the computation is cheap enough to just call every render.
Easing Presets
easings — a record of easing presets, applied to a 0..1 progress value before feeding it into an effect (a parallax offset, a CSS var, etc).
type Easing = (t: number) => number
const easings: Record<EasingName, Easing>
type EasingName = keyof typeof easingsPreset names: linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic, easeInSine, easeOutSine, easeInOutSine.
Example:
import { easings } from '@macrulez/inview-core'
const eased = easings.easeOutCubic(0.5)