icone animate
This commit is contained in:
parent
d303c189a9
commit
74f3e10fae
1 changed files with 138 additions and 0 deletions
138
apps/infoalloggi/src/components/animated-icons.tsx
Normal file
138
apps/infoalloggi/src/components/animated-icons.tsx
Normal file
|
|
@ -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<SVGSVGElement> {
|
||||||
|
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 (
|
||||||
|
<LazyMotion features={domAnimation}>
|
||||||
|
<m.svg
|
||||||
|
{...restProps}
|
||||||
|
className={className}
|
||||||
|
fill="none"
|
||||||
|
height={size}
|
||||||
|
onMouseEnter={() => 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"
|
||||||
|
>
|
||||||
|
<path d="M0 0h24v24H0z" fill="none" stroke="none" />
|
||||||
|
<path d="M12 6h-6a2 2 0 0 0 -2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-6" />
|
||||||
|
<m.path
|
||||||
|
animate={
|
||||||
|
shouldAnimate ? { x: [0, 2, 0], y: [0, -2, 0] } : { x: 0, y: 0 }
|
||||||
|
}
|
||||||
|
d="M11 13l9 -9"
|
||||||
|
transition={transition}
|
||||||
|
/>
|
||||||
|
<m.path
|
||||||
|
animate={
|
||||||
|
shouldAnimate ? { x: [0, 2, 0], y: [0, -2, 0] } : { x: 0, y: 0 }
|
||||||
|
}
|
||||||
|
d="M15 4h5v5"
|
||||||
|
transition={transition}
|
||||||
|
/>
|
||||||
|
</m.svg>
|
||||||
|
</LazyMotion>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<LazyMotion features={domAnimation}>
|
||||||
|
<m.svg
|
||||||
|
{...restProps}
|
||||||
|
className={className}
|
||||||
|
fill="none"
|
||||||
|
height={size}
|
||||||
|
onMouseEnter={() => 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"
|
||||||
|
>
|
||||||
|
<path d="M0 0h24v24H0z" fill="none" stroke="none" />
|
||||||
|
<m.g
|
||||||
|
{...groupAnimationProps}
|
||||||
|
style={{ originX: "21px", originY: "21px" }}
|
||||||
|
>
|
||||||
|
<m.circle cx="10" cy="10" r="7" />
|
||||||
|
<m.path d="M21 21l-6 -6" />
|
||||||
|
</m.g>
|
||||||
|
</m.svg>
|
||||||
|
</LazyMotion>
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Add table
Reference in a new issue