bend-image-reveal
pixel-perfect/bend-image-revealFreeBlock
A pixel-art frame that flies in and warps with an air-friction bend, deformed on a WebGL plane by a GSAP-driven vertex shader.
Install it
npx shadcn@latest add https://www.pixel-perfect.space/r/bend-image-reveal.jsonSource
1"use client";23/* eslint-disable react-hooks/immutability */45/**6 * A self-contained, scrollable image collage whose frames warp with an air-friction bend driven by scroll velocity, on WebGL planes.7 */89import { RefObject, useEffect, useRef, useState } from "react";10import { Canvas, useFrame, useThree } from "@react-three/fiber";11import * as THREE from "three";12import gsap from "gsap";1314const SRC = "/bend-image-reveal.gif";15const lerp = (a: number, b: number, t: number) => a + (b - a) * t;1617const vertexShader = /* glsl */ `18 varying vec2 vUv;19 uniform float uBendY;20 void main() {21 vUv = uv;22 vec3 pos = position;23 float px = uv.x * 2.0 - 1.0; // -1 (left) .. +1 (right)24 pos.y += uBendY * px * px; // parabola: 0 at center, max at both edges25 gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);26 }27`;2829const fragmentShader = /* glsl */ `30 varying vec2 vUv;31 uniform sampler2D uTex;32 void main() {33 gl_FragColor = texture2D(uTex, vUv);34 }35`;3637function Collage({38 scrollRef,39 slots,40 count,41}: {42 scrollRef: RefObject<HTMLDivElement | null>;43 slots: RefObject<HTMLDivElement[]>;44 count: number;45}) {46 const { gl } = useThree();47 const [texture, setTexture] = useState<THREE.Texture | null>(null);48 const meshes = useRef<THREE.Mesh[]>([]);4950 const [material] = useState(51 () =>52 new THREE.ShaderMaterial({53 vertexShader,54 fragmentShader,55 uniforms: { uTex: { value: null as THREE.Texture | null }, uBendY: { value: 0 } },56 }),57 );58 const [geometry] = useState(() => new THREE.PlaneGeometry(1, 1, 32, 24));5960 useEffect(() => {61 new THREE.TextureLoader().load(SRC, (t) => {62 t.colorSpace = THREE.SRGBColorSpace;63 t.magFilter = THREE.NearestFilter;64 material.uniforms.uTex.value = t;65 setTexture(t);66 });67 }, [material]);6869 const lastScroll = useRef(0);70 const bend = useRef(0);71 useFrame(() => {72 const scroller = scrollRef.current;73 if (!scroller) return;74 const canvasRect = gl.domElement.getBoundingClientRect();7576 for (let i = 0; i < meshes.current.length; i++) {77 const slot = slots.current[i];78 const mesh = meshes.current[i];79 if (!slot || !mesh) continue;80 const r = slot.getBoundingClientRect();81 const cx = r.left + r.width / 2 - canvasRect.left;82 const cy = r.top + r.height / 2 - canvasRect.top;83 mesh.position.set(cx - canvasRect.width / 2, -(cy - canvasRect.height / 2), 0);84// … truncated
What it pulls in
npm packages
Files it writes
More from pixel-perfect
All 60 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| 3d-button3d-button | pixel-perfect | Blocks | Unverified | 2 deps | |
| abhinav-bento-buttonabhinav-bento-button | pixel-perfect | Blocks | Unverified | no deps | |
| accordion-carouselaccordion-carousel | pixel-perfect | Blocks | Free | 1 dep | |
| accordion-foldaccordion-fold | pixel-perfect | Blocks | Free | 1 dep | |
| activity-wheel-motionactivity-wheel-motion | pixel-perfect | Blocks | Free | 2 deps | |
| animated-textureanimated-texture | pixel-perfect | Blocks | Free | 1 dep | |
| aperture-mask-revealaperture-mask-reveal | pixel-perfect | Blocks | Free | 1 dep | |
| aurora-curtainaurora-curtain | pixel-perfect | Blocks | Free | no deps |
