Hero: replace particle field with cobe globe (Capricorn view, multi-axis drag, reduced-motion safe), telemetry HUD with pointer parallax, particle text build effect, Space Grotesk display font. SEO: short titles with template, PT-BR accents, twitter cards on dynamic cases, escaped JSON-LD via lib/jsonld.ts, CSP report-only, 25 service FAQs answer-first. Design: fix TW4 layered reset killing spacing utilities, literal @theme tokens, mobile hero overflow, header overlap, empty case cards. Remove framer-motion (unified on motion/react), header version badge, phone number from hero CTA. Production build verified (24 static pages).
179 lines
4.4 KiB
TypeScript
179 lines
4.4 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
Float,
|
|
MeshDistortMaterial,
|
|
MeshTransmissionMaterial,
|
|
} from "@react-three/drei";
|
|
import { Canvas, useFrame } from "@react-three/fiber";
|
|
import { useMemo, useRef } from "react";
|
|
import * as THREE from "three";
|
|
|
|
function GlassSphere({
|
|
position,
|
|
scale = 1,
|
|
}: {
|
|
position: [number, number, number];
|
|
scale?: number;
|
|
}) {
|
|
const meshRef = useRef<THREE.Mesh>(null);
|
|
|
|
useFrame((state) => {
|
|
if (!meshRef.current) return;
|
|
const t = state.clock.elapsedTime;
|
|
meshRef.current.rotation.x = Math.sin(t * 0.3) * 0.2;
|
|
meshRef.current.rotation.z = Math.cos(t * 0.2) * 0.1;
|
|
});
|
|
|
|
return (
|
|
<Float speed={1.5} rotationIntensity={0.3} floatIntensity={0.8}>
|
|
<mesh ref={meshRef} position={position} scale={scale}>
|
|
<sphereGeometry args={[1, 32, 32]} />
|
|
<MeshDistortMaterial
|
|
color="#ccff00"
|
|
transparent
|
|
opacity={0.08}
|
|
distort={0.4}
|
|
speed={2}
|
|
wireframe
|
|
/>
|
|
</mesh>
|
|
</Float>
|
|
);
|
|
}
|
|
|
|
function WireframeRing({
|
|
position,
|
|
scale = 1,
|
|
}: {
|
|
position: [number, number, number];
|
|
scale?: number;
|
|
}) {
|
|
const meshRef = useRef<THREE.Mesh>(null);
|
|
|
|
useFrame((state) => {
|
|
if (!meshRef.current) return;
|
|
const t = state.clock.elapsedTime;
|
|
meshRef.current.rotation.x = t * 0.1;
|
|
meshRef.current.rotation.y = t * 0.15;
|
|
});
|
|
|
|
return (
|
|
<Float speed={1} rotationIntensity={0.5} floatIntensity={0.4}>
|
|
<mesh ref={meshRef} position={position} scale={scale}>
|
|
<torusGeometry args={[1, 0.02, 16, 100]} />
|
|
<meshStandardMaterial
|
|
color="#ccff00"
|
|
transparent
|
|
opacity={0.15}
|
|
emissive="#ccff00"
|
|
emissiveIntensity={0.1}
|
|
/>
|
|
</mesh>
|
|
</Float>
|
|
);
|
|
}
|
|
|
|
function DataStream({ count = 50 }: { count?: number }) {
|
|
const pointsRef = useRef<THREE.Points>(null);
|
|
|
|
const positions = useMemo(() => {
|
|
const pos = new Float32Array(count * 3);
|
|
for (let i = 0; i < count; i++) {
|
|
const i3 = i * 3;
|
|
// Create a spiral/helix pattern
|
|
const angle = (i / count) * Math.PI * 8;
|
|
const radius = 2 + Math.sin(angle * 0.5) * 0.5;
|
|
pos[i3] = Math.cos(angle) * radius;
|
|
pos[i3 + 1] = (i / count - 0.5) * 12;
|
|
pos[i3 + 2] = Math.sin(angle) * radius;
|
|
}
|
|
return pos;
|
|
}, [count]);
|
|
|
|
useFrame((state) => {
|
|
if (!pointsRef.current) return;
|
|
const t = state.clock.elapsedTime;
|
|
pointsRef.current.rotation.y = t * 0.05;
|
|
|
|
const posAttr = pointsRef.current.geometry.getAttribute(
|
|
"position",
|
|
) as THREE.BufferAttribute;
|
|
const arr = posAttr.array as Float32Array;
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
const i3 = i * 3;
|
|
arr[i3 + 1] += 0.02;
|
|
if (arr[i3 + 1] > 6) arr[i3 + 1] = -6;
|
|
}
|
|
posAttr.needsUpdate = true;
|
|
});
|
|
|
|
return (
|
|
<points ref={pointsRef}>
|
|
<bufferGeometry>
|
|
<bufferAttribute
|
|
attach="attributes-position"
|
|
args={[positions, 3]}
|
|
count={count}
|
|
/>
|
|
</bufferGeometry>
|
|
<pointsMaterial
|
|
color="#ccff00"
|
|
size={0.04}
|
|
transparent
|
|
opacity={0.4}
|
|
sizeAttenuation
|
|
blending={THREE.AdditiveBlending}
|
|
depthWrite={false}
|
|
/>
|
|
</points>
|
|
);
|
|
}
|
|
|
|
interface FloatingElementsProps {
|
|
variant?: "sphere" | "ring" | "data" | "combined";
|
|
className?: string;
|
|
}
|
|
|
|
export default function FloatingElements({
|
|
variant = "combined",
|
|
className = "",
|
|
}: FloatingElementsProps) {
|
|
return (
|
|
<div className={`pointer-events-none absolute inset-0 ${className}`}>
|
|
<Canvas
|
|
camera={{ position: [0, 0, 6], fov: 50 }}
|
|
dpr={[1, 1.5]}
|
|
gl={{
|
|
antialias: false,
|
|
alpha: true,
|
|
powerPreference: "high-performance",
|
|
}}
|
|
style={{ background: "transparent" }}
|
|
>
|
|
<ambientLight intensity={0.2} />
|
|
<pointLight position={[5, 5, 5]} intensity={0.3} color="#ccff00" />
|
|
|
|
{(variant === "sphere" || variant === "combined") && (
|
|
<>
|
|
<GlassSphere position={[3, 1, -2]} scale={0.8} />
|
|
<GlassSphere position={[-4, -1, -3]} scale={1.2} />
|
|
</>
|
|
)}
|
|
|
|
{(variant === "ring" || variant === "combined") && (
|
|
<>
|
|
<WireframeRing position={[0, 0, -2]} scale={1.5} />
|
|
<WireframeRing position={[-3, 2, -4]} scale={0.8} />
|
|
</>
|
|
)}
|
|
|
|
{(variant === "data" || variant === "combined") && (
|
|
<DataStream count={60} />
|
|
)}
|
|
</Canvas>
|
|
</div>
|
|
);
|
|
}
|