@phazr/custom-cursor
Version:
A lightweight and highly customizable React component for creating unique and interactive cursor experiences.
144 lines (140 loc) • 5.47 kB
JavaScript
'use client';
import { jsx, jsxs } from 'react/jsx-runtime';
import { createContext, useContext, useState, useEffect, useCallback } from 'react';
import { useMotionValue, useSpring, motion } from 'motion/react';
const CursorContext = createContext(null);
const useCursor = () => {
const context = useContext(CursorContext);
if (!context) {
throw new Error('useCursor must be used within a CursorProvider');
}
return context;
};
function CursorProvider({ children, className = '', enableOnTouch = false, }) {
const [variant, setVariant] = useState('default');
const [customConfig, setCustomConfig] = useState();
const [isTouchDevice, setIsTouchDevice] = useState(false);
useEffect(() => {
const checkTouchDevice = () => {
setIsTouchDevice('ontouchstart' in window || navigator.maxTouchPoints > 0);
};
checkTouchDevice();
window.addEventListener('resize', checkTouchDevice);
return () => window.removeEventListener('resize', checkTouchDevice);
}, []);
useEffect(() => {
if (isTouchDevice && !enableOnTouch) {
return;
}
if (variant !== 'input') {
document.body.classList.add('phazr-cursor-none');
}
else {
document.body.classList.remove('phazr-cursor-none');
}
return () => {
document.body.classList.remove('phazr-cursor-none');
};
}, [variant, isTouchDevice, enableOnTouch]);
const setVariantCallback = useCallback((newVariant) => {
setVariant(newVariant);
}, []);
const setCustomConfigCallback = useCallback((config) => {
setCustomConfig(config);
}, []);
if (isTouchDevice && !enableOnTouch) {
return jsx("div", { className: className, children: children });
}
return (jsx(CursorContext.Provider, { value: {
variant,
setVariant: setVariantCallback,
customConfig,
setCustomConfig: setCustomConfigCallback,
}, children: jsx("div", { className: className, children: children }) }));
}
function Cursor({ className = '', springConfig = { damping: 28, stiffness: 500 }, }) {
const { variant, customConfig } = useCursor();
const [isTouchDevice, setIsTouchDevice] = useState(false);
const cursorX = useMotionValue(-100);
const cursorY = useMotionValue(-100);
const cursorXSpring = useSpring(cursorX, springConfig);
const cursorYSpring = useSpring(cursorY, springConfig);
useEffect(() => {
setIsTouchDevice('ontouchstart' in window || navigator.maxTouchPoints > 0);
}, []);
useEffect(() => {
if (isTouchDevice)
return;
const mouseMove = (e) => {
cursorX.set(e.clientX);
cursorY.set(e.clientY);
};
window.addEventListener('mousemove', mouseMove);
return () => {
window.removeEventListener('mousemove', mouseMove);
};
}, [cursorX, cursorY, isTouchDevice]);
if (isTouchDevice) {
return null;
}
const cursorVariants = {
default: {
height: 16,
width: 16,
backgroundColor: '#fff',
mixBlendMode: 'difference',
},
link: {
height: 64,
width: 64,
backgroundColor: '#fff',
mixBlendMode: 'difference',
},
text: {
height: 8,
width: 8,
backgroundColor: '#fff',
mixBlendMode: 'difference',
},
input: {
height: 0,
width: 0,
opacity: 0,
},
sayHi: {
height: 90,
width: 90,
backgroundColor: '#fff',
mixBlendMode: 'difference',
},
custom: {
height: customConfig?.size || 32,
width: customConfig?.size || 32,
backgroundColor: customConfig?.backgroundColor || '#fff',
mixBlendMode: customConfig?.mixBlendMode || 'difference',
},
};
const textVariants = {
default: { opacity: 0, transition: { duration: 0.1 } },
link: { opacity: 0, transition: { duration: 0.1 } },
text: { opacity: 0, transition: { duration: 0.1 } },
input: { opacity: 0, transition: { duration: 0.1 } },
sayHi: { opacity: 1, transition: { delay: 0.1 } },
custom: {
opacity: customConfig?.text ? 1 : 0,
transition: { delay: customConfig?.text ? 0.1 : 0 },
},
};
return (jsx(motion.div, { variants: cursorVariants, animate: variant, style: {
left: cursorXSpring,
top: cursorYSpring,
x: '-50%',
y: '-50%',
}, className: `phazr-cursor-container ${className}`, children: jsxs(motion.div, { variants: textVariants, animate: variant, className: "phazr-cursor-text", style: {
color: customConfig?.textColor || '#000',
fontSize: customConfig?.fontSize || '16px',
fontFamily: customConfig?.fontFamily || 'inherit',
}, children: [variant === 'sayHi' && (jsxs("div", { className: "phazr-cursor-sayhi", children: [jsx("span", { children: "Say" }), jsx("span", { children: "Hi" })] })), variant === 'custom' && customConfig?.text && (jsx("span", { children: customConfig.text }))] }) }));
}
export { Cursor, CursorProvider, useCursor };
//# sourceMappingURL=index.mjs.map