UNPKG

framer-motion-3d

Version:

A simple and powerful React animation library for @react-three/fiber

112 lines (109 loc) 4.54 kB
"use client"; import { jsx } from 'react/jsx-runtime'; import { createRoot, events, unmountComponentAtNode } from '@react-three/fiber'; import { MotionContext, MotionConfigContext, useForceUpdate, useIsomorphicLayoutEffect, clamp } from 'framer-motion'; import * as React from 'react'; import { forwardRef, useContext, useRef, useLayoutEffect } from 'react'; import { mergeRefs } from 'react-merge-refs'; import { MotionCanvasContext } from './MotionCanvasContext.mjs'; const devicePixelRatio = typeof window !== "undefined" ? window.devicePixelRatio : 1; const calculateDpr = (dpr) => Array.isArray(dpr) ? clamp(dpr[0], dpr[1], devicePixelRatio) : dpr || devicePixelRatio; /** * This file contains a version of R3F's Canvas component that uses our projection * system for layout measurements instead of use-react-measure so we can keep the * projection and cameras in frame. * * https://github.com/pmndrs/react-three-fiber/blob/master/packages/fiber/src/web/Canvas.tsx */ function Block({ set }) { useIsomorphicLayoutEffect(() => { set(new Promise(() => null)); return () => set(false); }, []); return null; } class ErrorBoundary extends React.Component { constructor() { super(...arguments); this.state = { error: false }; } componentDidCatch(error) { this.props.set(error); } render() { return this.state.error ? null : this.props.children; } } ErrorBoundary.getDerivedStateFromError = () => ({ error: true }); function CanvasComponent({ children, fallback, tabIndex, id, style, className, events: events$1, ...props }, forwardedRef) { /** * Import existing contexts to pass through variants and MotionConfig from * the DOM to the 3D tree. Shared variants aren't officially supported yet * because the parent DOM tree fires effects before the 3D tree, whereas * variants are expected to run from bottom-up in useEffect. */ const motionContext = useContext(MotionContext); const configContext = useContext(MotionConfigContext); const [forceRender] = useForceUpdate(); const layoutCamera = useRef(null); const dimensions = useRef({ size: { width: 0, height: 0 }, }); const { size, dpr } = dimensions.current; const containerRef = useRef(null); const handleResize = () => { const container = containerRef.current; dimensions.current = { size: { width: container.offsetWidth, height: container.offsetHeight, }, }; forceRender(); }; // Set canvas size on mount useLayoutEffect(handleResize, []); const canvasRef = React.useRef(null); const [block, setBlock] = React.useState(false); const [error, setError] = React.useState(false); // Suspend this component if block is a promise (2nd run) if (block) throw block; // Throw exception outwards if anything within canvas throws if (error) throw error; const root = useRef(null); if (size.width > 0 && size.height > 0) { if (!root.current) { root.current = createRoot(canvasRef.current); } root.current.configure({ ...props, dpr: dpr || props.dpr, size: { ...size, top: 0, left: 0 }, events: events$1 || events, }).render(jsx(ErrorBoundary, { set: setError, children: jsx(React.Suspense, { fallback: jsx(Block, { set: setBlock }), children: jsx(MotionCanvasContext.Provider, { value: { dimensions, layoutCamera, requestedDpr: calculateDpr(props.dpr), }, children: jsx(MotionConfigContext.Provider, { value: configContext, children: jsx(MotionContext.Provider, { value: motionContext, children: children }) }) }) }) })); } useIsomorphicLayoutEffect(() => { const container = canvasRef.current; return () => unmountComponentAtNode(container); }, []); return (jsx("div", { ref: containerRef, id: id, className: className, tabIndex: tabIndex, style: { position: "relative", width: "100%", height: "100%", overflow: "hidden", ...style, }, children: jsx("canvas", { ref: mergeRefs([canvasRef, forwardedRef]), style: { display: "block" }, children: fallback }) })); } /** * @deprecated Motion 3D is deprecated. */ const MotionCanvas = forwardRef(CanvasComponent); export { MotionCanvas };