hide custom cursor om mobile devices

This commit is contained in:
2025-04-21 14:26:17 -07:00
parent 7cc954ae07
commit fce17d397e
2 changed files with 69 additions and 36 deletions

View File

@@ -6,7 +6,7 @@ interface CursorState {
isPointer: boolean; isPointer: boolean;
isClicking: boolean; isClicking: boolean;
isOverBackground: boolean; isOverBackground: boolean;
isOverIframe: boolean; isMobile: boolean;
} }
const Cursor: React.FC = () => { const Cursor: React.FC = () => {
@@ -16,7 +16,7 @@ const Cursor: React.FC = () => {
isPointer: false, isPointer: false,
isClicking: false, isClicking: false,
isOverBackground: false, isOverBackground: false,
isOverIframe: false isMobile: false
}); });
const cursorRef = useRef<HTMLDivElement>(null); const cursorRef = useRef<HTMLDivElement>(null);
@@ -25,16 +25,62 @@ const Cursor: React.FC = () => {
const targetY = useRef(0); const targetY = useRef(0);
useEffect(() => { useEffect(() => {
// Check if device is mobile or tablet
const checkMobile = () => {
const isMobileOrTablet = window.matchMedia('(max-width: 1024px)').matches ||
('ontouchstart' in window) ||
(navigator.maxTouchPoints > 0);
setState(prev => ({ ...prev, isMobile: isMobileOrTablet }));
};
checkMobile();
// Add resize listener to detect device changes
window.addEventListener('resize', checkMobile);
// Function to inject styles into shadow DOM
const injectShadowStyles = () => {
const giscusElements = document.querySelectorAll('.giscus-frame');
giscusElements.forEach(element => {
if (element.shadowRoot) {
// Check if we already injected styles
if (!element.shadowRoot.querySelector('#custom-cursor-style')) {
const style = document.createElement('style');
style.id = 'custom-cursor-style';
style.textContent = `
* {
cursor: none !important;
}
`;
element.shadowRoot.appendChild(style);
}
}
});
};
// Watch for Giscus loading
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.addedNodes.length) {
injectShadowStyles();
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
// Initial injection
injectShadowStyles();
const updateCursorPosition = (e: MouseEvent) => { const updateCursorPosition = (e: MouseEvent) => {
targetX.current = e.clientX; targetX.current = e.clientX;
targetY.current = e.clientY; targetY.current = e.clientY;
const target = e.target as HTMLElement; const target = e.target as HTMLElement;
// Check if we're over an iframe
const isOverIframe = target.tagName === 'IFRAME' ||
target.closest('iframe') !== null;
// Check if the element is interactive // Check if the element is interactive
const isInteractive = target.tagName === 'A' || target.tagName === 'BUTTON' || const isInteractive = target.tagName === 'A' || target.tagName === 'BUTTON' ||
target.closest('a') !== null || target.closest('button') !== null || target.closest('a') !== null || target.closest('button') !== null ||
@@ -46,8 +92,7 @@ const Cursor: React.FC = () => {
y: e.clientY, y: e.clientY,
isPointer: isInteractive, isPointer: isInteractive,
isOverBackground: target.tagName === 'CANVAS' || isOverBackground: target.tagName === 'CANVAS' ||
target.closest('canvas') !== null, target.closest('canvas') !== null
isOverIframe: isOverIframe
})); }));
}; };
@@ -63,15 +108,6 @@ const Cursor: React.FC = () => {
setState(prev => ({ ...prev, x: -100, y: -100, isClicking: false })); setState(prev => ({ ...prev, x: -100, y: -100, isClicking: false }));
}; };
// Handle iframe mouse enter/leave
const handleFrameEnter = () => {
setState(prev => ({ ...prev, isOverIframe: true }));
};
const handleFrameLeave = () => {
setState(prev => ({ ...prev, isOverIframe: false }));
};
// Smooth cursor movement animation // Smooth cursor movement animation
const animate = () => { const animate = () => {
if (cursorRef.current) { if (cursorRef.current) {
@@ -94,13 +130,6 @@ const Cursor: React.FC = () => {
document.addEventListener('mouseup', handleMouseUp); document.addEventListener('mouseup', handleMouseUp);
document.addEventListener('mouseleave', handleMouseLeave); document.addEventListener('mouseleave', handleMouseLeave);
// Add iframe detection listeners
const iframes = document.getElementsByTagName('iframe');
Array.from(iframes).forEach(iframe => {
iframe.addEventListener('mouseenter', handleFrameEnter);
iframe.addEventListener('mouseleave', handleFrameLeave);
});
// Start animation // Start animation
requestRef.current = requestAnimationFrame(animate); requestRef.current = requestAnimationFrame(animate);
@@ -109,16 +138,11 @@ const Cursor: React.FC = () => {
document.removeEventListener('mousedown', handleMouseDown); document.removeEventListener('mousedown', handleMouseDown);
document.removeEventListener('mouseup', handleMouseUp); document.removeEventListener('mouseup', handleMouseUp);
document.removeEventListener('mouseleave', handleMouseLeave); document.removeEventListener('mouseleave', handleMouseLeave);
window.removeEventListener('resize', checkMobile);
// Remove iframe listeners
Array.from(iframes).forEach(iframe => {
iframe.removeEventListener('mouseenter', handleFrameEnter);
iframe.removeEventListener('mouseleave', handleFrameLeave);
});
if (requestRef.current) { if (requestRef.current) {
cancelAnimationFrame(requestRef.current); cancelAnimationFrame(requestRef.current);
} }
observer.disconnect();
}; };
}, []); }, []);
@@ -195,8 +219,8 @@ const Cursor: React.FC = () => {
const cursorColor = getCursorColor(); const cursorColor = getCursorColor();
const scale = state.isClicking ? 0.8 : (state.isPointer ? 1.2 : 1); const scale = state.isClicking ? 0.8 : (state.isPointer ? 1.2 : 1);
// Hide custom cursor when over iframe // Hide cursor completely on mobile
if (state.isOverIframe) { if (state.isMobile) {
return null; return null;
} }

View File

@@ -2,9 +2,18 @@
@tailwind components; @tailwind components;
@tailwind utilities; @tailwind utilities;
/* Hide default cursor globally */ /* Hide default cursor globally on desktop */
* { @media (min-width: 1025px) {
cursor: none !important; * {
cursor: none !important;
}
}
/* Show default cursor on mobile and tablet */
@media (max-width: 1024px) {
* {
cursor: auto !important;
}
} }
/* Chrome, Edge, and Safari */ /* Chrome, Edge, and Safari */