feat(pulse-3d-landing): landing 3D completa — Atomic Design + Three.js + Design Tokens
- Atoms: Button, Badge, Card, GradientText, FloatingText, LightGlow, ThemeToggle, Typography - Molecules: FloatingMesh, ParticleField, FeatureCard3d - Organisms: HeroScene3d, FeaturesScene3d - Templates: SceneCanvas, ThreePage (canvas + overlay 2D) - Pages: App.tsx — Hero + FeaturesOverview + About + CTA wireframes - Design Tokens completo: space/font/color/shadow/radius/material3d/camera3d/animation - Globals CSS: reset, grid, scrollbar, focus-visible, light/dark mode - Vite + React 18 + TypeScript + @react-three/fiber + drei + framer-motion - npm install + dev server OK - node_modules em .gitignore — commit apenas código fonte - Repo standalone: pulse-3d-landing/
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
/** Organismo: FeaturesScene3d
|
||||
* Seção de funcionalidades — objetos 3D em grid flutuante orbitais.
|
||||
*/
|
||||
import { useRef } from 'react'
|
||||
import { useFrame } from '@react-three/fiber'
|
||||
import { Text, Float } from '@react-three/drei'
|
||||
import * as THREE from 'three'
|
||||
|
||||
const FEATURES = [
|
||||
{ id:1, title:'Atomic Design', desc:'Componentes isolados, reutilizáveis e testáveis.', color:'#2563eb' },
|
||||
{ id:2, title:'Design Tokens', desc:'Cores, espaçamentos e animações 100% dinâmicos.', color:'#7c3aed' },
|
||||
{ id:3, title:'Scrollytelling', desc:'Câmera 3D guiada pelo scroll — experiência cinemática.',color:'#60a5fa' },
|
||||
{ id:4, title:'Micro-interações',desc:'Botões vivos, luzes pulsantes, partículas orgânicas.',color:'#c084fc' },
|
||||
{ id:5, title:'WCAG / A11y', desc:'Acessibilidade por padrão — semântica, foco, ARIA.',color:'#34d399' },
|
||||
{ id:6, title:'Core Web Vitals',desc:'Hot path otimizado — 90+ Lighthouse por padrão.', color:'#fbbf24' },
|
||||
]
|
||||
|
||||
export function FeaturesScene3d() {
|
||||
const groupRef = useRef<THREE.Group>(null!)
|
||||
|
||||
useFrame((_s, dt) => {
|
||||
if (groupRef.current) groupRef.current.rotation.y += dt * 0.015
|
||||
})
|
||||
|
||||
// posições em círculo para efeito globo
|
||||
const positions = FEATURES.map((_, i) => {
|
||||
const angle = (i / FEATURES.length) * Math.PI * 2 - Math.PI / 2
|
||||
const r = 3.2
|
||||
return [Math.cos(angle) * r, Math.sin(angle) * r * 0.45, Math.sin(angle) * 0.8] as [number, number, number]
|
||||
})
|
||||
|
||||
return (
|
||||
<group ref={groupRef}>
|
||||
{FEATURES.map((f, i) => (
|
||||
<Float key={f.id} speed={0.8 + i * 0.1} rotationIntensity={0.05} floatIntensity={0.35}>
|
||||
<group position={positions[i]}>
|
||||
<mesh castShadow>
|
||||
<boxGeometry args={[0.75, 0.75, 0.75]} />
|
||||
<meshStandardMaterial
|
||||
color={f.color}
|
||||
metalness={0.85}
|
||||
roughness={0.1}
|
||||
emissive={f.color}
|
||||
emissiveIntensity={0.22}
|
||||
/>
|
||||
</mesh>
|
||||
<pointLight color={f.color} intensity={1.2} distance={5} decay={2} />
|
||||
<Text
|
||||
position={[0, 0.85, 0]}
|
||||
fontSize={0.3}
|
||||
color="#e4e4e7"
|
||||
anchorX="center"
|
||||
maxWidth={2.5}
|
||||
>
|
||||
{f.title}
|
||||
</Text>
|
||||
</group>
|
||||
</Float>
|
||||
))}
|
||||
</group>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/** Organismo: HeroScene3d
|
||||
* Cena hero com partículas + floating mesh + luzes.
|
||||
* Controlado pelo scroll — a câmera viaja conforme o usuário rola.
|
||||
*/
|
||||
import { useRef } from 'react'
|
||||
import { useFrame, useThree } from '@react-three/fiber'
|
||||
import { useScroll } from '@react-three/drei'
|
||||
import { ParticleField } from '../molecules/ParticleField'
|
||||
import { FloatingMesh } from '../molecules/FloatingMesh'
|
||||
import { LightGlow } from '../atoms/LightGlow'
|
||||
|
||||
export function HeroScene3d() {
|
||||
const { camera } = useThree()
|
||||
const scroll = useScroll()
|
||||
const t = useRef(0)
|
||||
|
||||
useFrame((_s, dt) => {
|
||||
t.current += dt
|
||||
// Scroll-driven camera
|
||||
const offset = scroll.offset // 0 → 1 conforme scroll na página
|
||||
camera.position.z = 12 - offset * 4 // 12 → 8
|
||||
camera.position.y = offset * -3 // 0 → -3
|
||||
camera.lookAt(0, offset * -2, 0)
|
||||
})
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Luzes */}
|
||||
<ambientLight intensity={0.4} color="#2563eb" />
|
||||
<directionalLight position={[8, 10, 5]} intensity={1.0} color="#ffffff" castShadow />
|
||||
<LightGlow color="#2563eb" intensity={2.5} position={[-4, 3, 2]} distance={18} />
|
||||
<LightGlow color="#7c3aed" intensity={2.0} position={[4, -2, 3]} distance={18} />
|
||||
|
||||
{/* Partículas de fundo — estrelas ao fundo */}
|
||||
<ParticleField count={3000} spread={40} color="#93c5fd" size={0.035} speed={0.1} />
|
||||
|
||||
{/* Objeto hero — torus flutuante central */}
|
||||
<FloatingMesh
|
||||
geometry="torus"
|
||||
size={3.5}
|
||||
position={[0, 0.5, -2]}
|
||||
color="#2563eb"
|
||||
emissive="#1e3a8a"
|
||||
floatSpeed={0.5}
|
||||
floatAmp={0.25}
|
||||
spinSpeed={0.08}
|
||||
/>
|
||||
|
||||
{/* Anéis orbitais */}
|
||||
<OrbitalRing radius={5} color="#7c3aed" speed={0.05} tiltX={Math.PI / 3} />
|
||||
<OrbitalRing radius={6.5} color="#2563eb" speed={-0.03} tiltX={-Math.PI / 5} />
|
||||
|
||||
{/* Cubo menor complementar */}
|
||||
<FloatingMesh
|
||||
geometry="box"
|
||||
size={1.2}
|
||||
position={[-3.5, -1, 0]}
|
||||
color="#7c3aed"
|
||||
emissive="#4c1d95"
|
||||
floatSpeed={0.8}
|
||||
spinSpeed={0.2}
|
||||
showGlow={false}
|
||||
/>
|
||||
<FloatingMesh
|
||||
geometry="sphere"
|
||||
size={0.9}
|
||||
position={[3.2, 1.5, -1]}
|
||||
color="#60a5fa"
|
||||
emissive="#1e40af"
|
||||
floatSpeed={1.0}
|
||||
spinSpeed={0.3}
|
||||
showGlow={false}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
/** Componente auxiliar: anel orbital */
|
||||
function OrbitalRing({
|
||||
radius = 5,
|
||||
color = '#7c3aed',
|
||||
speed = 0.05,
|
||||
tiltX = Math.PI / 4,
|
||||
}: {
|
||||
radius?: number
|
||||
color? : string
|
||||
speed? : number
|
||||
tiltX? : number
|
||||
}) {
|
||||
const ringRef = useRef<any>(null!)
|
||||
useFrame((_s, dt) => {
|
||||
if (ringRef.current)
|
||||
ringRef.current.rotation.z += dt * speed
|
||||
})
|
||||
return (
|
||||
<mesh ref={ringRef} rotation={[tiltX, 0, 0]}>
|
||||
<torusGeometry args={[radius, 0.04, 16, 200]} />
|
||||
<meshStandardMaterial color={color} emissive={color} emissiveIntensity={0.15} transparent opacity={0.5} />
|
||||
</mesh>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { HeroScene3d } from './HeroScene3d'
|
||||
export { FeaturesScene3d } from './FeaturesScene3d'
|
||||
Reference in New Issue
Block a user