Neon Cursor Effect
An interactive React component that creates stunning neon glow effects around cursor with customizable colors and smooth animations.
An interactive React component that creates stunning neon glow effects around cursor with customizable colors and smooth animations.
1// @ts-nocheck2'use client';34import { useState, useEffect, useCallback } from 'react';5import { motion, useAnimation } from 'motion/react';6import './NeonCursor.css';7// Attach your `neoncursor.css` file in your project.8// If you are using React, you can import the CSS directly into your `index.css` or another relevant CSS file.9// For Next.js, add the `neoncursor.css` styles to your global CSS file (e.g., `globals.css`).1011const NeonCursor = () => {12const [position, setPosition] = useState({13x: 0,14y: 0,15scale: 1,16opacity: 1,17});18const [isClicking, setIsClicking] = useState(false);19const [isHovering, setIsHovering] = useState(false);20const trailControls = useAnimation();21const glowControls = useAnimation();2223const handleMouseMove = useCallback((e) => {24setPosition((prev) => ({25...prev,26x: e.clientX,27y: e.clientY,28}));29}, []);3031const handleMouseDown = () => setIsClicking(true);32const handleMouseUp = () => setIsClicking(false);3334const handleMouseOver = useCallback(35(e) => {36const target = e.target;37if (target.matches('a, button, input, [data-hover="true"]')) {38setIsHovering(true);39void trailControls.start({40scale: 1.5,41borderColor: 'rgb(255, 150, 50)',42borderWidth: '3px',43});44void glowControls.start({45scale: 2,46opacity: 0.8,47});48}49},50[trailControls, glowControls]51);5253const handleMouseOut = useCallback(() => {54setIsHovering(false);55void trailControls.start({56scale: 1,57borderColor: 'rgb(236, 101, 23)',58borderWidth: '2px',59});60void glowControls.start({61scale: 1,62opacity: 0.4,63});64}, [trailControls, glowControls]);6566useEffect(() => {67window.addEventListener('mousemove', handleMouseMove);68window.addEventListener('mousedown', handleMouseDown);69window.addEventListener('mouseup', handleMouseUp);70window.addEventListener('mouseover', handleMouseOver);71window.addEventListener('mouseout', handleMouseOut);7273return () => {74window.removeEventListener('mousemove', handleMouseMove);75window.removeEventListener('mousedown', handleMouseDown);76window.removeEventListener('mouseup', handleMouseUp);77window.removeEventListener('mouseover', handleMouseOver);78window.removeEventListener('mouseout', handleMouseOut);79};80}, [handleMouseMove, handleMouseOver, handleMouseOut]);8182return (83<div className='neon-cursor-container'>84{/* Main cursor dot */}85<motion.div86className='cursor-main'87animate={{88x: position.x - 10,89y: position.y - 10,90scale: isClicking ? 0.8 : isHovering ? 1.2 : 1,91}}92transition={{93type: 'spring',94damping: 20,95stiffness: 400,96mass: 0.5,97}}98/>99100{/* Trailing circle */}101<motion.div102className='cursor-trail'103animate={{104x: position.x - 20,105y: position.y - 20,106}}107transition={{108type: 'spring',109damping: 30,110stiffness: 200,111mass: 0.8,112}}113initial={false}114/>115116{/* Outer glow */}117<motion.div118className='cursor-glow'119animate={{120x: position.x - 30,121y: position.y - 30,122}}123transition={{124type: 'spring',125damping: 40,126stiffness: 150,127mass: 1,128}}129initial={false}130/>131</div>132);133};134135export default NeonCursor;136
1// @ts-nocheck23import { useEffect } from 'react';45const useCanvasCursor = () => {6function n(e) {7this.init(e || {});8}9n.prototype = {10init: function (e) {11this.phase = e.phase || 0;12this.offset = e.offset || 0;13this.frequency = e.frequency || 0.001;14this.amplitude = e.amplitude || 1;15},16update: function () {17return (18(this.phase += this.frequency),19(e = this.offset + Math.sin(this.phase) * this.amplitude)20);21},22value: function () {23return e;24},25};2627function Line(e) {28this.init(e || {});29}3031Line.prototype = {32init: function (e) {33this.spring = e.spring + 0.1 * Math.random() - 0.02;34this.friction = E.friction + 0.01 * Math.random() - 0.002;35this.nodes = [];36for (var t, n = 0; n < E.size; n++) {37t = new Node();38t.x = pos.x;39t.y = pos.y;40this.nodes.push(t);41}42},43update: function () {44var e = this.spring,45t = this.nodes[0];46t.vx += (pos.x - t.x) * e;47t.vy += (pos.y - t.y) * e;48for (var n, i = 0, a = this.nodes.length; i < a; i++)49((t = this.nodes[i]),500 < i &&51((n = this.nodes[i - 1]),52(t.vx += (n.x - t.x) * e),53(t.vy += (n.y - t.y) * e),54(t.vx += n.vx * E.dampening),55(t.vy += n.vy * E.dampening)),56(t.vx *= this.friction),57(t.vy *= this.friction),58(t.x += t.vx),59(t.y += t.vy),60(e *= E.tension));61},62draw: function () {63var e,64t,65n = this.nodes[0].x,66i = this.nodes[0].y;67ctx.beginPath();68ctx.moveTo(n, i);69for (var a = 1, o = this.nodes.length - 2; a < o; a++) {70e = this.nodes[a];71t = this.nodes[a + 1];72n = 0.5 * (e.x + t.x);73i = 0.5 * (e.y + t.y);74ctx.quadraticCurveTo(e.x, e.y, n, i);75}76e = this.nodes[a];77t = this.nodes[a + 1];78ctx.quadraticCurveTo(e.x, e.y, t.x, t.y);79ctx.stroke();80ctx.closePath();81},82};8384function onMousemove(e) {85function o() {86lines = [];87for (var e = 0; e < E.trails; e++)88lines.push(new Line({ spring: 0.4 + (e / E.trails) * 0.025 }));89}90function c(e) {91(e.touches92? ((pos.x = e.touches[0].pageX), (pos.y = e.touches[0].pageY))93: ((pos.x = e.clientX), (pos.y = e.clientY)),94e.preventDefault());95}96function l(e) {971 == e.touches.length &&98((pos.x = e.touches[0].pageX), (pos.y = e.touches[0].pageY));99}100(document.removeEventListener('mousemove', onMousemove),101document.removeEventListener('touchstart', onMousemove),102document.addEventListener('mousemove', c),103document.addEventListener('touchmove', c),104document.addEventListener('touchstart', l),105c(e),106o(),107render());108}109110function render() {111if (ctx.running) {112ctx.globalCompositeOperation = 'source-over';113ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);114ctx.globalCompositeOperation = 'lighter';115ctx.strokeStyle = 'hsla(' + Math.round(f.update()) + ',50%,50%,0.2)';116ctx.lineWidth = 1;117for (var e, t = 0; t < E.trails; t++) {118(e = lines[t]).update();119e.draw();120}121ctx.frame++;122window.requestAnimationFrame(render);123}124}125126function resizeCanvas() {127ctx.canvas.width = window.innerWidth - 20;128ctx.canvas.height = window.innerHeight;129}130131var ctx,132f,133e = 0,134pos = {},135lines = [],136E = {137debug: true,138friction: 0.5,139trails: 20,140size: 50,141dampening: 0.25,142tension: 0.98,143};144function Node() {145this.x = 0;146this.y = 0;147this.vy = 0;148this.vx = 0;149}150151const renderCanvas = function () {152ctx = document.getElementById('canvas').getContext('2d');153ctx.running = true;154ctx.frame = 1;155f = new n({156phase: Math.random() * 2 * Math.PI,157amplitude: 85,158frequency: 0.0015,159offset: 285,160});161document.addEventListener('mousemove', onMousemove);162document.addEventListener('touchstart', onMousemove);163document.body.addEventListener('orientationchange', resizeCanvas);164window.addEventListener('resize', resizeCanvas);165window.addEventListener('focus', () => {166if (!ctx.running) {167ctx.running = true;168render();169}170});171window.addEventListener('blur', () => {172ctx.running = true;173});174resizeCanvas();175};176177useEffect(() => {178renderCanvas();179180return () => {181ctx.running = false;182document.removeEventListener('mousemove', onMousemove);183document.removeEventListener('touchstart', onMousemove);184document.body.removeEventListener('orientationchange', resizeCanvas);185window.removeEventListener('resize', resizeCanvas);186window.removeEventListener('focus', () => {187if (!ctx.running) {188ctx.running = true;189render();190}191});192window.removeEventListener('blur', () => {193ctx.running = true;194});195};196}, []);197};198199export default useCanvasCursor;
| Prop | Type | Default | Description |
|---|---|---|---|
classname | string | Optional CSS class for styling the main vignette container. | |
children | React.ReactNode | The content to display inside the vignette effect. | |
radius | string | 24px | The radius for the vignette effect. |
inset | string | 20px | The inset value for the vignette effect. |
transitionLength | string | 44px | The length of the transition effect applied to the vignette. |
blur | string | 6px | The blur amount for the vignette effect. |
blurclassname | string | Optional CSS class for styling the blur effect container. |