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

Glass Lens

Живая капля стекла тянется за курсором, увеличивает и преломляет снимок под собой, на рывке вытягивается нитью и рвётся на капли, а в покое стягивается обратно в купол. Форма считается каждый кадр: между соседними каплями натянута капсула с переменным радиусом (это нить, она тончает с расстоянием и обрывается), голова вытянута вдоль скорости с сохранением площади, а движение считает пружинная цепочка с инерцией — вязкость задаёт жёсткость пружины и затухание, от воды до мёда. Вторая версия приёма «жидкое стекло»: у liquid-glass форма задана решёткой плиток.

threemouse-moveautomorphadvanced~160 КБreduced-motion ✓
↗
🎛 Настройки — крути и копируй

Установка

npx shadcn@latest add https://motiva.pages.dev/r/glass-lens.json

зависимости: three@^0.185.1 · @types/three@^0.185.0

Что нужно в проекте до установки
  1. Файл components.json в корне

    Из него CLI узнаёт, куда класть файлы. Алиасы — в такой форме:

    "aliases": { "components": "@/components", "lib": "@/lib" }

    Нет файла — установка не начнётся вовсе.

  2. Алиас @ объявлен дважды

    В корневом tsconfig.json и в конфиге сборщика. В Vite корневой tsconfig часто содержит только references — этого мало.

    "paths": { "@/*": ["./src/*"] }        // tsconfig.json
    resolve.alias = { "@": "/src" }        // vite.config.ts

    Без paths CLI молча создаёт папку с именем @.

  3. Токены подключены один раз на приложение

    Они приезжают вместе с записью — достаточно импортировать их в точке входа:

    import "@/styles/motiva-tokens.css";

    Без них блок потеряет цвета, ритм и тему.

  4. Своя типографика не спорит с блоком

    Наследуемые свойства протекают внутрь: text-align: center на обёртке отцентрует содержимое секции. То же касается font-familyи line-height.

  5. Проект без TypeScript? всё равно нужен tsconfig

    CLI грузит tsconfig.json безусловно, а при "tsx": false идёт за jsconfig.json и падает так же. Минимум — файл с pathsи "tsx": true в components.json.

    Приехавший рядом .tsx можно не трогать — вам нужен .vanilla.js.

Экспорт

↓ Скачать .zip

Код

"use client";

import { useEffect, useRef } from "react";
import { createGlassLens, type GlassLens as Engine, type GlassLensOptions } from "@/lib/glass-lens-engine";

export type GlassLensProps = Omit<GlassLensOptions, "animate">;

/**
 * Glass Lens — живая стеклянная линза ходит за курсором и на броске рвётся на капли:
 * их поля сливаются плавным минимумом, поэтому вблизи между каплями тянется перемычка.
 * Содержимое под стеклом увеличивается и преломляется по нормали этой формы.
 *
 * Вторая версия приёма «жидкое стекло»: у `liquid-glass` форма задана решёткой плиток,
 * здесь она каждый кадр считается из физики цепочки капель.
 *
 * Сцена — `createGlassLens` из соседнего файла; компонент держит холст, прокидывает
 * крутилки через `set()` и разбирает всё при размонтировании.
 * `prefers-reduced-motion` и `data-shot` рисуют один стоячий кадр.
 */
export function GlassLens(options: GlassLensProps = {}) {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const engine = useRef<Engine | null>(null);
  const first = useRef(options);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const animate = !(
      document.documentElement.hasAttribute("data-shot") ||
      window.matchMedia("(prefers-reduced-motion: reduce)").matches
    );
    engine.current = createGlassLens(canvas, { ...first.current, animate });
    return () => {
      engine.current?.destroy();
      engine.current = null;
    };
  }, []);

  const { image, radius, thickness, zoom, dispersion, drops, viscosity, frost } = options;
  useEffect(() => {
    engine.current?.set({ image, radius, thickness, zoom, dispersion, drops, viscosity, frost });
  }, [image, radius, thickness, zoom, dispersion, drops, viscosity, frost]);

  return (
    <div style={{ position: "absolute", inset: 0, overflow: "hidden", background: "#07070c" }}>
      <canvas
        ref={canvasRef}
        aria-hidden="true"
        style={{ display: "block", width: "100%", height: "100%", cursor: "none", touchAction: "none" }}
      />
    </div>
  );
}
лицензия: MITавтор: Motivaисточник: оригинал (Motiva); альтернативная версия к liquid-glass по слову владельца 13.09 (пересечение с существующей записью — законная форма второго варианта): там форма стекла задана решёткой, здесь считается физикой цепочки капель со слиянием по плавному минимумуv1.0.0