Spotlight Cursor Effect
An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.
An interactive React component that adds a dynamic bubble effect, visually tracking cursor movement in real time.
1'use client';2import { HTMLAttributes } from 'react';3import useSpotlightEffect from '@/hooks/use-spotlight';45// Define an interface for the spotlight configuration6interface SpotlightConfig {7radius?: number;8brightness?: number;9color?: string;10smoothing?: number;11}1213// Combine props with potential HTML canvas attributes14interface SpotlightCursorProps extends HTMLAttributes<HTMLCanvasElement> {15config?: SpotlightConfig;16}1718const SpotlightCursor = ({19config = {},20className,21...rest22}: SpotlightCursorProps) => {23// Provide default configuration if not specified24const spotlightConfig = {25radius: 200,26brightness: 0.15,27color: '#ffffff',28smoothing: 0.1,29...config,30};3132const canvasRef = useSpotlightEffect(spotlightConfig);3334return (35<canvas36ref={canvasRef}37className={`fixed top-0 left-0 pointer-events-none z-[9999] w-full h-full ${className}`}38{...rest}39/>40);41};4243export default SpotlightCursor;44
1// @ts-nocheck2'use client';3import { useEffect, useRef, useState } from 'react';45const useSpotlightEffect = (config = {}) => {6const {7spotlightSize = 200,8spotlightIntensity = 0.8,9fadeSpeed = 0.1,10glowColor = '255, 255, 255',11pulseSpeed = 2000,12} = config;1314const canvasRef = useRef(null);15const ctxRef = useRef(null);16const spotlightPos = useRef({ x: 0, y: 0 });17const targetPos = useRef({ x: 0, y: 0 });18const animationFrame = useRef(null);19const [isHovered, setIsHovered] = useState(false);2021useEffect(() => {22const canvas = canvasRef.current;23const ctx = canvas.getContext('2d');24ctxRef.current = ctx;2526const resizeCanvas = () => {27canvas.width = window.innerWidth;28canvas.height = window.innerHeight;29};3031const lerp = (start, end, factor) => {32return start + (end - start) * factor;33};3435const handleMouseMove = (e) => {36targetPos.current = { x: e.clientX, y: e.clientY };37setIsHovered(true);38};3940const handleMouseLeave = () => {41setIsHovered(false);42};4344const render = () => {45if (!canvas || !ctx) return;4647// Smooth position transition48spotlightPos.current.x = lerp(49spotlightPos.current.x,50targetPos.current.x,51fadeSpeed52);53spotlightPos.current.y = lerp(54spotlightPos.current.y,55targetPos.current.y,56fadeSpeed57);5859ctx.clearRect(0, 0, canvas.width, canvas.height);6061// Create dark overlay62ctx.fillStyle = 'rgba(0, 0, 0, 0.85)';63ctx.fillRect(0, 0, canvas.width, canvas.height);6465// Calculate pulse effect66const pulseScale =671 + 0.1 * Math.sin((Date.now() / pulseSpeed) * Math.PI * 2);68const currentSpotlightSize = spotlightSize * pulseScale;6970// Create spotlight gradient71const gradient = ctx.createRadialGradient(72spotlightPos.current.x,73spotlightPos.current.y,740,75spotlightPos.current.x,76spotlightPos.current.y,77currentSpotlightSize78);7980// Add multiple color stops for smoother transition81gradient.addColorStop(0, `rgba(${glowColor}, ${spotlightIntensity})`);82gradient.addColorStop(830.5,84`rgba(${glowColor}, ${spotlightIntensity * 0.5})`85);86gradient.addColorStop(1, 'rgba(0, 0, 0, 0)');8788// Apply spotlight effect89ctx.globalCompositeOperation = 'destination-out';90ctx.fillStyle = gradient;91ctx.beginPath();92ctx.arc(93spotlightPos.current.x,94spotlightPos.current.y,95currentSpotlightSize,960,97Math.PI * 298);99ctx.fill();100101// Add glow effect102ctx.globalCompositeOperation = 'source-over';103const glowGradient = ctx.createRadialGradient(104spotlightPos.current.x,105spotlightPos.current.y,1060,107spotlightPos.current.x,108spotlightPos.current.y,109currentSpotlightSize * 1.2110);111glowGradient.addColorStop(0, `rgba(${glowColor}, 0.2)`);112glowGradient.addColorStop(1, 'rgba(0, 0, 0, 0)');113ctx.fillStyle = glowGradient;114ctx.beginPath();115ctx.arc(116spotlightPos.current.x,117spotlightPos.current.y,118currentSpotlightSize * 1.2,1190,120Math.PI * 2121);122ctx.fill();123124animationFrame.current = requestAnimationFrame(render);125};126127resizeCanvas();128window.addEventListener('resize', resizeCanvas);129document.addEventListener('mousemove', handleMouseMove);130document.addEventListener('mouseleave', handleMouseLeave);131render();132133return () => {134window.removeEventListener('resize', resizeCanvas);135document.addEventListener('mousemove', handleMouseMove);136document.removeEventListener('mouseleave', handleMouseLeave);137if (animationFrame.current) {138cancelAnimationFrame(animationFrame.current);139}140};141}, [spotlightSize, spotlightIntensity, fadeSpeed, glowColor, pulseSpeed]);142143return canvasRef;144};145146export default useSpotlightEffect;
Prop | Type | Default | Description |
---|---|---|---|
config | { radius?: number, brightness?: number, color?: string, smoothing?: number } | {} | Configuration object for the spotlight cursor. |
radius | number | undefined | The radius of the spotlight effect in pixels. |
brightness | number | undefined | The brightness of the spotlight effect. |
color | string | undefined | The color of the spotlight. |
smoothing | number | undefined | The smoothing factor applied to the spotlight effect (a value between 0 and 1). |
className | string | undefined | Additional class names to apply to the canvas element. |