diff --git a/src/content/Animations/TargetCursor/TargetCursor.jsx b/src/content/Animations/TargetCursor/TargetCursor.jsx
index 05ce3eb37..f9b8d63d5 100644
--- a/src/content/Animations/TargetCursor/TargetCursor.jsx
+++ b/src/content/Animations/TargetCursor/TargetCursor.jsx
@@ -37,7 +37,9 @@ const TargetCursor = ({
spinDuration = 2,
hideDefaultCursor = true,
hoverDuration = 0.2,
- parallaxOn = true
+ parallaxOn = true,
+ cursorColor = '#ffffff',
+ cursorColorOnTarget
}) => {
const cursorRef = useRef(null);
const cornersRef = useRef(null);
@@ -217,12 +219,27 @@ const TargetCursor = ({
activeTarget = target;
const corners = Array.from(cornersRef.current);
- corners.forEach(corner => gsap.killTweensOf(corner));
+ corners.forEach(corner => gsap.killTweensOf(corner, 'x,y'));
gsap.killTweensOf(cursorRef.current, 'rotation');
spinTl.current?.pause();
gsap.set(cursorRef.current, { rotation: 0 });
+ if (cursorColorOnTarget) {
+ gsap.to(corners, {
+ borderColor: cursorColorOnTarget,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ if (dotRef.current) {
+ gsap.to(dotRef.current, {
+ backgroundColor: cursorColorOnTarget,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ }
+ }
+
const rect = target.getBoundingClientRect();
const { borderWidth, cornerSize } = constants;
const { x: offsetX, y: offsetY } = getOffset();
@@ -262,9 +279,24 @@ const TargetCursor = ({
gsap.set(activeStrengthRef, { current: 0, overwrite: true });
activeTarget = null;
+ if (cursorColorOnTarget && cornersRef.current) {
+ gsap.to(Array.from(cornersRef.current), {
+ borderColor: cursorColor,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ if (dotRef.current) {
+ gsap.to(dotRef.current, {
+ backgroundColor: cursorColor,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ }
+ }
+
if (cornersRef.current) {
const corners = Array.from(cornersRef.current);
- gsap.killTweensOf(corners);
+ gsap.killTweensOf(corners, 'x,y');
const { cornerSize } = constants;
const positions = [
{ x: -cornerSize * 1.5, y: -cornerSize * 1.5 },
@@ -344,7 +376,18 @@ const TargetCursor = ({
targetCornerPositionsRef.current = null;
activeStrengthRef.current = 0;
};
- }, [targetSelector, spinDuration, moveCursor, constants, hideDefaultCursor, isMobile, hoverDuration, parallaxOn]);
+ }, [
+ targetSelector,
+ spinDuration,
+ moveCursor,
+ constants,
+ hideDefaultCursor,
+ isMobile,
+ hoverDuration,
+ parallaxOn,
+ cursorColor,
+ cursorColorOnTarget
+ ]);
useEffect(() => {
if (isMobile || !cursorRef.current || !spinTl.current) return;
@@ -362,13 +405,13 @@ const TargetCursor = ({
return (
);
};
-export default TargetCursor;
+export default TargetCursor;
\ No newline at end of file
diff --git a/src/demo/Animations/TargetCursorDemo.jsx b/src/demo/Animations/TargetCursorDemo.jsx
index 6c54b800c..835ba4901 100644
--- a/src/demo/Animations/TargetCursorDemo.jsx
+++ b/src/demo/Animations/TargetCursorDemo.jsx
@@ -11,6 +11,7 @@ import PropTable from '../../components/common/Preview/PropTable';
import Dependencies from '../../components/code/Dependencies';
import PreviewSlider from '../../components/common/Preview/PreviewSlider';
import PreviewSwitch from '../../components/common/Preview/PreviewSwitch';
+import PreviewColorPickerCustom from '../../components/common/Preview/PreviewColorPickerCustom';
import TargetCursor from '../../content/Animations/TargetCursor/TargetCursor';
import { targetCursor } from '../../constants/code/Animations/targetCursorCode';
@@ -19,12 +20,14 @@ const DEFAULT_PROPS = {
spinDuration: 2,
hideDefaultCursor: true,
hoverDuration: 0.2,
- parallaxOn: true
+ parallaxOn: true,
+ cursorColor: '#ffffff',
+ cursorColorOnTarget: '#B497CF'
};
const TargetCursorDemo = () => {
const { props, updateProp, resetProps, hasChanges } = useComponentProps(DEFAULT_PROPS);
- const { spinDuration, hideDefaultCursor, hoverDuration, parallaxOn } = props;
+ const { spinDuration, hideDefaultCursor, hoverDuration, parallaxOn, cursorColor, cursorColorOnTarget } = props;
const propData = useMemo(
() => [
@@ -57,6 +60,18 @@ const TargetCursorDemo = () => {
type: 'boolean',
default: 'true',
description: 'Enables a subtle parallax effect on the corners when moving over a target'
+ },
+ {
+ name: 'cursorColor',
+ type: 'string',
+ default: "'#ffffff'",
+ description: 'Color of the cursor dot and corner brackets at rest'
+ },
+ {
+ name: 'cursorColorOnTarget',
+ type: 'string',
+ default: 'undefined',
+ description: 'Optional color the cursor smoothly transitions to while locked onto a target'
}
],
[]
@@ -172,6 +187,16 @@ const TargetCursorDemo = () => {
isChecked={parallaxOn}
onChange={val => updateProp('parallaxOn', val)}
/>
+ updateProp('cursorColor', val)}
+ />
+ updateProp('cursorColorOnTarget', val)}
+ />
@@ -189,9 +214,11 @@ const TargetCursorDemo = () => {
hideDefaultCursor={hideDefaultCursor}
hoverDuration={hoverDuration}
parallaxOn={parallaxOn}
+ cursorColor={cursorColor}
+ cursorColorOnTarget={cursorColorOnTarget}
/>
>
);
};
-export default TargetCursorDemo;
+export default TargetCursorDemo;
\ No newline at end of file
diff --git a/src/tailwind/Animations/TargetCursor/TargetCursor.jsx b/src/tailwind/Animations/TargetCursor/TargetCursor.jsx
index e3a89e2a9..aa689a819 100644
--- a/src/tailwind/Animations/TargetCursor/TargetCursor.jsx
+++ b/src/tailwind/Animations/TargetCursor/TargetCursor.jsx
@@ -36,7 +36,9 @@ const TargetCursor = ({
spinDuration = 2,
hideDefaultCursor = true,
hoverDuration = 0.2,
- parallaxOn = true
+ parallaxOn = true,
+ cursorColor = '#ffffff',
+ cursorColorOnTarget
}) => {
const cursorRef = useRef(null);
const cornersRef = useRef(null);
@@ -209,11 +211,26 @@ const TargetCursor = ({
activeTarget = target;
const corners = Array.from(cornersRef.current);
- corners.forEach(corner => gsap.killTweensOf(corner));
+ corners.forEach(corner => gsap.killTweensOf(corner, 'x,y'));
gsap.killTweensOf(cursorRef.current, 'rotation');
spinTl.current?.pause();
gsap.set(cursorRef.current, { rotation: 0 });
+ if (cursorColorOnTarget) {
+ gsap.to(corners, {
+ borderColor: cursorColorOnTarget,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ if (dotRef.current) {
+ gsap.to(dotRef.current, {
+ backgroundColor: cursorColorOnTarget,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ }
+ }
+
const rect = target.getBoundingClientRect();
const { borderWidth, cornerSize } = constants;
const { x: offsetX, y: offsetY } = getOffset();
@@ -247,9 +264,25 @@ const TargetCursor = ({
targetCornerPositionsRef.current = null;
gsap.set(activeStrengthRef, { current: 0, overwrite: true });
activeTarget = null;
+
+ if (cursorColorOnTarget && cornersRef.current) {
+ gsap.to(Array.from(cornersRef.current), {
+ borderColor: cursorColor,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ if (dotRef.current) {
+ gsap.to(dotRef.current, {
+ backgroundColor: cursorColor,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ }
+ }
+
if (cornersRef.current) {
const corners = Array.from(cornersRef.current);
- gsap.killTweensOf(corners);
+ gsap.killTweensOf(corners, 'x,y');
const { cornerSize } = constants;
const positions = [
{ x: -cornerSize * 1.5, y: -cornerSize * 1.5 },
@@ -313,7 +346,18 @@ const TargetCursor = ({
targetCornerPositionsRef.current = null;
activeStrengthRef.current = 0;
};
- }, [targetSelector, spinDuration, moveCursor, constants, hideDefaultCursor, isMobile, hoverDuration, parallaxOn]);
+ }, [
+ targetSelector,
+ spinDuration,
+ moveCursor,
+ constants,
+ hideDefaultCursor,
+ isMobile,
+ hoverDuration,
+ parallaxOn,
+ cursorColor,
+ cursorColorOnTarget
+ ]);
useEffect(() => {
if (isMobile || !cursorRef.current || !spinTl.current) return;
@@ -337,27 +381,27 @@ const TargetCursor = ({
>
);
};
-export default TargetCursor;
+export default TargetCursor;
\ No newline at end of file
diff --git a/src/ts-default/Animations/TargetCursor/TargetCursor.tsx b/src/ts-default/Animations/TargetCursor/TargetCursor.tsx
index 1c1761bb6..ed804950c 100644
--- a/src/ts-default/Animations/TargetCursor/TargetCursor.tsx
+++ b/src/ts-default/Animations/TargetCursor/TargetCursor.tsx
@@ -38,6 +38,8 @@ export interface TargetCursorProps {
hideDefaultCursor?: boolean;
hoverDuration?: number;
parallaxOn?: boolean;
+ cursorColor?: string;
+ cursorColorOnTarget?: string;
}
const TargetCursor: React.FC = ({
@@ -45,7 +47,9 @@ const TargetCursor: React.FC = ({
spinDuration = 2,
hideDefaultCursor = true,
hoverDuration = 0.2,
- parallaxOn = true
+ parallaxOn = true,
+ cursorColor = '#ffffff',
+ cursorColorOnTarget
}) => {
const cursorRef = useRef(null);
const cornersRef = useRef | null>(null);
@@ -68,12 +72,23 @@ const TargetCursor: React.FC = ({
return (hasTouchScreen && isSmallScreen) || isMobileUserAgent;
}, []);
- const constants = useMemo(() => ({ borderWidth: 3, cornerSize: 12 }), []);
+ const constants = useMemo(
+ () => ({
+ borderWidth: 3,
+ cornerSize: 12
+ }),
+ []
+ );
const moveCursor = useCallback((x: number, y: number) => {
if (!cursorRef.current) return;
const { x: offsetX, y: offsetY } = getContainingBlockOffset(containingBlockRef.current);
- gsap.to(cursorRef.current, { x: x - offsetX, y: y - offsetY, duration: 0.1, ease: 'power3.out' });
+ gsap.to(cursorRef.current, {
+ x: x - offsetX,
+ y: y - offsetY,
+ duration: 0.1,
+ ease: 'power3.out'
+ });
}, []);
useEffect(() => {
@@ -124,19 +139,26 @@ const TargetCursor: React.FC = ({
if (!targetCornerPositionsRef.current || !cursorRef.current || !cornersRef.current) {
return;
}
+
const strength = activeStrengthRef.current.current;
if (strength === 0) return;
+
const cursorX = gsap.getProperty(cursorRef.current, 'x') as number;
const cursorY = gsap.getProperty(cursorRef.current, 'y') as number;
+
const corners = Array.from(cornersRef.current);
corners.forEach((corner, i) => {
const currentX = gsap.getProperty(corner, 'x') as number;
const currentY = gsap.getProperty(corner, 'y') as number;
+
const targetX = targetCornerPositionsRef.current![i].x - cursorX;
const targetY = targetCornerPositionsRef.current![i].y - cursorY;
+
const finalX = currentX + (targetX - currentX) * strength;
const finalY = currentY + (targetY - currentY) * strength;
+
const duration = strength >= 0.99 ? (parallaxOn ? 0.2 : 0) : 0.05;
+
gsap.to(corner, {
x: finalX,
y: finalY,
@@ -205,11 +227,27 @@ const TargetCursor: React.FC = ({
activeTarget = target;
const corners = Array.from(cornersRef.current);
- corners.forEach(corner => gsap.killTweensOf(corner));
+ corners.forEach(corner => gsap.killTweensOf(corner, 'x,y'));
+
gsap.killTweensOf(cursorRef.current, 'rotation');
spinTl.current?.pause();
gsap.set(cursorRef.current, { rotation: 0 });
+ if (cursorColorOnTarget) {
+ gsap.to(corners, {
+ borderColor: cursorColorOnTarget,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ if (dotRef.current) {
+ gsap.to(dotRef.current, {
+ backgroundColor: cursorColorOnTarget,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ }
+ }
+
const rect = target.getBoundingClientRect();
const { borderWidth, cornerSize } = constants;
const { x: offsetX, y: offsetY } = getOffset();
@@ -226,7 +264,11 @@ const TargetCursor: React.FC = ({
isActiveRef.current = true;
gsap.ticker.add(tickerFnRef.current!);
- gsap.to(activeStrengthRef.current, { current: 1, duration: hoverDuration, ease: 'power2.out' });
+ gsap.to(activeStrengthRef.current, {
+ current: 1,
+ duration: hoverDuration,
+ ease: 'power2.out'
+ });
corners.forEach((corner, i) => {
gsap.to(corner, {
@@ -239,13 +281,30 @@ const TargetCursor: React.FC = ({
const leaveHandler = () => {
gsap.ticker.remove(tickerFnRef.current!);
+
isActiveRef.current = false;
targetCornerPositionsRef.current = null;
gsap.set(activeStrengthRef.current, { current: 0, overwrite: true });
activeTarget = null;
+
+ if (cursorColorOnTarget && cornersRef.current) {
+ gsap.to(Array.from(cornersRef.current), {
+ borderColor: cursorColor,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ if (dotRef.current) {
+ gsap.to(dotRef.current, {
+ backgroundColor: cursorColor,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ }
+ }
+
if (cornersRef.current) {
const corners = Array.from(cornersRef.current);
- gsap.killTweensOf(corners);
+ gsap.killTweensOf(corners, 'x,y');
const { cornerSize } = constants;
const positions = [
{ x: -cornerSize * 1.5, y: -cornerSize * 1.5 },
@@ -255,9 +314,19 @@ const TargetCursor: React.FC = ({
];
const tl = gsap.timeline();
corners.forEach((corner, index) => {
- tl.to(corner, { x: positions[index].x, y: positions[index].y, duration: 0.3, ease: 'power3.out' }, 0);
+ tl.to(
+ corner,
+ {
+ x: positions[index].x,
+ y: positions[index].y,
+ duration: 0.3,
+ ease: 'power3.out'
+ },
+ 0
+ );
});
}
+
resumeTimeout = setTimeout(() => {
if (!activeTarget && cursorRef.current && spinTl.current) {
const currentRotation = gsap.getProperty(cursorRef.current, 'rotation') as number;
@@ -277,13 +346,15 @@ const TargetCursor: React.FC = ({
}
resumeTimeout = null;
}, 50);
+
cleanupTarget(target);
};
+
currentLeaveHandler = leaveHandler;
target.addEventListener('mouseleave', leaveHandler);
};
- window.addEventListener('mouseover', enterHandler as EventListener);
+ window.addEventListener('mouseover', enterHandler as EventListener, { passive: true });
const resizeHandler = () => {
containingBlockRef.current = getContainingBlock(cursor);
@@ -294,22 +365,37 @@ const TargetCursor: React.FC = ({
if (tickerFnRef.current) {
gsap.ticker.remove(tickerFnRef.current);
}
+
window.removeEventListener('mousemove', moveHandler);
window.removeEventListener('mouseover', enterHandler as EventListener);
window.removeEventListener('scroll', scrollHandler);
window.removeEventListener('resize', resizeHandler);
window.removeEventListener('mousedown', mouseDownHandler);
window.removeEventListener('mouseup', mouseUpHandler);
+
if (activeTarget) {
cleanupTarget(activeTarget);
}
+
spinTl.current?.kill();
document.body.style.cursor = originalCursor;
+
isActiveRef.current = false;
targetCornerPositionsRef.current = null;
activeStrengthRef.current.current = 0;
};
- }, [targetSelector, spinDuration, moveCursor, constants, hideDefaultCursor, isMobile, hoverDuration, parallaxOn]);
+ }, [
+ targetSelector,
+ spinDuration,
+ moveCursor,
+ constants,
+ hideDefaultCursor,
+ isMobile,
+ hoverDuration,
+ parallaxOn,
+ cursorColor,
+ cursorColorOnTarget
+ ]);
useEffect(() => {
if (isMobile || !cursorRef.current || !spinTl.current) return;
@@ -327,13 +413,13 @@ const TargetCursor: React.FC = ({
return (
);
};
-export default TargetCursor;
+export default TargetCursor;
\ No newline at end of file
diff --git a/src/ts-tailwind/Animations/TargetCursor/TargetCursor.tsx b/src/ts-tailwind/Animations/TargetCursor/TargetCursor.tsx
index f9a2c4461..f2363023c 100644
--- a/src/ts-tailwind/Animations/TargetCursor/TargetCursor.tsx
+++ b/src/ts-tailwind/Animations/TargetCursor/TargetCursor.tsx
@@ -37,6 +37,8 @@ export interface TargetCursorProps {
hideDefaultCursor?: boolean;
hoverDuration?: number;
parallaxOn?: boolean;
+ cursorColor?: string;
+ cursorColorOnTarget?: string;
}
const TargetCursor: React.FC = ({
@@ -44,7 +46,9 @@ const TargetCursor: React.FC = ({
spinDuration = 2,
hideDefaultCursor = true,
hoverDuration = 0.2,
- parallaxOn = true
+ parallaxOn = true,
+ cursorColor = '#ffffff',
+ cursorColorOnTarget
}) => {
const cursorRef = useRef(null);
const cornersRef = useRef | null>(null);
@@ -204,11 +208,26 @@ const TargetCursor: React.FC = ({
activeTarget = target;
const corners = Array.from(cornersRef.current);
- corners.forEach(corner => gsap.killTweensOf(corner));
+ corners.forEach(corner => gsap.killTweensOf(corner, 'x,y'));
gsap.killTweensOf(cursorRef.current, 'rotation');
spinTl.current?.pause();
gsap.set(cursorRef.current, { rotation: 0 });
+ if (cursorColorOnTarget) {
+ gsap.to(corners, {
+ borderColor: cursorColorOnTarget,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ if (dotRef.current) {
+ gsap.to(dotRef.current, {
+ backgroundColor: cursorColorOnTarget,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ }
+ }
+
const rect = target.getBoundingClientRect();
const { borderWidth, cornerSize } = constants;
const { x: offsetX, y: offsetY } = getOffset();
@@ -242,9 +261,25 @@ const TargetCursor: React.FC = ({
targetCornerPositionsRef.current = null;
gsap.set(activeStrengthRef.current, { current: 0, overwrite: true });
activeTarget = null;
+
+ if (cursorColorOnTarget && cornersRef.current) {
+ gsap.to(Array.from(cornersRef.current), {
+ borderColor: cursorColor,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ if (dotRef.current) {
+ gsap.to(dotRef.current, {
+ backgroundColor: cursorColor,
+ duration: 0.15,
+ ease: 'power2.out'
+ });
+ }
+ }
+
if (cornersRef.current) {
const corners = Array.from(cornersRef.current);
- gsap.killTweensOf(corners);
+ gsap.killTweensOf(corners, 'x,y');
const { cornerSize } = constants;
const positions = [
{ x: -cornerSize * 1.5, y: -cornerSize * 1.5 },
@@ -308,7 +343,18 @@ const TargetCursor: React.FC = ({
targetCornerPositionsRef.current = null;
activeStrengthRef.current.current = 0;
};
- }, [targetSelector, spinDuration, moveCursor, constants, hideDefaultCursor, isMobile, hoverDuration, parallaxOn]);
+ }, [
+ targetSelector,
+ spinDuration,
+ moveCursor,
+ constants,
+ hideDefaultCursor,
+ isMobile,
+ hoverDuration,
+ parallaxOn,
+ cursorColor,
+ cursorColorOnTarget
+ ]);
useEffect(() => {
if (isMobile || !cursorRef.current || !spinTl.current) return;
@@ -332,27 +378,27 @@ const TargetCursor: React.FC = ({
>
);
};
-export default TargetCursor;
+export default TargetCursor;
\ No newline at end of file