From 74f3e10fae24b4262c51a2a3d63ec0aa1c523651 Mon Sep 17 00:00:00 2001 From: Marco Pedone Date: Tue, 12 May 2026 16:12:40 +0200 Subject: [PATCH] icone animate --- .../src/components/animated-icons.tsx | 138 ++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 apps/infoalloggi/src/components/animated-icons.tsx diff --git a/apps/infoalloggi/src/components/animated-icons.tsx b/apps/infoalloggi/src/components/animated-icons.tsx new file mode 100644 index 0000000..cfa17c6 --- /dev/null +++ b/apps/infoalloggi/src/components/animated-icons.tsx @@ -0,0 +1,138 @@ +"use client"; + +import { + domAnimation, + type Easing, + LazyMotion, + m, + type SVGMotionProps, +} from "framer-motion"; +import { useState } from "react"; + +// https://www.chamaac.com/components/animated-icons + +interface IconProps extends SVGMotionProps { + size?: number; + duration?: number; + strokeWidth?: number; + isHovered?: boolean; + repeatDelay?: number; + ease?: Easing; +} + +export const ExternalLinkIcon = (props: IconProps) => { + const { + size = 28, + duration = 1.5, + strokeWidth = 2, + isHovered = false, + repeatDelay = 1, + ease = "easeInOut", + className, + ...restProps + } = props; + + const [isHoveredInternal, setIsHoveredInternal] = useState(false); + const shouldAnimate = isHovered ? isHoveredInternal : true; + + const transition = { + duration: duration, + ease: ease, + repeat: isHovered ? 0 : Infinity, + repeatDelay: repeatDelay, + }; + + return ( + + isHovered && setIsHoveredInternal(true)} + onMouseLeave={() => isHovered && setIsHoveredInternal(false)} + stroke="currentColor" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={strokeWidth} + viewBox="0 0 24 24" + width={size} + xmlns="http://www.w3.org/2000/svg" + > + + + + + + + ); +}; + +export const SearchIcon = (props: IconProps) => { + const { + size = 28, // Icon size in pixels + duration = 1.2, // Animation duration in seconds + strokeWidth = 2, // SVG stroke width + isHovered = false, // When true, animate only on hover + repeatDelay = 1, // Delay between animation loops (seconds) + ease = "easeInOut", // Animation easing function + className, + ...restProps + } = props; + + const [isHoveredInternal, setIsHoveredInternal] = useState(false); + + const shouldAnimate = isHovered ? isHoveredInternal : true; + + const groupAnimationProps = { + animate: shouldAnimate ? { rotate: [0, -25, 15, 0] } : { rotate: 0 }, + transition: { + duration: duration, + ease: ease, + repeat: isHovered ? 0 : Infinity, + repeatDelay: repeatDelay, + }, + }; + + return ( + + isHovered && setIsHoveredInternal(true)} + onMouseLeave={() => isHovered && setIsHoveredInternal(false)} + overflow="visible" + stroke="currentColor" + strokeLinecap="round" + strokeLinejoin="round" + strokeWidth={strokeWidth} + viewBox="0 0 24 24" + width={size} + xmlns="http://www.w3.org/2000/svg" + > + + + + + + + + ); +};