vit-design-webapp / src / lib / effects / liquid-glass.ts Blame
290 lines
65beb53 samthecodingguy Jan 29, 2026
// Liquid Glass Effect System
// Inspired by iOS 26 design language
export interface LiquidGlassConfig {
  blur: number; // 0-50
  opacity: number; // 0-1
  saturation: number; // 1-2
  tint: string; // hex color for glass tint
  tintOpacity: number; // 0-1
  borderRadius: number;
  borderWidth: number;
  borderColor: string;
  innerGlow: boolean;
  innerGlowColor: string;
  innerGlowBlur: number;
  refraction: boolean;
  refractionIntensity: number; // 0-1
}
export const defaultLiquidGlassConfig: LiquidGlassConfig = {
  blur: 20,
  opacity: 0.25,
  saturation: 1.8,
  tint: '#ffffff',
  tintOpacity: 0.1,
  borderRadius: 24,
  borderWidth: 1,
  borderColor: 'rgba(255, 255, 255, 0.2)',
  innerGlow: true,
  innerGlowColor: 'rgba(255, 255, 255, 0.3)',
  innerGlowBlur: 1,
  refraction: false,
  refractionIntensity: 0.5,
};
// Preset glass effects
export const GLASS_PRESETS = {
  subtle: {
    ...defaultLiquidGlassConfig,
    blur: 10,
    opacity: 0.15,
    saturation: 1.2,
    innerGlow: false,
  },
  standard: {
    ...defaultLiquidGlassConfig,
  },
  frosted: {
    ...defaultLiquidGlassConfig,
    blur: 30,
    opacity: 0.4,
    saturation: 1.5,
    tintOpacity: 0.2,
  },
  vibrant: {
    ...defaultLiquidGlassConfig,
    blur: 25,
    opacity: 0.2,
    saturation: 2.0,
    innerGlow: true,
    innerGlowBlur: 2,
  },
  dark: {
    ...defaultLiquidGlassConfig,
    tint: '#000000',
    tintOpacity: 0.3,
    borderColor: 'rgba(255, 255, 255, 0.1)',
    innerGlowColor: 'rgba(255, 255, 255, 0.1)',
  },
  colorful: {
    ...defaultLiquidGlassConfig,
    blur: 35,
    opacity: 0.2,
    saturation: 2.0,
    tint: '#ff6b6b',
    tintOpacity: 0.15,
  },
} as const;
export type GlassPreset = keyof typeof GLASS_PRESETS;
// Generate CSS for liquid glass effect
export function generateLiquidGlassCSS(config: LiquidGlassConfig): string {
  const {
    blur,
    opacity,
    saturation,
    tint,
    tintOpacity,
    borderRadius,
    borderWidth,
    borderColor,
    innerGlow,
    innerGlowColor,
    innerGlowBlur,
  } = config;
  // Convert hex to rgba
  const hexToRgba = (hex: string, alpha: number) => {
    const r = parseInt(hex.slice(1, 3), 16);
    const g = parseInt(hex.slice(3, 5), 16);
    const b = parseInt(hex.slice(5, 7), 16);
    return `rgba(${r}, ${g}, ${b}, ${alpha})`;
  };
  const backgroundColor = hexToRgba(tint, tintOpacity);
  let boxShadow = `0 8px 32px rgba(0, 0, 0, ${opacity * 0.4})`;
  if (innerGlow) {
    boxShadow += `, inset 0 ${innerGlowBlur}px 0 ${innerGlowColor}`;
  }
  return `
    background: ${backgroundColor};
    backdrop-filter: blur(${blur}px) saturate(${saturation * 100}%);
    -webkit-backdrop-filter: blur(${blur}px) saturate(${saturation * 100}%);
    border-radius: ${borderRadius}px;
    border: ${borderWidth}px solid ${borderColor};
    box-shadow: ${boxShadow};
  `;
}
// Generate SVG filter for Konva canvas
export function generateGlassFilter(config: LiquidGlassConfig): string {
  const { blur, saturation } = config;
  return `
    <filter id="glass-filter" x="-50%" y="-50%" width="200%" height="200%">
      <feGaussianBlur in="SourceGraphic" stdDeviation="${blur / 2}" result="blur" />
      <feColorMatrix in="blur" type="saturate" values="${saturation}" result="saturated" />
      <feComponentTransfer in="saturated" result="adjusted">
        <feFuncA type="linear" slope="0.8" />
      </feComponentTransfer>
    </filter>
  `;
}
// Gradient Mesh Background Generator
export interface MeshGradientConfig {
  colors: string[];
  points: { x: number; y: number; radius: number }[];
  blur: number;
}
export function generateMeshGradientCSS(config: MeshGradientConfig): string {
  const { colors, points, blur } = config;
  const gradients = points.map((point, i) => {
    const color = colors[i % colors.length];
    return `radial-gradient(at ${point.x}% ${point.y}%, ${color} 0px, transparent ${point.radius}%)`;
  });
  return `
    background-color: ${colors[0]};
    background-image: ${gradients.join(', ')};
    filter: blur(${blur}px);
  `;
}
// Preset mesh gradients
export const MESH_GRADIENT_PRESETS = {
  sunset: {
    colors: ['#ff6b6b', '#feca57', '#ff9ff3', '#54a0ff'],
    points: [
      { x: 0, y: 100, radius: 50 },
      { x: 100, y: 0, radius: 50 },
      { x: 50, y: 50, radius: 40 },
      { x: 100, y: 100, radius: 45 },
    ],
    blur: 40,
  },
  ocean: {
    colors: ['#0abde3', '#10ac84', '#48dbfb', '#00d2d3'],
    points: [
      { x: 20, y: 80, radius: 55 },
      { x: 80, y: 20, radius: 55 },
      { x: 60, y: 60, radius: 45 },
      { x: 10, y: 30, radius: 40 },
    ],
    blur: 45,
  },
  aurora: {
    colors: ['#a29bfe', '#6c5ce7', '#fd79a8', '#00cec9'],
    points: [
      { x: 10, y: 90, radius: 60 },
      { x: 90, y: 10, radius: 55 },
      { x: 40, y: 40, radius: 50 },
      { x: 70, y: 70, radius: 45 },
    ],
    blur: 50,
  },
  midnight: {
    colors: ['#2c3e50', '#3498db', '#9b59b6', '#1a1a2e'],
    points: [
      { x: 0, y: 0, radius: 70 },
      { x: 100, y: 100, radius: 60 },
      { x: 50, y: 30, radius: 45 },
      { x: 30, y: 70, radius: 50 },
    ],
    blur: 55,
  },
  candy: {
    colors: ['#ff9a9e', '#fecfef', '#a18cd1', '#fad0c4'],
    points: [
      { x: 0, y: 50, radius: 50 },
      { x: 100, y: 50, radius: 50 },
      { x: 50, y: 0, radius: 45 },
      { x: 50, y: 100, radius: 45 },
    ],
    blur: 35,
  },
  forest: {
    colors: ['#134e5e', '#71b280', '#2d5016', '#a8e063'],
    points: [
      { x: 20, y: 20, radius: 55 },
      { x: 80, y: 80, radius: 55 },
      { x: 60, y: 30, radius: 45 },
      { x: 30, y: 60, radius: 50 },
    ],
    blur: 45,
  },
} as const;
export type MeshGradientPreset = keyof typeof MESH_GRADIENT_PRESETS;
// Spotlight/Highlight Effect for feature showcasing
export interface SpotlightConfig {
  x: number;
  y: number;
  radius: number;
  feather: number;
  backgroundColor: string;
  backgroundOpacity: number;
}
export function generateSpotlightCSS(config: SpotlightConfig): string {
  const { x, y, radius, feather, backgroundColor, backgroundOpacity } = config;
  return `
    background: radial-gradient(
      circle at ${x}% ${y}%,
      transparent 0%,
      transparent ${radius}%,
      rgba(0, 0, 0, 0) ${radius}%,
      ${backgroundColor} ${radius + feather}%
    );
    opacity: ${backgroundOpacity};
  `;
}
// Animation configurations for liquid effects
export interface LiquidAnimationConfig {
  duration: number;
  easing: string;
  loop: boolean;
}
export const LIQUID_ANIMATIONS = {
  pulse: {
    duration: 2000,
    easing: 'ease-in-out',
    loop: true,
    keyframes: [
      { blur: 20, scale: 1 },
      { blur: 25, scale: 1.02 },
      { blur: 20, scale: 1 },
    ],
  },
  shimmer: {
    duration: 3000,
    easing: 'linear',
    loop: true,
    keyframes: [
      { innerGlowOpacity: 0.2 },
      { innerGlowOpacity: 0.4 },
      { innerGlowOpacity: 0.2 },
    ],
  },
  float: {
    duration: 4000,
    easing: 'ease-in-out',
    loop: true,
    keyframes: [
      { y: 0 },
      { y: -10 },
      { y: 0 },
    ],
  },
} as const;