bend-zoom-reveal
pixel-perfect/bend-zoom-revealFreeBlock
A pixel-art frame that flies in, then zooms to fill the box as you scroll inside it, warping with an air-friction bend on a WebGL plane.
Install it
npx shadcn@latest add https://www.pixel-perfect.space/r/bend-zoom-reveal.jsonSource
1"use client";23/**4 * A pixel-art frame that flies in, then zooms to fill the box as you scroll inside it, warping with an air-friction bend on a WebGL plane.5 */67import { RefObject, useEffect, useMemo, useRef, useState } from "react";8import { Canvas, useFrame, useThree } from "@react-three/fiber";9import * as THREE from "three";10import gsap from "gsap";1112const SRC = "/bend-image-reveal.gif";13const IMG_ASPECT = 540 / 304;14const lerp = (a: number, b: number, t: number) => a + (b - a) * t;1516const vertexShader = /* glsl */ `17 varying vec2 vUv;18 uniform float uBendX;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 float py = uv.y * 2.0 - 1.0; // -1 (bottom) .. +1 (top)25 pos.y += uBendY * px * px;26 pos.x += uBendX * py * py;27 gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);28 }29`;3031const fragmentShader = /* glsl */ `32 varying vec2 vUv;33 uniform sampler2D uTex;34 uniform float uOpacity;35 void main() {36 vec4 c = texture2D(uTex, vUv);37 c.a *= uOpacity;38 gl_FragColor = c;39 }40`;4142function BendImage({ scrollRef }: { scrollRef: RefObject<HTMLDivElement | null> }) {43 const meshRef = useRef<THREE.Mesh>(null);44 const matRef = useRef<THREE.ShaderMaterial>(null);45 const { size } = useThree();46 const [texture, setTexture] = useState<THREE.Texture | null>(null);4748 const uniforms = useMemo(49 () => ({50 uTex: { value: null as THREE.Texture | null },51 uBendX: { value: 0 },52 uBendY: { value: 0 },53 uOpacity: { value: 0 },54 }),55 [],56 );5758 const planeH = Math.min(size.height * 0.42, (size.width * 0.7) / IMG_ASPECT);59 const planeW = planeH * IMG_ASPECT;60 const coverScale = Math.max(size.width / planeW, size.height / planeH);6162 useEffect(() => {63 new THREE.TextureLoader().load(SRC, (t) => {64 t.colorSpace = THREE.SRGBColorSpace;65 t.magFilter = THREE.NearestFilter;66 setTexture(t);67 });68 }, []);6970 const state = useRef({ x: 0, y: 0, s: 0, bx: 0, by: 0 });7172 useEffect(() => {73 const mesh = meshRef.current;74 const mat = matRef.current;75 if (!texture || !mesh || !mat) return;76 mat.uniforms.uTex.value = texture;7778 const rightX = size.width * 0.28;79 const topY = size.height * 0.28;80 const bottomY = -size.height * 0.28;8182 mesh.position.set(rightX, bottomY, 0);83 state.current.x = rightX;84 state.current.y = bottomY;8586 const tl = gsap87// … 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 |
