rocket-cursor-component 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Tomáš Dinh
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # RocketCursor
2
+ A customizable React component that replaces the mouse cursor with an animated rocket that rotates based on movement and displays a flame effect when in motion.
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ interface RocketCursorProps {
3
+ size?: number;
4
+ threshold?: number;
5
+ isVisible?: boolean;
6
+ }
7
+ declare const RocketCursor: React.FC<RocketCursorProps>;
8
+ export default RocketCursor;
@@ -0,0 +1,83 @@
1
+ import { useState, useEffect, useCallback, useRef } from "react";
2
+ import React from "react";
3
+ const RocketCursor = ({ size = 50, threshold = 10, isVisible = true, }) => {
4
+ // Track cursor position
5
+ const [position, setPosition] = useState({ x: 0, y: 0 });
6
+ // Track rocket's rotation angle
7
+ const [angle, setAngle] = useState(0);
8
+ // Track if the cursor is moving
9
+ const [isMoving, setIsMoving] = useState(true);
10
+ // Store last significant cursor position to calculate movement
11
+ const lastSignificantPosition = useRef({ x: 0, y: 0 });
12
+ const lastMoveTimestamp = useRef(Date.now());
13
+ // Update the position and angle when the mouse moves
14
+ const handleMouseMove = useCallback((e) => {
15
+ const currentPosition = { x: e.clientX, y: e.clientY };
16
+ setPosition(currentPosition);
17
+ // Calculate distance moved to decide if the rocket should rotate
18
+ const dx = currentPosition.x - lastSignificantPosition.current.x;
19
+ const dy = currentPosition.y - lastSignificantPosition.current.y;
20
+ const distance = Math.sqrt(dx * dx + dy * dy);
21
+ // Rotate if the movement exceeds the threshold
22
+ if (distance > threshold) {
23
+ const newAngle = Math.atan2(dy, dx) * (180 / Math.PI) + 45;
24
+ setAngle(newAngle);
25
+ lastSignificantPosition.current = currentPosition;
26
+ }
27
+ // Mark as moving
28
+ lastMoveTimestamp.current = Date.now();
29
+ setIsMoving(true);
30
+ }, [threshold]);
31
+ // Check if the cursor has stopped moving
32
+ useEffect(() => {
33
+ const checkIfStopped = () => {
34
+ const now = Date.now();
35
+ if (now - lastMoveTimestamp.current > 300) {
36
+ setIsMoving(false);
37
+ }
38
+ };
39
+ const intervalId = setInterval(checkIfStopped, 100);
40
+ // Listen to mousemove events
41
+ window.addEventListener("mousemove", handleMouseMove);
42
+ // Cleanup event listeners on unmount
43
+ return () => {
44
+ window.removeEventListener("mousemove", handleMouseMove);
45
+ clearInterval(intervalId);
46
+ };
47
+ }, [handleMouseMove]);
48
+ // Hide the rocket if not visible
49
+ if (!isVisible) {
50
+ return null;
51
+ }
52
+ // Render the rocket SVG at the cursor position with the correct rotation
53
+ return (React.createElement("div", { style: {
54
+ position: "fixed",
55
+ left: position.x,
56
+ top: position.y,
57
+ transform: `translate(-50%, -50%) rotate(${angle}deg)`,
58
+ pointerEvents: "none",
59
+ zIndex: 9999,
60
+ width: `${size}px`,
61
+ height: `${size * 1.5}px`,
62
+ } },
63
+ React.createElement("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 416.449 516.449", width: "100%", height: "100%" },
64
+ React.createElement("defs", null,
65
+ React.createElement("linearGradient", { id: "fireGradient", gradientUnits: "userSpaceOnUse", x1: "94.141", y1: "255", x2: "94.141", y2: "0.188" },
66
+ React.createElement("stop", { offset: "0", stopColor: "#ff4c0d" }),
67
+ React.createElement("stop", { offset: "1", stopColor: "#fc9502" }))),
68
+ isMoving && (React.createElement("g", { transform: "translate(1.2245, 350.449) rotate(45) scale(0.5, -0.5)" },
69
+ React.createElement("path", { d: "M187.899,164.809 C185.803,214.868 144.574,254.812 94.000,254.812 C42.085,254.812 -0.000,211.312 -0.000,160.812 C-0.000,154.062 -0.121,140.572 10.000,117.812 C16.057,104.191 19.856,95.634 22.000,87.812 C23.178,83.513 25.469,76.683 32.000,87.812 C35.851,94.374 36.000,103.812 36.000,103.812 C36.000,103.812 50.328,92.817 60.000,71.812 C74.179,41.019 62.866,22.612 59.000,9.812 C57.662,5.384 56.822,-2.574 66.000,0.812 C75.352,4.263 100.076,21.570 113.000,39.812 C131.445,65.847 138.000,90.812 138.000,90.812 C138.000,90.812 143.906,83.482 146.000,75.812 C148.365,67.151 148.400,58.573 155.999,67.813 C163.226,76.600 173.959,93.113 180.000,108.812 C190.969,137.321 187.899,164.809 187.899,164.809 Z", fill: "url(#fireGradient)", fillRule: "evenodd" }),
70
+ React.createElement("path", { d: "M94.000,254.812 C58.101,254.812 29.000,225.711 29.000,189.812 C29.000,168.151 37.729,155.000 55.896,137.166 C67.528,125.747 78.415,111.722 83.042,102.172 C83.953,100.292 86.026,90.495 94.019,101.966 C98.212,107.982 104.785,118.681 109.000,127.812 C116.266,143.555 118.000,158.812 118.000,158.812 C118.000,158.812 125.121,154.616 130.000,143.812 C131.573,140.330 134.753,127.148 143.643,140.328 C150.166,150.000 159.127,167.390 159.000,189.812 C159.000,225.711 129.898,254.812 94.000,254.812 Z", fill: "#fc9502", fillRule: "evenodd" }),
71
+ React.createElement("path", { d: "M95.000,183.812 C104.250,183.812 104.250,200.941 116.000,223.812 C123.824,239.041 112.121,254.812 95.000,254.812 C77.879,254.812 69.000,240.933 69.000,223.812 C69.000,206.692 85.750,183.812 95.000,183.812 Z", fill: "#fce202", fillRule: "evenodd" }))),
72
+ React.createElement("g", { transform: "translate(0, 0)" },
73
+ React.createElement("path", { style: { fill: "#FF7124" }, d: "M399.76,16.699c10.12,37.84,8.67,78.13-4.34,115.28h-0.01L284.48,21.049v-0.01 C321.63,8.029,361.92,6.579,399.76,16.699z" }),
74
+ React.createElement("path", { style: { fill: "#F2D59F" }, d: "M90.21,207.929l87.14-101.42h0.01l33.71-39.24c21.43-21.43,46.6-36.84,73.41-46.23v0.01 l110.93,110.93h0.01c-9.39,26.81-24.8,51.98-46.23,73.41l-39.24,33.71l-101.43,87.14l-29.57-29.57l-29.58-29.58l-29.58-29.58 L90.21,207.929z M296.11,193.399c20.18-20.17,20.18-52.89,0-73.06c-20.17-20.18-52.89-20.18-73.06,0 c-20.18,20.17-20.18,52.89,0,73.06C243.22,213.579,275.94,213.579,296.11,193.399z" }),
75
+ React.createElement("path", { style: { fill: "#F2D59F" }, d: "M309.95,239.099c1.74,45.6-14.8,91.78-49.61,126.59c-10.69,10.68-22.44,19.65-34.93,26.89 l-16.89-66.34L309.95,239.099z" }),
76
+ React.createElement("path", { style: { fill: "#8ECAC1" }, d: "M296.11,120.339c20.18,20.17,20.18,52.89,0,73.06c-20.17,20.18-52.89,20.18-73.06,0 c-20.18-20.17-20.18-52.89,0-73.06C243.22,100.159,275.94,100.159,296.11,120.339z" }),
77
+ React.createElement("path", { style: { fill: "#E6B263" }, d: "M208.52,326.239l-39.94,14.71c-10.98,4.05-23.31,1.34-31.58-6.94l-6.85-6.85l48.8-30.49 L208.52,326.239z" }),
78
+ React.createElement("polygon", { style: { fill: "#E6B263" }, points: "178.95,296.669 130.15,327.159 130.14,327.159 109.72,306.739 149.37,267.089" }),
79
+ React.createElement("path", { style: { fill: "#F2D59F" }, d: "M177.35,106.509l-87.14,101.42l-66.33-16.88c7.24-12.49,16.21-24.24,26.89-34.93 C85.58,121.309,131.74,104.769,177.35,106.509z" }),
80
+ React.createElement("polygon", { style: { fill: "#E6B263" }, points: "149.37,267.089 109.72,306.739 89.3,286.309 119.79,237.509" }),
81
+ React.createElement("path", { style: { fill: "#E6B263" }, d: "M119.79,237.509l-30.49,48.8l-6.86-6.85c-8.27-8.28-10.98-20.6-6.94-31.58l14.71-39.95 L119.79,237.509z" })))));
82
+ };
83
+ export default RocketCursor;
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "rocket-cursor-component",
3
+ "version": "1.0.0",
4
+ "description": "A customizable React component that replaces the cursor with an animated rocket.",
5
+ "main": "dist/rocket.Cursor.js",
6
+ "types": "dist/rocket.Cursor.d.ts",
7
+ "scripts": {
8
+ "build": "tsc"
9
+ },
10
+ "author": "Your Name",
11
+ "license": "MIT",
12
+ "peerDependencies": {
13
+ "react": "^17.0.0"
14
+ },
15
+ "devDependencies": {
16
+ "typescript": "^4.0.0",
17
+ "react": "^17.0.0"
18
+ }
19
+ }
@@ -0,0 +1,175 @@
1
+ import { useState, useEffect, useCallback, useRef } from "react";
2
+ import React from "react";
3
+
4
+ // Props interface for customizing the size, rotation sensitivity, and visibility of the rocket cursor
5
+ interface RocketCursorProps {
6
+ size?: number; // Rocket size (default is 50)
7
+ threshold?: number; // Threshold for rotating the rocket (default is 10)
8
+ isVisible?: boolean; // Control visibility of the rocket (default is true)
9
+ }
10
+
11
+ const RocketCursor: React.FC<RocketCursorProps> = ({
12
+ size = 50,
13
+ threshold = 10,
14
+ isVisible = true,
15
+ }) => {
16
+ // Track cursor position
17
+ const [position, setPosition] = useState({ x: 0, y: 0 });
18
+ // Track rocket's rotation angle
19
+ const [angle, setAngle] = useState(0);
20
+ // Track if the cursor is moving
21
+ const [isMoving, setIsMoving] = useState(true);
22
+
23
+ // Store last significant cursor position to calculate movement
24
+ const lastSignificantPosition = useRef({ x: 0, y: 0 });
25
+ const lastMoveTimestamp = useRef(Date.now());
26
+
27
+ // Update the position and angle when the mouse moves
28
+ const handleMouseMove = useCallback(
29
+ (e: { clientX: any; clientY: any }) => {
30
+ const currentPosition = { x: e.clientX, y: e.clientY };
31
+ setPosition(currentPosition);
32
+
33
+ // Calculate distance moved to decide if the rocket should rotate
34
+ const dx = currentPosition.x - lastSignificantPosition.current.x;
35
+ const dy = currentPosition.y - lastSignificantPosition.current.y;
36
+ const distance = Math.sqrt(dx * dx + dy * dy);
37
+
38
+ // Rotate if the movement exceeds the threshold
39
+ if (distance > threshold) {
40
+ const newAngle = Math.atan2(dy, dx) * (180 / Math.PI) + 45;
41
+ setAngle(newAngle);
42
+ lastSignificantPosition.current = currentPosition;
43
+ }
44
+
45
+ // Mark as moving
46
+ lastMoveTimestamp.current = Date.now();
47
+ setIsMoving(true);
48
+ },
49
+ [threshold]
50
+ );
51
+
52
+ // Check if the cursor has stopped moving
53
+ useEffect(() => {
54
+ const checkIfStopped = () => {
55
+ const now = Date.now();
56
+ if (now - lastMoveTimestamp.current > 300) {
57
+ setIsMoving(false);
58
+ }
59
+ };
60
+
61
+ const intervalId = setInterval(checkIfStopped, 100);
62
+
63
+ // Listen to mousemove events
64
+ window.addEventListener("mousemove", handleMouseMove);
65
+
66
+ // Cleanup event listeners on unmount
67
+ return () => {
68
+ window.removeEventListener("mousemove", handleMouseMove);
69
+ clearInterval(intervalId);
70
+ };
71
+ }, [handleMouseMove]);
72
+
73
+ // Hide the rocket if not visible
74
+ if (!isVisible) {
75
+ return null;
76
+ }
77
+
78
+ // Render the rocket SVG at the cursor position with the correct rotation
79
+ return (
80
+ <div
81
+ style={{
82
+ position: "fixed",
83
+ left: position.x,
84
+ top: position.y,
85
+ transform: `translate(-50%, -50%) rotate(${angle}deg)`,
86
+ pointerEvents: "none",
87
+ zIndex: 9999,
88
+ width: `${size}px`,
89
+ height: `${size * 1.5}px`,
90
+ }}
91
+ >
92
+ <svg
93
+ xmlns="http://www.w3.org/2000/svg"
94
+ viewBox="0 0 416.449 516.449"
95
+ width="100%"
96
+ height="100%"
97
+ >
98
+ <defs>
99
+ <linearGradient
100
+ id="fireGradient"
101
+ gradientUnits="userSpaceOnUse"
102
+ x1="94.141"
103
+ y1="255"
104
+ x2="94.141"
105
+ y2="0.188"
106
+ >
107
+ <stop offset="0" stopColor="#ff4c0d" />
108
+ <stop offset="1" stopColor="#fc9502" />
109
+ </linearGradient>
110
+ </defs>
111
+
112
+ {isMoving && (
113
+ <g transform="translate(1.2245, 350.449) rotate(45) scale(0.5, -0.5)">
114
+ <path
115
+ d="M187.899,164.809 C185.803,214.868 144.574,254.812 94.000,254.812 C42.085,254.812 -0.000,211.312 -0.000,160.812 C-0.000,154.062 -0.121,140.572 10.000,117.812 C16.057,104.191 19.856,95.634 22.000,87.812 C23.178,83.513 25.469,76.683 32.000,87.812 C35.851,94.374 36.000,103.812 36.000,103.812 C36.000,103.812 50.328,92.817 60.000,71.812 C74.179,41.019 62.866,22.612 59.000,9.812 C57.662,5.384 56.822,-2.574 66.000,0.812 C75.352,4.263 100.076,21.570 113.000,39.812 C131.445,65.847 138.000,90.812 138.000,90.812 C138.000,90.812 143.906,83.482 146.000,75.812 C148.365,67.151 148.400,58.573 155.999,67.813 C163.226,76.600 173.959,93.113 180.000,108.812 C190.969,137.321 187.899,164.809 187.899,164.809 Z"
116
+ fill="url(#fireGradient)"
117
+ fillRule="evenodd"
118
+ />
119
+ <path
120
+ d="M94.000,254.812 C58.101,254.812 29.000,225.711 29.000,189.812 C29.000,168.151 37.729,155.000 55.896,137.166 C67.528,125.747 78.415,111.722 83.042,102.172 C83.953,100.292 86.026,90.495 94.019,101.966 C98.212,107.982 104.785,118.681 109.000,127.812 C116.266,143.555 118.000,158.812 118.000,158.812 C118.000,158.812 125.121,154.616 130.000,143.812 C131.573,140.330 134.753,127.148 143.643,140.328 C150.166,150.000 159.127,167.390 159.000,189.812 C159.000,225.711 129.898,254.812 94.000,254.812 Z"
121
+ fill="#fc9502"
122
+ fillRule="evenodd"
123
+ />
124
+ <path
125
+ d="M95.000,183.812 C104.250,183.812 104.250,200.941 116.000,223.812 C123.824,239.041 112.121,254.812 95.000,254.812 C77.879,254.812 69.000,240.933 69.000,223.812 C69.000,206.692 85.750,183.812 95.000,183.812 Z"
126
+ fill="#fce202"
127
+ fillRule="evenodd"
128
+ />
129
+ </g>
130
+ )}
131
+
132
+ <g transform="translate(0, 0)">
133
+ <path
134
+ style={{ fill: "#FF7124" }}
135
+ d="M399.76,16.699c10.12,37.84,8.67,78.13-4.34,115.28h-0.01L284.48,21.049v-0.01 C321.63,8.029,361.92,6.579,399.76,16.699z"
136
+ />
137
+ <path
138
+ style={{ fill: "#F2D59F" }}
139
+ d="M90.21,207.929l87.14-101.42h0.01l33.71-39.24c21.43-21.43,46.6-36.84,73.41-46.23v0.01 l110.93,110.93h0.01c-9.39,26.81-24.8,51.98-46.23,73.41l-39.24,33.71l-101.43,87.14l-29.57-29.57l-29.58-29.58l-29.58-29.58 L90.21,207.929z M296.11,193.399c20.18-20.17,20.18-52.89,0-73.06c-20.17-20.18-52.89-20.18-73.06,0 c-20.18,20.17-20.18,52.89,0,73.06C243.22,213.579,275.94,213.579,296.11,193.399z"
140
+ />
141
+ <path
142
+ style={{ fill: "#F2D59F" }}
143
+ d="M309.95,239.099c1.74,45.6-14.8,91.78-49.61,126.59c-10.69,10.68-22.44,19.65-34.93,26.89 l-16.89-66.34L309.95,239.099z"
144
+ />
145
+ <path
146
+ style={{ fill: "#8ECAC1" }}
147
+ d="M296.11,120.339c20.18,20.17,20.18,52.89,0,73.06c-20.17,20.18-52.89,20.18-73.06,0 c-20.18-20.17-20.18-52.89,0-73.06C243.22,100.159,275.94,100.159,296.11,120.339z"
148
+ />
149
+ <path
150
+ style={{ fill: "#E6B263" }}
151
+ d="M208.52,326.239l-39.94,14.71c-10.98,4.05-23.31,1.34-31.58-6.94l-6.85-6.85l48.8-30.49 L208.52,326.239z"
152
+ />
153
+ <polygon
154
+ style={{ fill: "#E6B263" }}
155
+ points="178.95,296.669 130.15,327.159 130.14,327.159 109.72,306.739 149.37,267.089"
156
+ />
157
+ <path
158
+ style={{ fill: "#F2D59F" }}
159
+ d="M177.35,106.509l-87.14,101.42l-66.33-16.88c7.24-12.49,16.21-24.24,26.89-34.93 C85.58,121.309,131.74,104.769,177.35,106.509z"
160
+ />
161
+ <polygon
162
+ style={{ fill: "#E6B263" }}
163
+ points="149.37,267.089 109.72,306.739 89.3,286.309 119.79,237.509"
164
+ />
165
+ <path
166
+ style={{ fill: "#E6B263" }}
167
+ d="M119.79,237.509l-30.49,48.8l-6.86-6.85c-8.27-8.28-10.98-20.6-6.94-31.58l14.71-39.95 L119.79,237.509z"
168
+ />
169
+ </g>
170
+ </svg>
171
+ </div>
172
+ );
173
+ };
174
+
175
+ export default RocketCursor;
package/tsconfig.json ADDED
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES6", // Target ES6 or later for modern JavaScript compatibility
4
+ "module": "ES6", // Use ES6 modules
5
+ "jsx": "react", // Allow JSX in .tsx files
6
+ "declaration": true, // Generate .d.ts files for TypeScript consumers
7
+ "outDir": "./dist", // Output directory for compiled files
8
+ "strict": true, // Enable strict type checking
9
+ "moduleResolution": "node", // Resolve modules using Node.js style
10
+ "esModuleInterop": true, // Compatibility with CommonJS modules
11
+ "skipLibCheck": true // Skip type checking for libraries
12
+ },
13
+ "include": ["./src"], // Include all files in the src directory
14
+ "exclude": ["node_modules", "**/*.test.tsx"] // Exclude tests and node_modules
15
+ }