mouseGlowDirective.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. const defaultOptions = {
  2. size: 400,
  3. color1: 'rgba(128, 119, 255, 0.5)',
  4. color2: 'rgba(231, 76, 255, 0.5)',
  5. angle: 148,
  6. opacity: 0.8,
  7. blur: 50,
  8. duration: 0.15
  9. }
  10. const createGlowElement = (container, options) => {
  11. const glow = document.createElement('div')
  12. const gradient = `conic-gradient(from ${options.angle}deg, ${options.color1} 0%, ${options.color2} 50%, ${options.color1} 100%)`
  13. glow.style.cssText = `
  14. position: absolute;
  15. left: 0;
  16. top: 0;
  17. width: ${options.size}px;
  18. height: ${options.size}px;
  19. background: ${gradient};
  20. border-radius: 50%;
  21. pointer-events: none;
  22. opacity: 0;
  23. transition: opacity ${options.duration}s ease;
  24. will-change: transform;
  25. filter: blur(${options.blur}px) drop-shadow(0 4px 4px rgba(0,0,0,0.25));
  26. z-index: 1;
  27. `
  28. container.style.position = 'relative'
  29. container.style.overflow = 'hidden'
  30. container.appendChild(glow)
  31. return glow
  32. }
  33. const updateGlowPosition = (glow, x, y, options) => {
  34. glow.style.transform = `translate(${x - options.size / 2}px, ${y - options.size / 2}px)`
  35. }
  36. const mouseGlowDirective = {
  37. mounted(el, binding) {
  38. const options = { ...defaultOptions, ...binding.value }
  39. const glow = createGlowElement(el, options)
  40. let animationFrameId = null
  41. let isVisible = false
  42. const handleMouseMove = (e) => {
  43. if (animationFrameId !== null) {
  44. cancelAnimationFrame(animationFrameId)
  45. }
  46. animationFrameId = requestAnimationFrame(() => {
  47. const rect = el.getBoundingClientRect()
  48. const x = e.clientX - rect.left
  49. const y = e.clientY - rect.top
  50. updateGlowPosition(glow, x, y, options)
  51. animationFrameId = null
  52. })
  53. }
  54. const handleMouseEnter = () => {
  55. if (!isVisible) {
  56. glow.style.opacity = options.opacity?.toString() || defaultOptions.opacity.toString()
  57. isVisible = true
  58. }
  59. }
  60. const handleMouseLeave = () => {
  61. if (isVisible) {
  62. glow.style.opacity = '0'
  63. isVisible = false
  64. }
  65. }
  66. el.addEventListener('mousemove', handleMouseMove, { passive: true })
  67. el.addEventListener('mouseenter', handleMouseEnter, { passive: true })
  68. el.addEventListener('mouseleave', handleMouseLeave, { passive: true })
  69. el._mouseGlowDirective = {
  70. glow,
  71. handleMouseMove,
  72. handleMouseEnter,
  73. handleMouseLeave,
  74. animationFrameId: () => animationFrameId
  75. }
  76. },
  77. updated(el, binding) {
  78. const directiveData = el._mouseGlowDirective
  79. if (directiveData) {
  80. const options = { ...defaultOptions, ...binding.value }
  81. const { glow } = directiveData
  82. const gradient = `conic-gradient(from ${options.angle}deg, ${options.color1} 0%, ${options.color2} 50%, ${options.color1} 100%)`
  83. glow.style.width = `${options.size}px`
  84. glow.style.height = `${options.size}px`
  85. glow.style.background = gradient
  86. glow.style.filter = `blur(${options.blur}px) drop-shadow(0 4px 4px rgba(0,0,0,0.25))`
  87. glow.style.transition = `opacity ${options.duration}s ease`
  88. }
  89. },
  90. unmounted(el) {
  91. const directiveData = el._mouseGlowDirective
  92. if (directiveData) {
  93. const { glow, handleMouseMove, handleMouseEnter, handleMouseLeave, animationFrameId } = directiveData
  94. const frameId = animationFrameId()
  95. if (frameId !== null) {
  96. cancelAnimationFrame(frameId)
  97. }
  98. el.removeEventListener('mousemove', handleMouseMove)
  99. el.removeEventListener('mouseenter', handleMouseEnter)
  100. el.removeEventListener('mouseleave', handleMouseLeave)
  101. glow.remove()
  102. delete el._mouseGlowDirective
  103. }
  104. }
  105. }
  106. export default mouseGlowDirective