Motiva
Мои коллекции
← каталог

Shader Flow

Full-bleed WebGL-градиент на Three.js: плавно перетекающая плазма с IQ-палитрой. Настоящий шейдер, не CSS.

threeautoparallaxadvanced~165 КБreduced-motion ✓

Установка

npx shadcn@latest add https://motiva.pages.dev/r/shader-flow.json

зависимости: three@^0.185.1 · @react-three/fiber@^9.6.1

Что нужно в проекте до установки
  1. components.json в корне — с алиасами в форме"components": "@/components". Нет файла — CLI не поймёт, куда класть.
  2. Алиас @ объявлен дважды: "paths": {"@/*": ["./src/*"]} в корневом tsconfig.json (в Vite он часто содержит только references — тогда CLI создаёт папку@) и resolve.alias в конфиге сборщика.
  3. Токены подключены один раз на приложение:import "@/styles/motiva-tokens.css" — они приезжают вместе с записью. Без них секция потеряет цвета, ритм и тему.
  4. Своя типографика не должна спорить: если у вас на обёрткеtext-align: center, он протечёт внутрь блоков.
  5. Проект без TypeScript? Всё равно нужен минимальныйtsconfig.json с pathsи "tsx": true в components.json: CLI грузит tsconfig безусловно, а с "tsx": false идёт за jsconfig.json и падает так же. Приехавший рядом.tsx можно не трогать — вам нужен.vanilla.js.

Экспорт

↓ Скачать .zip

Код

"use client";

import { Canvas, useFrame, useThree } from "@react-three/fiber";
import { useMemo, useRef } from "react";
import * as THREE from "three";

const vertexShader = /* glsl */ `
  varying vec2 vUv;
  void main() {
    vUv = uv;
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
  }
`;

const fragmentShader = /* glsl */ `
  precision highp float;
  uniform float uTime;
  varying vec2 vUv;

  // IQ cosine palette
  vec3 palette(float t) {
    return vec3(0.5) + vec3(0.5) * cos(6.28318 * (vec3(1.0) * t + vec3(0.0, 0.33, 0.67)));
  }

  void main() {
    vec2 uv = vUv * 2.0 - 1.0;
    float t = uTime * 0.15;
    // domain warp → плавные перетекающие полосы
    float w = sin(uv.x * 3.0 + t) + cos(uv.y * 3.0 - t * 1.3);
    float n = sin(uv.x * 4.0 + w + t) * cos(uv.y * 4.0 - w - t);
    vec3 col = palette(0.55 + 0.45 * n + t * 0.1);
    col *= 1.0 - 0.35 * dot(uv, uv); // виньетка
    gl_FragColor = vec4(col, 1.0);
  }
`;

function ShaderPlane() {
  const { viewport } = useThree();
  const mat = useRef<THREE.ShaderMaterial>(null);
  const uniforms = useMemo(() => ({ uTime: { value: 0 } }), []);
  useFrame((state) => {
    const u = mat.current?.uniforms.uTime;
    if (u) u.value = state.clock.elapsedTime;
  });
  return (
    <mesh scale={[viewport.width, viewport.height, 1]}>
      <planeGeometry args={[1, 1]} />
      <shaderMaterial ref={mat} vertexShader={vertexShader} fragmentShader={fragmentShader} uniforms={uniforms} />
    </mesh>
  );
}

/** Full-bleed WebGL-градиент на Three.js. `?noanim`/data-shot → рендер одного кадра (frameloop demand). */
export function ShaderFlow() {
  const frozen =
    typeof document !== "undefined" && document.documentElement.hasAttribute("data-shot");
  return (
    <div style={{ position: "absolute", inset: 0 }}>
      <Canvas frameloop={frozen ? "demand" : "always"} dpr={[1, 2]} gl={{ antialias: true }}>
        <ShaderPlane />
      </Canvas>
    </div>
  );
}
лицензия: MITавтор: Motivaисточник: оригинал (Motiva)v1.0.0