const defaultOptions = { size: 300, color1: 'rgba(128, 119, 255, 0.5)', color2: 'rgba(231, 76, 255, 0.5)', angle: 148, opacity: 0.5, blur: 50, duration: 0.15 } const createGlowElement = (container, options) => { const glow = document.createElement('div') const gradient = `conic-gradient(from ${options.angle}deg, ${options.color1} 0%, ${options.color2} 50%, ${options.color1} 100%)` glow.style.cssText = ` position: absolute; left: 0; top: 0; width: ${options.size}px; height: ${options.size}px; background: ${gradient}; border-radius: 50%; pointer-events: none; opacity: 0; transition: opacity ${options.duration}s ease; will-change: transform; filter: blur(${options.blur}px) drop-shadow(0 4px 4px rgba(0,0,0,0.25)); z-index: 1; ` container.style.position = 'relative' container.style.overflow = 'hidden' container.appendChild(glow) return glow } const updateGlowPosition = (glow, x, y, options) => { glow.style.transform = `translate(${x - options.size / 2}px, ${y - options.size / 2}px)` } const mouseGlowDirective = { mounted(el, binding) { const options = { ...defaultOptions, ...binding.value } const glow = createGlowElement(el, options) let animationFrameId = null let isVisible = false const handleMouseMove = (e) => { if (animationFrameId !== null) { cancelAnimationFrame(animationFrameId) } animationFrameId = requestAnimationFrame(() => { const rect = el.getBoundingClientRect() const x = e.clientX - rect.left const y = e.clientY - rect.top updateGlowPosition(glow, x, y, options) animationFrameId = null }) } const handleMouseEnter = () => { if (!isVisible) { glow.style.opacity = options.opacity?.toString() || defaultOptions.opacity.toString() isVisible = true } } const handleMouseLeave = () => { if (isVisible) { glow.style.opacity = '0' isVisible = false } } el.addEventListener('mousemove', handleMouseMove, { passive: true }) el.addEventListener('mouseenter', handleMouseEnter, { passive: true }) el.addEventListener('mouseleave', handleMouseLeave, { passive: true }) el._mouseGlowDirective = { glow, handleMouseMove, handleMouseEnter, handleMouseLeave, animationFrameId: () => animationFrameId } }, updated(el, binding) { const directiveData = el._mouseGlowDirective if (directiveData) { const options = { ...defaultOptions, ...binding.value } const { glow } = directiveData const gradient = `conic-gradient(from ${options.angle}deg, ${options.color1} 0%, ${options.color2} 50%, ${options.color1} 100%)` glow.style.width = `${options.size}px` glow.style.height = `${options.size}px` glow.style.background = gradient glow.style.filter = `blur(${options.blur}px) drop-shadow(0 4px 4px rgba(0,0,0,0.25))` glow.style.transition = `opacity ${options.duration}s ease` } }, unmounted(el) { const directiveData = el._mouseGlowDirective if (directiveData) { const { glow, handleMouseMove, handleMouseEnter, handleMouseLeave, animationFrameId } = directiveData const frameId = animationFrameId() if (frameId !== null) { cancelAnimationFrame(frameId) } el.removeEventListener('mousemove', handleMouseMove) el.removeEventListener('mouseenter', handleMouseEnter) el.removeEventListener('mouseleave', handleMouseLeave) glow.remove() delete el._mouseGlowDirective } } } export default mouseGlowDirective