← каталог
Particle Field
Медленно дрейфующее 3D-поле из ~1800 частиц — фон на настоящем WebGL (React Three Fiber).
threeautoparallaxintermediate~160 КБreduced-motion ✓
Установка
npx shadcn@latest add https://motiva.pages.dev/r/particle-field.jsonзависимости: three@^0.185.1 · @react-three/fiber@^9.6.1
Что нужно в проекте до установки
components.jsonв корне — с алиасами в форме"components": "@/components". Нет файла — CLI не поймёт, куда класть.- Алиас
@объявлен дважды:"paths": {"@/*": ["./src/*"]}в корневомtsconfig.json(в Vite он часто содержит толькоreferences— тогда CLI создаёт папку@) иresolve.aliasв конфиге сборщика. - Токены подключены один раз на приложение:
import "@/styles/motiva-tokens.css"— они приезжают вместе с записью. Без них секция потеряет цвета, ритм и тему. - Своя типографика не должна спорить: если у вас на обёртке
text-align: center, он протечёт внутрь блоков. - Проект без TypeScript? Всё равно нужен минимальный
tsconfig.jsonсpathsи"tsx": trueвcomponents.json: CLI грузит tsconfig безусловно, а с"tsx": falseидёт заjsconfig.jsonи падает так же. Приехавший рядом.tsxможно не трогать — вам нужен.vanilla.js.
Экспорт
Код
"use client";
import { Canvas, useFrame } from "@react-three/fiber";
import { useMemo, useRef } from "react";
import * as THREE from "three";
function Particles({ count = 1800 }: { count?: number }) {
const ref = useRef<THREE.Points>(null);
const positions = useMemo(() => {
const a = new Float32Array(count * 3);
for (let i = 0; i < count * 3; i++) a[i] = (Math.random() - 0.5) * 11;
return a;
}, [count]);
useFrame((s) => {
if (!ref.current) return;
ref.current.rotation.y = s.clock.elapsedTime * 0.04;
ref.current.rotation.x = s.clock.elapsedTime * 0.02;
});
return (
<points ref={ref}>
<bufferGeometry>
<bufferAttribute attach="attributes-position" args={[positions, 3]} />
</bufferGeometry>
<pointsMaterial size={0.03} color="#38bdf8" transparent opacity={0.85} sizeAttenuation depthWrite={false} />
</points>
);
}
/** Дрейфующее 3D-поле частиц (React Three Fiber). `?noanim`/data-shot → стоп-кадр. */
export function ParticleField() {
const frozen =
typeof document !== "undefined" && document.documentElement.hasAttribute("data-shot");
return (
<div style={{ position: "absolute", inset: 0 }}>
<Canvas camera={{ position: [0, 0, 6], fov: 60 }} frameloop={frozen ? "demand" : "always"} dpr={[1, 2]}>
<Particles />
</Canvas>
</div>
);
}
Похожие эффекты
лицензия: MITавтор: Motivaисточник: оригинал (Motiva)v1.0.0