Skip to content

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.

ts
function mapRange(
  value: number,
  from: [number, number],
  to: [number, number],
  clampResult?: boolean,
): number

Parameters ​

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:

ts
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.

ts
function bindCSSVar(element: HTMLElement, name: string, value: number | string): void

Parameters ​

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:

ts
import { bindCSSVar } from '@macrulez/inview-core'

bindCSSVar(el, 'progress', 0.42) // sets el.style.setProperty('--progress', '0.42')
css
.card {
  opacity: var(--progress);
}

Reduced Motion Preference ​

prefersReducedMotion() — reads the prefers-reduced-motion media query.

ts
function prefersReducedMotion(): boolean

Return 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:

ts
function usePrefersReducedMotion(): Ref<boolean> // Vue
function usePrefersReducedMotion(): boolean // React
vue
<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).

ts
function scrollToElement(el: Element, options?: ScrollToElementOptions): void

Parameters ​

el ​

Element · default: —

options ​

ScrollToElementOptions · default: {}

FieldTypeDefault
targetWindow | HTMLElementwindow
offsetnumber0 — 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:

ts
import { scrollToElement } from '@macrulez/inview-core'

scrollToElement(sectionEl, { offset: 80 }) // e.g. clearing a sticky header

useScrollTo (Vue) ​

ts
function useScrollTo(
  target?: MaybeRefOrGetter<Window | HTMLElement | null | undefined>,
): (el: Element | null | undefined, options?: UseScrollToOptions) => void

Returns 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.

vue
<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.

ts
function clamp(value: number, min: number, max: number): number

Parameters ​

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.

ts
function staggerDelay(index: number, options?: StaggerDelayOptions): string

Parameters ​

index ​

number · default: —

Position of this element in the sequence (0-based).

options ​

StaggerDelayOptions · default: {}

FieldTypeDefault
stepnumber60 — ms of delay per step
maxnumberInfinity — 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:

ts
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 1

createRevealController'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) ​

ts
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.

vue
<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).

ts
type Easing = (t: number) => number

const easings: Record<EasingName, Easing>
type EasingName = keyof typeof easings

Preset names: linear, easeInQuad, easeOutQuad, easeInOutQuad, easeInCubic, easeOutCubic, easeInOutCubic, easeInSine, easeOutSine, easeInOutSine.

Example:

ts
import { easings } from '@macrulez/inview-core'

const eased = easings.easeOutCubic(0.5)