mirror of
https://github.com/DefectingCat/DefectingCat.github.io
synced 2025-07-15 16:51:37 +00:00
feat(3d): add camera animation
when hover on navbar
This commit is contained in:
@ -2,16 +2,24 @@ import { useFrame } from '@react-three/fiber';
|
|||||||
import { easing } from 'maath';
|
import { easing } from 'maath';
|
||||||
import { JSX, useRef } from 'react';
|
import { JSX, useRef } from 'react';
|
||||||
import { useMediaQuery } from 'react-responsive';
|
import { useMediaQuery } from 'react-responsive';
|
||||||
|
import useStore from 'store';
|
||||||
import * as THREE from 'three';
|
import * as THREE from 'three';
|
||||||
|
|
||||||
const DeskCamera = ({ children }: { children: JSX.Element }) => {
|
const DeskCamera = ({ children }: { children: JSX.Element }) => {
|
||||||
const groupRef = useRef<THREE.Group>(null);
|
const groupRef = useRef<THREE.Group>(null);
|
||||||
|
|
||||||
|
const navbarHoverItems = useStore((state) => state.navbarHoverItems);
|
||||||
const isMobile = useMediaQuery({ query: '(max-width: 768px)' });
|
const isMobile = useMediaQuery({ query: '(max-width: 768px)' });
|
||||||
useFrame((state, delta) => {
|
useFrame((state, delta) => {
|
||||||
|
// enter animation
|
||||||
easing.damp3(state.camera.position, [0, 0, 26], 0.25, delta);
|
easing.damp3(state.camera.position, [0, 0, 26], 0.25, delta);
|
||||||
|
|
||||||
if (!isMobile && groupRef.current) {
|
const hasHover = Object.values(navbarHoverItems).some((v) => v);
|
||||||
|
if (groupRef.current && hasHover) {
|
||||||
|
easing.dampE(groupRef.current.rotation, [0, 0, 0], 0.25, delta);
|
||||||
|
}
|
||||||
|
// mouse move animation
|
||||||
|
if (!isMobile && groupRef.current && !hasHover) {
|
||||||
const x = (() => {
|
const x = (() => {
|
||||||
const _x = state.pointer.y / 3;
|
const _x = state.pointer.y / 3;
|
||||||
if (_x < 0) {
|
if (_x < 0) {
|
||||||
@ -37,6 +45,40 @@ const DeskCamera = ({ children }: { children: JSX.Element }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// move camera to specific position when hovering navbar
|
||||||
|
useFrame((state, delta) => {
|
||||||
|
const map = {
|
||||||
|
blog: () => {
|
||||||
|
easing.damp3(state.camera.position, [0, 8.8, -10], 0.25, delta);
|
||||||
|
},
|
||||||
|
projects: () => {
|
||||||
|
easing.damp3(state.camera.position, [-10, 8, -12], 0.25, delta);
|
||||||
|
if (groupRef.current) {
|
||||||
|
easing.dampE(groupRef.current.rotation, [0, -7.2, 0], 0.25, delta);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tags: () => {
|
||||||
|
easing.damp3(state.camera.position, [-7, 5, -14], 0.25, delta);
|
||||||
|
if (groupRef.current) {
|
||||||
|
easing.dampE(groupRef.current.rotation, [1, 0, 0], 0.25, delta);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
friends: () => {
|
||||||
|
easing.damp3(state.camera.position, [10, 5, -12], 0.25, delta);
|
||||||
|
},
|
||||||
|
about: () => {
|
||||||
|
easing.damp3(state.camera.position, [-24, 14, -10], 0.25, delta);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(navbarHoverItems)) {
|
||||||
|
if (value) {
|
||||||
|
/** @ts-expect-error */
|
||||||
|
map[key]();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return <group ref={groupRef}>{children}</group>;
|
return <group ref={groupRef}>{children}</group>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@ import clsx from 'clsx';
|
|||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import { memo, useEffect, useState } from 'react';
|
import { memo, useEffect, useState } from 'react';
|
||||||
import { FiMenu } from 'react-icons/fi';
|
import { FiMenu } from 'react-icons/fi';
|
||||||
|
import useStore from 'store';
|
||||||
import DarkModeBtn from './dark-mode-btn';
|
import DarkModeBtn from './dark-mode-btn';
|
||||||
|
|
||||||
const txtMenu = [
|
const txtMenu = [
|
||||||
@ -71,6 +72,10 @@ const HeadBar = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const toggleNavbarHoverItems = useStore(
|
||||||
|
(state) => state.toggleNavbarHoverItems,
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header
|
<header
|
||||||
@ -122,6 +127,8 @@ const HeadBar = () => {
|
|||||||
'mb-2 last:mb-0 md:mb-0',
|
'mb-2 last:mb-0 md:mb-0',
|
||||||
'md:mr-4 md:last:mr-0',
|
'md:mr-4 md:last:mr-0',
|
||||||
)}
|
)}
|
||||||
|
onMouseOver={() => toggleNavbarHoverItems(m.name.toLowerCase())}
|
||||||
|
onMouseOut={() => toggleNavbarHoverItems(m.name.toLowerCase())}
|
||||||
>
|
>
|
||||||
<Link href={m.path}>{m.name}</Link>
|
<Link href={m.path}>{m.name}</Link>
|
||||||
</li>
|
</li>
|
||||||
|
@ -1,8 +1,21 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
|
|
||||||
|
// type NavbarHoverItemsKey = ['blog', 'projects', 'tags', 'friends', 'about'];
|
||||||
|
|
||||||
interface MainStore {
|
interface MainStore {
|
||||||
|
/** Loading state of the model */
|
||||||
modelLoading: boolean;
|
modelLoading: boolean;
|
||||||
toggleLoading: (loaded: boolean) => void;
|
toggleLoading: (loaded: boolean) => void;
|
||||||
|
|
||||||
|
/** mouse hover on navbar */
|
||||||
|
navbarHoverItems: {
|
||||||
|
blog: boolean;
|
||||||
|
projects: boolean;
|
||||||
|
tags: boolean;
|
||||||
|
friends: boolean;
|
||||||
|
about: boolean;
|
||||||
|
};
|
||||||
|
toggleNavbarHoverItems: (item: string) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useStore = create<MainStore>()((set) => ({
|
const useStore = create<MainStore>()((set) => ({
|
||||||
@ -11,6 +24,23 @@ const useStore = create<MainStore>()((set) => ({
|
|||||||
set(() => ({
|
set(() => ({
|
||||||
modelLoading: loaded,
|
modelLoading: loaded,
|
||||||
})),
|
})),
|
||||||
|
|
||||||
|
navbarHoverItems: {
|
||||||
|
blog: false,
|
||||||
|
projects: false,
|
||||||
|
tags: false,
|
||||||
|
friends: false,
|
||||||
|
about: false,
|
||||||
|
} as const,
|
||||||
|
toggleNavbarHoverItems: (item) =>
|
||||||
|
set((state) => ({
|
||||||
|
navbarHoverItems: {
|
||||||
|
...state.navbarHoverItems,
|
||||||
|
[item as unknown as string]:
|
||||||
|
/** @ts-expect-error */
|
||||||
|
!state.navbarHoverItems[item],
|
||||||
|
},
|
||||||
|
})),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export default useStore;
|
export default useStore;
|
||||||
|
Reference in New Issue
Block a user