motionsites中文站 / Structure Flow
Structure Flow
aadmin2026-09-16 发布3D0 阅读

Structure Flow

类型:3D · 分类:Three.js · 收录:2026-09-16 21:09:01 · 源码:会员可见

[hidecontent type="vip1"]
# Structure Flow
Thirteen authored Three.js field studies collected as one family, spanning particle domes, horizons, orbital systems, matrices, topology, fluid fields, embers, and vortexes.

> 来源:ThreeUI Community · https://threeui.com · MIT License(Meng To / Design+Code)
> 组件页:https://threeui.com/component/structure-flow · 预览动画:https://threeui.com/previews/structure-flow.webm
> 分类:Three.js
> 包含变体(12):emerald-horizon、orbital-sphere、dot-matrix、expanse-field、logic-core、dimensional-field、data-field、topology-field、nebula、fluid-field、ember-storm、flux-vortex

/* ===== src/shaders/structure-flow/StructureFlowBackground.tsx (tsx) ===== */
import { useEffect, useRef } from "react";
import { createStructureFlowRenderer, STRUCTURE_FLOW_DEFAULTS, type StructureFlowOptions } from "./structureFlowRenderer";

export type StructureFlowBackgroundProps = Partial<StructureFlowOptions> & { className?: string };
export function StructureFlowBackground({ className = "", ...props }: StructureFlowBackgroundProps) {
  const hostRef = useRef<HTMLDivElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
  const optionsRef = useRef({ ...STRUCTURE_FLOW_DEFAULTS, ...props }); optionsRef.current = { ...STRUCTURE_FLOW_DEFAULTS, ...props };
  useEffect(() => {
    const host = hostRef.current, canvas = canvasRef.current; if (!host || !canvas) return undefined;
    const renderer = createStructureFlowRenderer(canvas, () => optionsRef.current); let frame = 0, visible = true;
    const resize = () => { const bounds = host.getBoundingClientRect(); renderer.resize(bounds.width, bounds.height); renderer.render(); };
    const tick = () => { renderer.render(); frame = visible && !document.hidden ? requestAnimationFrame(tick) : 0; };
    const resizeObserver = new ResizeObserver(resize); const intersection = new IntersectionObserver(([entry]) => { visible = entry?.isIntersecting ?? true; if (visible && !frame) frame = requestAnimationFrame(tick); if (!visible && frame) cancelAnimationFrame(frame), frame = 0; });
    resizeObserver.observe(host); intersection.observe(host); resize(); frame = requestAnimationFrame(tick);
    return () => { if (frame) cancelAnimationFrame(frame); resizeObserver.disconnect(); intersection.disconnect(); renderer.dispose(); };
  }, []);
  const options = optionsRef.current;
  const mask = `linear-gradient(to bottom, transparent ${options.maskStart * 100}%, black ${options.maskSolid * 100}%, black 100%)`;
  return <div ref={hostRef} className={`threeui-background structure-flow${className ? ` ${className}` : ""}`} style={{ opacity: 0.8, WebkitMaskImage: mask, maskImage: mask }}><canvas ref={canvasRef} /></div>;
}

/* ===== src/shaders/structure-flow/StructureFlowCollection.tsx (tsx) ===== */
import { lazy, Suspense } from "react";

import type { DotMatrixBackgroundProps } from "../dot-matrix/DotMatrixBackground";
import type { EmeraldHorizonBackgroundProps } from "../emerald-horizon/EmeraldHorizonBackground";
import type { NeuformBatchEffectProps } from "../neuform-isolated/NeuformBatchEffects";
import type { NeuformCraftEffectProps } from "../neuform-isolated/NeuformCraftEffects";
import type { NeuformIsolatedEffectProps } from "../neuform-isolated/NeuformIsolatedEffects";
import type { OrbitalSphereBackgroundProps } from "../orbital-sphere/OrbitalSphereBackground";
import type { StructureFlowBackgroundProps } from "./StructureFlowBackground";

export const STRUCTURE_FLOW_VARIANTS = [
  "structure-flow",
  "emerald-horizon",
  "orbital-sphere",
  "dot-matrix",
  "expanse-field",
  "logic-core",
  "dimensional-field",
  "data-field",
  "topology-field",
  "nebula",
  "fluid-field",
  "ember-storm",
  "flux-vortex",
] as const;

export type StructureFlowVariant = (typeof STRUCTURE_FLOW_VARIANTS)[number];

type StructureVariantProps = StructureFlowBackgroundProps & { variant?: "structure-flow" };
type EmeraldVariantProps = EmeraldHorizonBackgroundProps & { variant: "emerald-horizon" };
type OrbitalVariantProps = OrbitalSphereBackgroundProps & { variant: "orbital-sphere" };
type DotMatrixVariantProps = DotMatrixBackgroundProps & { variant: "dot-matrix" };
type IsolatedVariantProps = NeuformIsolatedEffectProps & {
  variant: "expanse-field" | "logic-core" | "dimensional-field" | "data-field" | "topology-field";
};
type CraftVariantProps = NeuformCraftEffectProps & {
  variant: "nebula" | "fluid-field" | "ember-storm";
};
type FluxVariantProps = NeuformBatchEffectProps & { variant: "flux-vortex" };

export type StructureFlowCollectionProps =
  | StructureVariantProps
  | EmeraldVariantProps
  | OrbitalVariantProps
  | DotMatrixVariantProps
  | IsolatedVariantProps
  | CraftVariantProps
  | FluxVariantProps;

