title: clickEffect
slug: clickEffect
date: 2024-8-16
id: 4
tags: 
  - js
  - css

网页粒子点击效果实现

这种全局的样式,当然也是放在app/layout.js当中全局显示,只不过ClickEffect组件的返回值是粒子元素,而且是一堆粒子,因此我们使用一个粒子数组

而且我们需要监听点击事件,因此document.addEventListner监听click激活handleClick自定义函数,点击完给粒子数组赋值,因此需要useState,此外,显然useEffect是需要的,同时考虑到粒子的位置更新,也需要使用useCallback

因此得到component/ClickEffect/ClickEffect.js

"use client"
import {useState,useCallback,useEffect} from 'react'
import styles from './ClickEffect.module.css'
​
const ClickEffect = ({
  particleCount = 20, particleSize = 20, duration = 1000
})=>{
  const [particles,setParticles] = useState([]);
  
  //新建粒子
  const createParticle = useCallback((x,y)=>{
    return{
            x,
            y,
            color: COLORS[Math.floor(Math.random() * COLORS.length)],
            angle: Math.random() * Math.PI * 2,
            speed: Math.random() * 6 + 2,
            rotation: Math.random() * 360,
            id: Math.random(),
    }
  },[])
  
  //自定义点击事件
  const handleClick = useCallback((e)=>{
    const newParticles = Array.from(
      {length:particleCount},()=>{
          createParticle(e.clientX,e.elientY)//react-dom property
      })
    setParticles((prevParticles)=>
                [...prevParticles,newParticles])
    //prev + new,prev 不消失
    setTimeout(()=>{
      setParticles((prevParticles)=>{
        prevParticles.filter(
          (prev)=>!newParticles.includes(prev))
      })
    })
  },[particleCount,createParticle,duration])
  
  //监听点击事件
  useEffect(() => {
        document.addEventListener('click', handleClick);
        return () => {
            document.removeEventListener('click', handleClick);
        };
    }, [handleClick]);
  
  //动画效果
  useEffect(() => {
        let animationFrameId;
​
        const animate = () => {
            setParticles((prevParticles) =>
                prevParticles.map((particle) => ({
                    ...particle,
                    x: particle.x + Math.cos(particle.angle) * particle.speed,
                    y: particle.y + Math.sin(particle.angle) * particle.speed,
                    speed: particle.speed * 0.98,
                }))
            );
            animationFrameId = requestAnimationFrame(animate);
        };
​
        animate();
​
        return () => {
            cancelAnimationFrame(animationFrameId);
        };
    }, []);
  // 返回粒子元素
  return (
  <>
    {particles.map((particle) => (
                <div
                    key={particle.id}
                    className={styles.particle}
                    style={{
                        left: `${particle.x}px`,
                        top: `${particle.y}px`,
                        backgroundColor: particle.color,
                        width: `${particleSize}px`,
                        height: `${particleSize}px`,
                        transform: `rotate(${particle.rotation}deg)`,
                    }}
                />
            ))}
  </>
  )
}
​
export default ClickEffect;

在此模块css中只需要定义一个动画就行了component/ClickEffect/ClickEffect.module.css

.particle {
    position: fixed;
    border-radius: 50%;
    pointer-events: none;
    z-index: 9999;
    opacity: 0;
    animation: particleAnim 1s ease-out forwards;
}
​
@keyframes particleAnim {
    0% {
        opacity: 1;
        transform: scale(1) rotate(0deg);
    }
    100% {
        opacity: 0;
        transform: scale(0) rotate(360deg);
    }
}

最终在app/layout.js引入就可看到效果,点击页面迸发出粒子烟花

import ClickEffect from '@/components/ClickEffect/ClickEffect'
​
export default function RootLayout({children}){
  return (
    <html>
      <body>
        <CLickEffect/>
        {children}
      </body>
    </html>
  )
}