src/components/uiable/blocks/hero/hero-16/hero-16.tsx
"use client"
import { useEffect, useRef } from "react"
// third-party
import { motion, useMotionValue, useMotionTemplate } from "framer-motion"
import { Renderer, Program, Mesh, Triangle } from "ogl"
type MoltenMetalColorMode = "molten" | "ember" | "frost"
interface MoltenMetalProps {
color1?: string
color2?: string
color3?: string
speed?: number
scale?: number
detail?: number
glow?: number
coreSize?: number
swirl?: number
fold?: number
blackPoint?: number
brightness?: number
colorMode?: MoltenMetalColorMode
grain?: boolean
grainIntensity?: number
mouseInteraction?: boolean
mouseStrength?: number
opacity?: number
backgroundColor?: string
lightMode?: boolean
className?: string
}
const hexToRgb = (hex: string): [number, number, number] => {
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex)
if (!result) return [1, 1, 1]
return [
parseInt(result[1], 16) / 255,
parseInt(result[2], 16) / 255,
parseInt(result[3], 16) / 255,
]
}
const colorModeToFloat = (mode: MoltenMetalColorMode): number =>
mode === "ember" ? 1 : mode === "frost" ? 2 : 0
const moltenMetalVertex = `#version 300 es
in vec2 position;
void main() {
gl_Position = vec4(position, 0.0, 1.0);
}
`
const moltenMetalFragment = `#version 300 es
precision highp float;
uniform vec2 iResolution;
uniform float iTime;
uniform float uSpeed;
uniform float uScale;
uniform float uDetail;
uniform float uGlow;
uniform float uCoreSize;
uniform float uSwirl;
uniform float uFold;
uniform float uBlackPoint;
uniform float uBrightness;
uniform float uColorMode;
uniform float uGrain;
uniform float uGrainIntensity;
uniform float uOpacity;
uniform vec2 uMouse;
uniform float uMouseStrength;
uniform bool uEnableMouse;
uniform vec3 uColor1;
uniform vec3 uColor2;
uniform vec3 uColor3;
uniform vec3 uBackgroundColor;
uniform bool uLightMode;
out vec4 fragColor;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
}
void main() {
float time = iTime * uSpeed;
vec2 p = uScale * ((gl_FragCoord.xy - 0.5 * iResolution.xy) / iResolution.y) - 0.5;
vec2 drift = vec2(0.0);
if (uEnableMouse) {
drift = (uMouse - 0.5) * uMouseStrength * 2.0;
}
p += drift;
vec2 i = p;
float c = 0.0;
float r = length(p + vec2(sin(time), sin(time * 0.3 + 5.0)) * 0.5);
float d = length(p);
float rot = d + time + p.x * uSwirl;
float cosRot = cos(rot);
mat2 warp = mat2(cos(rot - sin(time / 5.0)), sin(rot), -sin(cosRot - time), cosRot) * uFold;
float glowCore = uGlow * uCoreSize;
for (float n = 0.0; n < 8.0; n++) {
if (n >= uDetail) break;
p *= warp;
float t = r - time / (n + 3.0);
i -= p + vec2(cos(t - i.x - r) + sin(t + i.y), sin(t - i.y) + cos(t + i.x) + r);
c += glowCore / length(vec2(sin(i.x + t), cos(i.y + t)));
}
c /= 6.0;
float intensity = max(c - uBlackPoint, 0.0) * uBrightness;
float g = clamp(intensity, 0.0, 1.0);
float mid = 0.5;
if (uColorMode > 1.5) {
mid = 0.65;
} else if (uColorMode > 0.5) {
mid = 0.35;
}
vec3 col = mix(uColor1, uColor2, smoothstep(0.0, mid, g));
col = mix(col, uColor3, smoothstep(mid, 1.0, g));
float a = g;
if (uGrain > 0.5) {
float gr = hash(gl_FragCoord.xy + iTime);
a += (gr - 0.5) * uGrainIntensity;
}
a = clamp(a, 0.0, 1.0) * uOpacity;
if (uLightMode) {
float signal = 1.0 - exp(-max(c, 0.0) * 6.5);
float body = smoothstep(0.075, 0.68, signal);
float ridge = smoothstep(0.42, 0.92, signal);
vec3 lightCol = mix(uColor1, uColor2, smoothstep(0.08, 0.52, signal));
lightCol = mix(lightCol, uColor3, smoothstep(0.52, 0.96, signal));
lightCol = mix(lightCol, lightCol * 0.72, ridge * 0.24);
float coverage = body * mix(0.2, 0.86, signal) * uOpacity;
if (uGrain > 0.5) {
float gr = hash(gl_FragCoord.xy + iTime);
coverage += (gr - 0.5) * uGrainIntensity * body * 0.16;
}
fragColor = vec4(mix(uBackgroundColor, lightCol, clamp(coverage, 0.0, 0.92)), 1.0);
} else {
fragColor = vec4(col * a, a);
}
}
`
type MoltenMetalCtx = {
renderer: InstanceType<typeof Renderer>
program: InstanceType<typeof Program>
mesh: InstanceType<typeof Mesh>
}
const moltenMetalCtxMap = new WeakMap<HTMLDivElement, MoltenMetalCtx>()
// Used only by this block (hero-16).
function MoltenMetal({
color1 = "#5227FF",
color2 = "#FF9FFC",
color3 = "#FFFFFF",
speed = 0.35,
scale = 4,
detail = 3,
glow = 1.6,
coreSize = 0.1,
swirl = 1,
fold = -0.2,
blackPoint = 0.05,
brightness = 1.3,
colorMode = "molten",
grain = true,
grainIntensity = 0.05,
mouseInteraction = true,
mouseStrength = 0.3,
opacity = 1.0,
backgroundColor = "#FFFFFF",
lightMode = false,
className = "",
}: MoltenMetalProps) {
const containerRef = useRef<HTMLDivElement | null>(null)
useEffect(() => {
const container = containerRef.current
if (!container) return
const renderer = new Renderer({
webgl: 2,
alpha: true,
premultipliedAlpha: true,
antialias: false,
dpr: Math.min(window.devicePixelRatio || 1, 2),
})
const gl = renderer.gl
gl.clearColor(0, 0, 0, 0)
const canvas = gl.canvas
canvas.style.width = "100%"
canvas.style.height = "100%"
canvas.style.display = "block"
container.appendChild(canvas)
const geometry = new Triangle(gl)
const program = new Program(gl, {
vertex: moltenMetalVertex,
fragment: moltenMetalFragment,
uniforms: {
iTime: { value: 0 },
iResolution: { value: new Float32Array([1, 1]) },
uSpeed: { value: 0.35 },
uScale: { value: 4 },
uDetail: { value: 3 },
uGlow: { value: 1.6 },
uCoreSize: { value: 0.1 },
uSwirl: { value: 1 },
uFold: { value: -0.2 },
uBlackPoint: { value: 0.05 },
uBrightness: { value: 1.3 },
uColorMode: { value: 0 },
uGrain: { value: 1 },
uGrainIntensity: { value: 0.05 },
uOpacity: { value: 1.0 },
uMouse: { value: new Float32Array([0.5, 0.5]) },
uMouseStrength: { value: 0.3 },
uEnableMouse: { value: true },
uColor1: { value: new Float32Array([1, 1, 1]) },
uColor2: { value: new Float32Array([1, 1, 1]) },
uColor3: { value: new Float32Array([1, 1, 1]) },
uBackgroundColor: { value: new Float32Array([1, 1, 1]) },
uLightMode: { value: false },
},
})
const mesh = new Mesh(gl, { geometry, program })
moltenMetalCtxMap.set(container, { renderer, program, mesh })
const setSize = () => {
const rect = container.getBoundingClientRect()
const w = Math.max(1, Math.floor(rect.width))
const h = Math.max(1, Math.floor(rect.height))
renderer.setSize(w, h)
const res = program.uniforms.iResolution.value as Float32Array
res[0] = gl.drawingBufferWidth
res[1] = gl.drawingBufferHeight
renderer.render({ scene: mesh })
}
const ro = new ResizeObserver(setSize)
ro.observe(container)
setSize()
const targetMouse: [number, number] = [0.5, 0.5]
const currentMouse: [number, number] = [0.5, 0.5]
const handleMouseMove = (e: MouseEvent) => {
const rect = canvas.getBoundingClientRect()
targetMouse[0] = (e.clientX - rect.left) / rect.width
targetMouse[1] = 1.0 - (e.clientY - rect.top) / rect.height
}
const handleMouseLeave = () => {
targetMouse[0] = 0.5
targetMouse[1] = 0.5
}
canvas.addEventListener("mousemove", handleMouseMove)
canvas.addEventListener("mouseleave", handleMouseLeave)
let raf = 0
let isVisible = true
let isPageVisible = !document.hidden
const t0 = performance.now()
const loop = (t: number) => {
program.uniforms.iTime.value = (t - t0) * 0.001
currentMouse[0] += 0.05 * (targetMouse[0] - currentMouse[0])
currentMouse[1] += 0.05 * (targetMouse[1] - currentMouse[1])
const m = program.uniforms.uMouse.value as Float32Array
m[0] = currentMouse[0]
m[1] = currentMouse[1]
renderer.render({ scene: mesh })
raf = requestAnimationFrame(loop)
}
const tryStart = () => {
if (isVisible && isPageVisible && raf === 0)
raf = requestAnimationFrame(loop)
}
const tryStop = () => {
if (raf !== 0) {
cancelAnimationFrame(raf)
raf = 0
}
}
const io = new IntersectionObserver(
([entry]) => {
isVisible = entry.isIntersecting
if (isVisible) {
tryStart()
} else {
tryStop()
}
},
{ threshold: 0 }
)
io.observe(container)
const onVisibility = () => {
isPageVisible = !document.hidden
if (isPageVisible) {
tryStart()
} else {
tryStop()
}
}
document.addEventListener("visibilitychange", onVisibility)
tryStart()
return () => {
tryStop()
ro.disconnect()
io.disconnect()
document.removeEventListener("visibilitychange", onVisibility)
canvas.removeEventListener("mousemove", handleMouseMove)
canvas.removeEventListener("mouseleave", handleMouseLeave)
moltenMetalCtxMap.delete(container)
if (canvas.parentNode === container) container.removeChild(canvas)
gl.getExtension("WEBGL_lose_context")?.loseContext()
}
}, [])
useEffect(() => {
const container = containerRef.current
if (!container) return
const ctx = moltenMetalCtxMap.get(container)
if (!ctx) return
const u = ctx.program.uniforms
u.uSpeed.value = speed
u.uScale.value = scale
u.uDetail.value = detail
u.uGlow.value = glow
u.uCoreSize.value = Math.max(coreSize, 0.001)
u.uSwirl.value = swirl
u.uFold.value = fold
u.uBlackPoint.value = blackPoint
u.uBrightness.value = brightness
u.uColorMode.value = colorModeToFloat(colorMode)
u.uGrain.value = grain ? 1 : 0
u.uGrainIntensity.value = grainIntensity
u.uOpacity.value = opacity
u.uMouseStrength.value = mouseStrength
u.uEnableMouse.value = mouseInteraction
const c1 = hexToRgb(color1)
const c2 = hexToRgb(color2)
const c3 = hexToRgb(color3)
const bg = hexToRgb(backgroundColor)
const uc1 = u.uColor1.value as Float32Array
const uc2 = u.uColor2.value as Float32Array
const uc3 = u.uColor3.value as Float32Array
uc1[0] = c1[0]
uc1[1] = c1[1]
uc1[2] = c1[2]
uc2[0] = c2[0]
uc2[1] = c2[1]
uc2[2] = c2[2]
uc3[0] = c3[0]
uc3[1] = c3[1]
uc3[2] = c3[2]
u.uBackgroundColor.value[0] = bg[0]
u.uBackgroundColor.value[1] = bg[1]
u.uBackgroundColor.value[2] = bg[2]
}, [
color1,
color2,
color3,
speed,
scale,
detail,
glow,
coreSize,
swirl,
fold,
blackPoint,
brightness,
colorMode,
grain,
grainIntensity,
mouseInteraction,
mouseStrength,
opacity,
backgroundColor,
lightMode,
])
return (
<div
ref={containerRef}
className={`relative h-full w-full overflow-hidden ${className}`.trim()}
/>
)
}
// Used only by this block (hero-16).
function HeroBackground() {
return (
<div className="absolute inset-0 z-10">
<div className="absolute inset-0 z-10 bg-card/90"></div>
<MoltenMetal
color1="#ffffff"
color2="#00786f"
color3="#ffffff"
colorMode="molten"
speed={0.45}
scale={7.3}
detail={5}
glow={3}
coreSize={0.09}
swirl={0.75}
fold={-0.31}
blackPoint={0.14}
brightness={1.3}
opacity={1}
grain
grainIntensity={0}
mouseInteraction
mouseStrength={1}
/>
</div>
)
}
const fadeInUp: any = {
hidden: { opacity: 0, y: 25 },
visible: (i: number = 0) => ({
opacity: 1,
y: 0,
transition: {
duration: 0.6,
delay: i * 0.1,
ease: [0.25, 0.46, 0.45, 0.94],
},
}),
}
export default function Hero16() {
const heroRef = useRef<HTMLElement>(null)
const mouseX = useMotionValue(0)
const mouseY = useMotionValue(0)
const handleMouseMove = (e: React.MouseEvent) => {
if (!heroRef.current) return
const { left, top } = heroRef.current.getBoundingClientRect()
mouseX.set(e.clientX - left)
mouseY.set(e.clientY - top)
}
return (
<section
ref={heroRef}
onMouseMove={handleMouseMove}
className="relative overflow-hidden px-4 pt-32 pb-16 text-center md:px-8 md:pt-38 md:pb-20"
>
<HeroBackground />
{/* Dynamic Glow following mouse */}
<motion.div
className="pointer-events-none absolute inset-0 opacity-40"
style={{
background: useMotionTemplate`
radial-gradient(
400px circle at ${mouseX}px ${mouseY}px,
rgba(45, 212, 191, 0.15),
transparent 80%
)
`,
}}
/>
<div className="pointer-events-none absolute top-10 left-1/3 -z-10 h-72 w-72 rounded-full bg-teal-100/40 blur-[100px]" />
<motion.div
initial="hidden"
animate="visible"
variants={fadeInUp}
custom={0}
className="relative z-10 container mx-auto"
>
<span className="mb-6 inline-block rounded-full border border-teal-100/50 bg-teal-50/80 px-4 py-1.5 text-xs font-bold tracking-[0.2em] text-teal-700 uppercase shadow-sm">
Connect With Us
</span>
<h1 className="text-4xl leading-[1.1] font-extrabold tracking-tight text-teal-950 sm:text-5xl md:text-6xl">
Let's build the{" "}
<span className="bg-gradient-to-r from-teal-600 to-emerald-500 bg-clip-text text-transparent">
future of payments
</span>
</h1>
<p className="mx-auto mt-8 max-w-3xl text-lg leading-relaxed text-slate-600 sm:text-xl">
Have technical questions about our APIs, pricing brackets, or ledger
synchronization engine? Connect with our support engineers.
</p>
</motion.div>
</section>
)
}






























