@octanejs/motion
v0.1.37
Published
Framer Motion bindings for the octane renderer — reuses motion-dom's animation engine, swaps the React components for octane host components.
Readme
@octanejs/motion
Framer Motion for the octane UI framework.
Motion separates a framework-agnostic animation engine (animate) and gesture
primitives (hover, press) from its React components (motion.div,
AnimatePresence). This package reuses the engine + gestures verbatim and
reimplements the components on octane.
// before
import { motion, AnimatePresence } from 'motion/react';
// after
import { motion, AnimatePresence } from '@octanejs/motion';
function Card() @{
<motion.div
className="card"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.3 }}
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
{'hello'}
</motion.div>
}
function List(props) @{
<AnimatePresence>
@if (props.show) {
<motion.div exit={{ opacity: 0 }}>{'I fade out when removed'}</motion.div>
}
</AnimatePresence>
}What's bound
motion.<tag>—initial,animate,transition,whileHover,whileTap,whileFocus,whileInView(+viewport),exit,drag(+dragConstraints,onDrag*),layout,layoutId,variants, plus any DOM props (className, style, events, …) andstyleMotionValues spread/bound onto the element.AnimatePresence— exit animations on removal.MotionConfig— globaltransition/reducedMotiondefaults via context.useReducedMotion()— a liveprefers-reduced-motionsubscription; operating-system setting changes update mounted consumers.LayoutGroup— namespaceslayoutIdvalues so independent shared-layout surfaces do not cross-animate.LazyMotion+domAnimation/domMax+m— feature-gated hosts. The./react-mentry exposes every HTML/SVG host as a named export.variants— label resolution (animate="visible") + parent→child propagation +staggerChildren/delayChildren(number orstagger()function) /staggerDirection.useMotionValue(),useScroll(),useAnimate()— MotionValues, scroll-linked values, and imperative scoped animation.useTransform(),useSpring(),useMotionValueEvent()— MotionValue composition: derive a value (range-map / transformer / multi-input combiner), spring toward a value or source, and subscribe to a value's events.- Motion's framework-agnostic helpers (
animate,stagger, value types, …), re-exported.
How it works
octane had no public way for a runtime-proxy component to render a host element
wrapping children, nor to provide context from plain-TS — so this package added two
runtime primitives: hostComponent and provideContext. motion.<tag> renders a
real <tag> through hostComponent, captures the node, and drives:
- Animations from layout effects calling motion's
animate(); gestures viahover()/press()/inView(); MotionValues (fromuseMotionValue/useScroll) by subscribing instyleand writing the element directly. MotionConfig+variantsthroughprovideContext: a plain-TS component stamps context for its children (config defaults, active variant labels).dragwith pointer events (axis lock +dragConstraints).- Exit without any deferred-deletion machinery: octane fires cleanups before detaching the DOM, so a leaving element's unmount cleanup clones it (outside the range octane is about to remove), animates the exit on the clone, and removes it when it finishes.
layout/layoutIdvia FLIP: measure the box, and if it moved/resized — vs the previous commit (layout) or a same-id element that just unmounted (layoutId) — apply the inverse transform then animate it back to identity. The same cleanup-before-detach ordering lets a leavinglayoutIdelement record its box for a same-commit replacement; unused boxes expire after that commit.layout="position"applies only translation,layout="size"only scaling, and layout animations usetransition.layoutwhen provided.
Not yet ported
The full layout projection tree — nested projection, child scale correction, and
continuous shared-layout during drag (the layout/layoutId here are single-element
FLIPs). Also drag momentum/elastic physics and
useTransform's output-map form (useTransform(mv, [0, 100], { opacity: [0, 1] })).
Stagger specifics: when: 'beforeChildren' | 'afterChildren' parent/child sequencing
is not implemented, and a child's stagger index is fixed at registration order (a
keyed reorder does not re-stagger).
Status
Current scope, known divergences, and verification status are tracked in the
generated bindings status table, sourced from
this package's status.json.
