Draggable Avatar
scrollxui/draggable-avatarFreeComponent
It displays a circular, draggable avatar with customizable size and style.
Live preview
live · rendered by the registry
Rendered by scrollxui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://scrollxui.dev/registry/draggable-avatar.jsonSource
1'use client';2import React, { useState, useRef, useEffect } from 'react';3import { motion, PanInfo } from 'motion/react';45interface DraggableAvatarProps {6 image?: string;7 borderColor?: string;8 range?: number;9 size?: number;10}1112export default function DraggableAvatar({13 image = 'https://cdn.pixabay.com/photo/2023/06/26/04/38/ai-generated-8088680_1280.jpg',14 borderColor = '#60A5FA',15 range = 300,16 size = 100,17}: DraggableAvatarProps) {18 const [isHolding, setIsHolding] = useState(false);19 const [constraints, setConstraints] = useState({20 left: -range,21 right: range,22 top: -range,23 bottom: range,24 });25 const containerRef = useRef<HTMLDivElement>(null);26 const motionRef = useRef<HTMLDivElement>(null);27 const lineLength = 45;28 const lineLengthHolding = 30;29 const markerSize = 12;3031 useEffect(() => {32 const updateConstraints = () => {33 if (containerRef.current) {34 const rect = containerRef.current.getBoundingClientRect();35 const maxX = Math.min(range, window.innerWidth - rect.left - size);36 const maxY = Math.min(37 range,38 window.innerHeight - rect.top - size - lineLength - markerSize,39 );40 const minX = Math.max(-range, -rect.left);41 const minY = Math.max(-range, -rect.top);4243 setConstraints({44 left: minX,45 right: maxX,46 top: minY,47 bottom: maxY,48 });49 }50 };5152 updateConstraints();53 window.addEventListener('resize', updateConstraints);54 return () => window.removeEventListener('resize', updateConstraints);55 }, [range, size, lineLength, markerSize]);5657 const handleDragEnd = (58 _event: MouseEvent | TouchEvent | PointerEvent,59 info: PanInfo,60 ) => {61 setIsHolding(false);62 const distance = Math.sqrt(info.offset.x ** 2 + info.offset.y ** 2);6364 if (distance > range && motionRef.current) {65 const angle = Math.atan2(info.offset.y, info.offset.x);66 const newX = Math.cos(angle) * range;67 const newY = Math.sin(angle) * range;6869 motionRef.current.style.transform = `translate(${newX}px, ${newY}px)`;70 }71 };7273 const currentLineLength = isHolding ? lineLengthHolding : lineLength;7475 return (76 <div ref={containerRef} className='relative'>77 <motion.div78 ref={motionRef}79 drag80 dragElastic={0}81 dragMomentum={false}82 dragConstraints={constraints}83 onPointerDown={() => setIsHolding(true)}84// … truncated
What it pulls in
npm packages
Files it writes
More from scrollxui
All 180 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | scrollxui | Components | Free | 4 deps | |
| Alert Dialogalert-dialog | scrollxui | Components | Free | 7 deps | |
| Animated Buttonanimated-button | scrollxui | Components | Free | 4 deps | |
| Animated Tabsanimated-tabs | scrollxui | Components | Free | 1 dep | |
| Animated Testimonialsanimated-testimonials | scrollxui | Components | Free | 2 deps | |
| Animated TextGenerateanimated-textgenerate | scrollxui | Components | Free | 3 deps | |
| Announcementannouncement | scrollxui | Components | Free | 5 deps | |
| Aspect Ratioaspect-ratio | scrollxui | Components | Free | 1 dep |
