feat: redesign service cards with 3D icons, enhanced hover effects, and updated action buttons
This commit is contained in:
@@ -3,20 +3,56 @@ import { useRef } from 'react';
|
||||
import { Canvas, useFrame } from '@react-three/fiber';
|
||||
import { Float, Torus, Cylinder, Box, Octahedron, MeshTransmissionMaterial } from '@react-three/drei';
|
||||
import * as THREE from 'three';
|
||||
import { useLoader } from '@react-three/fiber';
|
||||
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
||||
import { Suspense, useMemo } from 'react';
|
||||
|
||||
function ActualBikeIcon() {
|
||||
const obj = useLoader(OBJLoader, '/uploads_files_3634924_uploads_files_850048_aerox.obj');
|
||||
const group = useRef<THREE.Group>(null);
|
||||
|
||||
const model = useMemo(() => {
|
||||
const clone = obj.clone();
|
||||
|
||||
const box = new THREE.Box3().setFromObject(clone);
|
||||
const center = box.getCenter(new THREE.Vector3());
|
||||
const size = box.getSize(new THREE.Vector3());
|
||||
const maxDim = Math.max(size.x, size.y, size.z);
|
||||
|
||||
const scale = 2.0 / maxDim;
|
||||
clone.scale.setScalar(scale);
|
||||
clone.position.sub(center.multiplyScalar(scale));
|
||||
clone.rotation.y = -Math.PI / 4;
|
||||
|
||||
const bodyMat = new THREE.MeshStandardMaterial({ color: '#00bc84', roughness: 0.2, metalness: 0.8 });
|
||||
const darkMat = new THREE.MeshStandardMaterial({ color: '#111111', roughness: 0.9 });
|
||||
|
||||
clone.traverse((child) => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
const b = new THREE.Box3().setFromObject(child);
|
||||
const s = new THREE.Vector3();
|
||||
b.getSize(s);
|
||||
const minLocal = Math.min(s.x, s.y, s.z);
|
||||
const maxLocal = Math.max(s.x, s.y, s.z);
|
||||
if (minLocal < 0.3 && maxLocal > 0.4) child.material = darkMat;
|
||||
else child.material = bodyMat;
|
||||
}
|
||||
});
|
||||
|
||||
return clone;
|
||||
}, [obj]);
|
||||
|
||||
function BikeIcon() {
|
||||
const mesh = useRef<THREE.Mesh>(null);
|
||||
useFrame((_, delta) => {
|
||||
if (mesh.current) {
|
||||
mesh.current.rotation.y += delta;
|
||||
mesh.current.rotation.x += delta * 0.5;
|
||||
if (group.current) {
|
||||
group.current.rotation.y += delta * 1.5;
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Float floatIntensity={2} speed={2}>
|
||||
<Torus ref={mesh} args={[1.2, 0.4, 16, 32]} rotation={[Math.PI / 2, 0, 0]}>
|
||||
<meshStandardMaterial color="#f97316" roughness={0.1} metalness={0.8} />
|
||||
</Torus>
|
||||
<Float floatIntensity={1.5} speed={2}>
|
||||
<group ref={group}>
|
||||
<primitive object={model} />
|
||||
</group>
|
||||
</Float>
|
||||
);
|
||||
}
|
||||
@@ -38,44 +74,68 @@ function InvestorIcon() {
|
||||
}
|
||||
|
||||
function SwapIcon() {
|
||||
const mesh = useRef<THREE.Mesh>(null);
|
||||
const group = useRef<THREE.Group>(null);
|
||||
useFrame((_, delta) => {
|
||||
if (mesh.current) {
|
||||
mesh.current.rotation.x += delta;
|
||||
mesh.current.rotation.y += delta * 1.2;
|
||||
if (group.current) {
|
||||
group.current.rotation.y += delta * 1.2;
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Float floatIntensity={2} speed={2}>
|
||||
<Box ref={mesh} args={[1.5, 1.5, 1.5]}>
|
||||
<MeshTransmissionMaterial
|
||||
color="#a855f7"
|
||||
thickness={0.5}
|
||||
roughness={0.1}
|
||||
transmission={0.9}
|
||||
ior={1.5}
|
||||
/>
|
||||
</Box>
|
||||
<Box args={[0.8, 0.8, 0.8]}>
|
||||
<meshStandardMaterial color="#d8b4fe" emissive="#a855f7" emissiveIntensity={0.5} />
|
||||
</Box>
|
||||
<Float floatIntensity={1.5} speed={2}>
|
||||
<group ref={group} scale={0.7} position={[0, -0.8, 0]}>
|
||||
{/* Main Station Body */}
|
||||
<Box args={[1.4, 2.8, 1]} position={[0, 1.4, 0]}>
|
||||
<meshStandardMaterial color="#e2e8f0" roughness={0.2} metalness={0.8} />
|
||||
</Box>
|
||||
{/* Glowing Screen */}
|
||||
<Box args={[0.9, 0.5, 0.1]} position={[0, 2.2, 0.51]}>
|
||||
<meshStandardMaterial color="#d8b4fe" emissive="#a855f7" emissiveIntensity={0.8} />
|
||||
</Box>
|
||||
{/* Battery Slots Container */}
|
||||
<Box args={[1.2, 1.2, 0.2]} position={[0, 1.0, 0.45]}>
|
||||
<meshStandardMaterial color="#1e293b" />
|
||||
</Box>
|
||||
{/* Battery 1 inside slot */}
|
||||
<Box args={[0.3, 0.6, 0.2]} position={[-0.3, 1.2, 0.5]}>
|
||||
<meshStandardMaterial color="#a855f7" />
|
||||
</Box>
|
||||
{/* Battery 2 inside slot */}
|
||||
<Box args={[0.3, 0.6, 0.2]} position={[0.3, 0.8, 0.5]}>
|
||||
<meshStandardMaterial color="#a855f7" />
|
||||
</Box>
|
||||
</group>
|
||||
</Float>
|
||||
);
|
||||
}
|
||||
|
||||
function MerchantIcon() {
|
||||
const mesh = useRef<THREE.Mesh>(null);
|
||||
const group = useRef<THREE.Group>(null);
|
||||
useFrame((_, delta) => {
|
||||
if (mesh.current) {
|
||||
mesh.current.rotation.x += delta * 0.5;
|
||||
mesh.current.rotation.y += delta;
|
||||
if (group.current) {
|
||||
group.current.rotation.y += delta * 1.2;
|
||||
group.current.rotation.z = Math.sin(_.clock.elapsedTime * 3) * 0.05;
|
||||
}
|
||||
});
|
||||
return (
|
||||
<Float floatIntensity={2} speed={2}>
|
||||
<Octahedron ref={mesh} args={[1.5]}>
|
||||
<meshStandardMaterial color="#00bc84" roughness={0.1} metalness={0.7} />
|
||||
</Octahedron>
|
||||
<Float floatIntensity={2} speed={2.5}>
|
||||
<group ref={group} scale={0.9}>
|
||||
{/* Main Cargo Box */}
|
||||
<Box args={[1.4, 1.4, 1.4]}>
|
||||
<meshStandardMaterial color="#fcd34d" roughness={0.8} />
|
||||
</Box>
|
||||
{/* Tape / Ribbon 1 */}
|
||||
<Box args={[1.42, 0.15, 1.42]} position={[0, 0, 0]}>
|
||||
<meshStandardMaterial color="#00bc84" roughness={0.3} />
|
||||
</Box>
|
||||
{/* Tape / Ribbon 2 */}
|
||||
<Box args={[1.42, 1.42, 0.15]} position={[0, 0, 0]}>
|
||||
<meshStandardMaterial color="#00bc84" roughness={0.3} />
|
||||
</Box>
|
||||
{/* Delivery Logo / Detail */}
|
||||
<Box args={[0.5, 0.5, 0.05]} position={[0, 0.3, 0.71]}>
|
||||
<meshStandardMaterial color="#ffffff" emissive="#00bc84" emissiveIntensity={0.3} />
|
||||
</Box>
|
||||
</group>
|
||||
</Float>
|
||||
);
|
||||
}
|
||||
@@ -87,7 +147,9 @@ export default function CardIcon3D({ type }: { type: 'bike' | 'investor' | 'swap
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight position={[5, 5, 5]} intensity={1} />
|
||||
<pointLight position={[-5, -5, -5]} intensity={0.5} />
|
||||
{type === 'bike' && <BikeIcon />}
|
||||
<Suspense fallback={null}>
|
||||
{type === 'bike' && <ActualBikeIcon />}
|
||||
</Suspense>
|
||||
{type === 'investor' && <InvestorIcon />}
|
||||
{type === 'swap' && <SwapIcon />}
|
||||
{type === 'merchant' && <MerchantIcon />}
|
||||
|
||||
Reference in New Issue
Block a user