feat: redesign service cards with 3D icons, enhanced hover effects, and updated action buttons

This commit is contained in:
sazzadulalambd
2026-06-22 17:17:40 +06:00
parent 24b00fefcd
commit f50b982ae1
2 changed files with 134 additions and 79 deletions

View File

@@ -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 />}