bend-mouse-follower
pixel-perfect/bend-mouse-followerFreeBlock
A pixel-art frame that smoothly chases the cursor and warps with an air-friction bend on a WebGL plane.
Install it
npx shadcn@latest add https://www.pixel-perfect.space/r/bend-mouse-follower.jsonSource
1"use client";23/**4 * A pixel-art frame that smoothly chases the cursor and warps 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 void main() {35 gl_FragColor = texture2D(uTex, vUv);36 }37`;3839function MouseImage({ containerRef }: { containerRef: RefObject<HTMLDivElement | null> }) {40 const meshRef = useRef<THREE.Mesh>(null);41 const matRef = useRef<THREE.ShaderMaterial>(null);42 const { size } = useThree();43 const [texture, setTexture] = useState<THREE.Texture | null>(null);4445 const uniforms = useMemo(46 () => ({47 uTex: { value: null as THREE.Texture | null },48 uBendX: { value: 0 },49 uBendY: { value: 0 },50 }),51 [],52 );5354 const planeH = Math.min(size.height * 0.4, (size.width * 0.55) / IMG_ASPECT);55 const planeW = planeH * IMG_ASPECT;5657 useEffect(() => {58 new THREE.TextureLoader().load(SRC, (t) => {59 t.colorSpace = THREE.SRGBColorSpace;60 t.magFilter = THREE.NearestFilter;61 setTexture(t);62 });63 }, []);6465 useEffect(() => {66 if (texture && matRef.current) matRef.current.uniforms.uTex.value = texture;67 }, [texture]);6869 const target = useRef({ x: 0, y: 0 });70 const st = useRef({ x: 0, y: 0, bx: 0, by: 0 });7172 useEffect(() => {73 const el = containerRef.current;74 if (!el) return;75 const onMove = (e: PointerEvent) => {76 const r = el.getBoundingClientRect();77 const inside =78 e.clientX >= r.left &&79 e.clientX <= r.right &&80 e.clientY >= r.top &&81 e.clientY <= r.bottom;82 if (inside) {83 target.current.x = e.clientX - r.left - size.width / 2;84 target.current.y = -(e.clientY - r.top - size.height / 2); // three.js: +y up85// … 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 |