const StructureVariant = lazy(() =>
  import("./StructureFlowBackground").then((module) => ({ default: module.StructureFlowBackground })),
);
const EmeraldVariant = lazy(() =>
  import("../emerald-horizon/EmeraldHorizonBackground").then((module) => ({ default: module.EmeraldHorizonBackground })),
);
const OrbitalVariant = lazy(() =>
  import("../orbital-sphere/OrbitalSphereBackground").then((module) => ({ default: module.OrbitalSphereBackground })),
);
const DotMatrixVariant = lazy(() =>
  import("../dot-matrix/DotMatrixBackground").then((module) => ({ default: module.DotMatrixBackground })),
);
const ExpanseVariant = lazy(() =>
  import("../neuform-isolated/NeuformIsolatedEffects").then((module) => ({ default: module.ExpanseField })),
);
const LogicCoreVariant = lazy(() =>
  import("../neuform-isolated/NeuformIsolatedEffects").then((module) => ({ default: module.LogicCoreField })),
);
const DimensionalVariant = lazy(() =>
  import("../neuform-isolated/NeuformIsolatedEffects").then((module) => ({ default: module.DimensionalField })),
);
const DataVariant = lazy(() =>
  import("../neuform-isolated/NeuformIsolatedEffects").then((module) => ({ default: module.DataField })),
);
const TopologyVariant = lazy(() =>
  import("../neuform-isolated/NeuformIsolatedEffects").then((module) => ({ default: module.TopologyField })),
);
const NebulaVariant = lazy(() =>
  import("../neuform-isolated/NeuformCraftEffects").then((module) => ({ default: module.NebulaBackground })),
);
const FluidVariant = lazy(() =>
  import("../neuform-isolated/NeuformCraftEffects").then((module) => ({ default: module.FluidFieldBackground })),
);
const EmberVariant = lazy(() =>
  import("../neuform-isolated/NeuformCraftEffects").then((module) => ({ default: module.EmberStorm })),
);
const FluxVariant = lazy(() =>
  import("../neuform-isolated/NeuformBatchEffects").then((module) => ({ default: module.FluxVortex })),
);

const FALLBACK = <div className="threeui-background" style={{ background: "#050607" }} />;

export function StructureFlowCollection(props: StructureFlowCollectionProps) {
  if (props.variant === "emerald-horizon") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><EmeraldVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "orbital-sphere") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><OrbitalVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "dot-matrix") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><DotMatrixVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "expanse-field") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><ExpanseVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "logic-core") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><LogicCoreVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "dimensional-field") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><DimensionalVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "data-field") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><DataVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "topology-field") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><TopologyVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "nebula") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><NebulaVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "fluid-field") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><FluidVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "ember-storm") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><EmberVariant {...variantProps} /></Suspense>;
  }
  if (props.variant === "flux-vortex") {
    const { variant: _variant, ...variantProps } = props;
    return <Suspense fallback={FALLBACK}><FluxVariant {...variantProps} /></Suspense>;
  }

  const { variant: _variant, ...variantProps } = props;
  return <Suspense fallback={FALLBACK}><StructureVariant {...variantProps} /></Suspense>;
}

/* ===== src/shaders/structure-flow/structureFlowRenderer.ts (typescript) ===== */
import * as THREE from "three128";

export type StructureFlowOptions = { speed: number; pointSize: number; opacity: number; maskStart: number; maskSolid: number };
export const STRUCTURE_FLOW_DEFAULTS: StructureFlowOptions = { speed: 1, pointSize: 0.08, opacity: 0.4, maskStart: 0.2, maskSolid: 0.5 };

export function createStructureFlowRenderer(canvas: HTMLCanvasElement, getOptions: () => StructureFlowOptions) {
  const scene = new THREE.Scene();
  const camera = new THREE.PerspectiveCamera(60, 1, 0.1, 1000);
  camera.position.z = 30;
  camera.position.y = 5;
  const renderer = new THREE.WebGLRenderer({ canvas, alpha: true, antialias: true });
  renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
  const geometry = new THREE.BufferGeometry();
  const count = 15000;
  const positions = new Float32Array(count * 3);
  const radius = 25;
  for (let index = 0; index < count; index += 1) {
    const u = Math.random();
    Math.random(); // The canonical renderer sampled v even though it did not use it.
    const theta = u * 2 * Math.PI;
    const phi = Math.acos(Math.random() * 0.8 + 0.2);
    positions[index * 3] = radius * Math.sin(phi) * Math.cos(theta);
    positions[index * 3 + 1] = radius * Math.cos(phi) - 20;
    positions[index * 3 + 2] = radius * Math.sin(phi) * Math.sin(theta);
  }
  geometry.setAttribute("position", new THREE.BufferAttribute(positions, 3));
  const material = new THREE.PointsMaterial({ size: 0.08, color: 0xffffff, transparent: true, opacity: 0.4, blending: THREE.AdditiveBlending, depthWrite: false });
  const particles = new THREE.Points(geometry, material);
  scene.add(particles);
  return {
    resize(width: number, height: number) {
      camera.aspect = width / Math.max(1, height);
      camera.updateProjectionMatrix();
      renderer.setSize(width, height, false);
    },
    render() {
      const options = getOptions();
      particles.rotation.y += 0.0008 * options.speed;
      particles.rotation.z += 0.0002 * options.speed;
      material.size = options.pointSize;
      material.opacity = options.opacity;
      renderer.render(scene, camera);
    },
    dispose() { geometry.dispose(); material.dispose(); renderer.dispose(); },
  };
}

/* ===== src/shaders/structure-flow/three128.d.ts (typescript) ===== */
declare module "three128" {
  const THREE: typeof import("three");
  export = THREE;
}
[/hidecontent]
← 返回首页