Skip to content

粒子效果 tsparticles 封装使用

shell
pnpm add tsparticles @tsparticles/vue3 @tsparticles/engine tsparticles-interaction-particles-links  lodash-es
pnpm add -D @types/lodash-es

下载封装资源

查看封装代码
ts
import type { Engine, IOptions, IParticlesOptions, RecursivePartial } from '@tsparticles/engine'
import { merge } from 'lodash-es'
import { loadFull } from 'tsparticles'
import type { ILinks } from 'tsparticles-interaction-particles-links'

// 官方网站 https://particles.js.org/
// 官方文档 https://particles.js.org/docs/interfaces/tsParticles_Engine.Options_Interfaces_IOptions.IOptions.html
export type Option = RecursivePartial<
  IOptions & {
    particles: RecursivePartial<IParticlesOptions & { links: ILinks }>
    emitters?: Option
  }
>

export function createParticlesOption(option: Option) {
  return merge(
    {
      autoPlay: true, // 自动播放
      clear: true,
      background: {
        color: '#fff'
      },
      // 启用视网膜检测,如果禁用,画布使用的比率将始终为 1,而不是设备设置
      detectRetina: true,
      fpsLimit: 90, // 帧率限制
      fullScreen: false, // 设置粒子画布的动画背景模式,将其置于前面或后面

      // 粒子相互作用选项 交互
      interactivity: {
        // 将检测鼠标事件的位置 如果设置为canvas仅将粒子画布作为目标 如果设置为parent仅将粒子画布父级作为目标 如果设置为window每个区域都将作为目标
        detectsOn: 'window',
        // 交互模式选项,这配置每个模式的行为
        modes: {
          // 跟踪,拖拽
          trail: {
            delay: 1,
            pauseOnStop: false,
            quantity: 1
          },
          // 吸引
          attract: {
            distance: 100,
            duration: 0.4,
            easing: 'ease-out-quad',
            factor: 1,
            maxSpeed: 50,
            speed: 1
          },
          // 碰撞
          bounce: {
            distance: 200
          },
          // 气泡
          bubble: {
            distance: 400,
            duration: 2,
            mix: false,
            opacity: 1,
            size: 40,
            divs: {
              distance: 200,
              duration: 0.4,
              mix: false,
              selectors: []
            }
          },
          // 连接
          connect: {
            distance: 80,
            links: {
              opacity: 0.5
            },
            radius: 60
          },
          // 抓住
          grab: {
            distance: 100,
            links: {
              blink: false,
              consent: false,
              opacity: 1
            }
          },
          // 推、放(点击有效)
          push: {
            default: true,
            groups: [],
            quantity: 4
          },
          // 删除(点击有效)
          remove: {
            quantity: 2
          },
          // 排斥
          repulse: {
            distance: 200,
            duration: 0.4,
            factor: 100,
            speed: 1,
            maxSpeed: 50,
            easing: 'ease-out-quad',
            divs: {
              distance: 200,
              duration: 0.4,
              factor: 100,
              speed: 1,
              maxSpeed: 50,
              easing: 'ease-out-quad',
              selectors: []
            }
          },
          // 使迟缓,hover有效
          slow: {
            factor: 3,
            radius: 200
          },
          // 高亮?
          light: {
            area: {
              gradient: {
                start: {
                  value: 'red'
                },
                stop: {
                  value: 'green'
                }
              },
              radius: 1000
            },
            shadow: {
              color: {
                value: '#000000'
              },
              length: 2000
            }
          }
        },
        // 交互事件选项,这配置了启用哪些事件以及应使用哪些模式
        events: {
          onHover: { enable: false, mode: 'light' },
          resize: { enable: true }
        }
      },

      particles: {
        // 反弹
        bounce: {},
        // 碰撞
        collisions: {},
        color: '#aaa',
        // 效果
        effect: {},
        // 分组
        // groups:{},
        // 粒子间的连线
        links: {},
        move: {},
        number: {},
        opacity: {},
        shadow: {},
        // 形状
        shape: {
          // 自定义图片第一种
          //type:'image'
          //image:{ src:'' }
          // 自定义图片第二种
          //type: "images",
          //options: {
          //  images: {
          //    src: "https://particles.js.org/images/cyan_amongus.png",
          //    width: 500,
          //    height: 634
          //  }
          //}
        },
        size: {
          // 变换
          anim: {
            enable: false,
            speed: 80,
            size_min: 4,
            sync: false
          }
        },
        stroke: {},
        zIndex: {},
        // 摇晃 loadSlim不生效,loadFull才生效
        wobble: {
          distance: 10,
          enable: false,
          speed: {
            angle: 10,
            move: 10
          }
        },
        // 减少重复
        reduceDuplicates: true
      },

      smooth: true, // 平滑动画
      pauseOnOutsideViewport: false, // 视窗外暂停
      pauseOnBlur: false // 失去焦点后暂停
      // 弹射 loadFull生效
      //emitters: {
      // 速率
      // rate: {
      //   量
      //   quantity: 1,
      //   延迟s
      //  delay: 1
      // },
      //}
    } as Option,
    option
  ) as Option
}

export async function particlesInit(engine: Engine) {
  console.log('[ engine ]', engine)
  await loadFull(engine)
  //await loadSlim(engine);
}

基本使用

查看代码
vue
<template>
  <div>
    <ClientOnly>
      <vue-particles
        style="z-index: -1; height: 400px; width: 100%"
        id="tsparticles-basic"
        @particles-loaded="particlesLoaded"
        :options
      />
    </ClientOnly>
  </div>
</template>
<script lang="ts" setup>
import type { Container } from '@tsparticles/engine'
import { loadFull } from 'tsparticles'
import { getCurrentInstance } from 'vue'
import { createParticlesOption } from './particles'

const particlesLoaded = (container: Container) => {
  console.log('Particles container loaded', container)
}

const options = createParticlesOption({
  background: { color: '#000' },
  interactivity: {
    modes: {
      grab: {
        distance: 300
      }
    },
    // 交互事件选项,这配置了启用哪些事件以及应使用哪些模式
    events: {
      onHover: { enable: true, mode: 'grab' },
      onClick: { enable: true, mode: 'push' },
      resize: { enable: true }
    }
  },
  particles: {
    color: {
      value: '#aaa'
    },
    links: {
      color: '#aaa',
      distance: 150,
      enable: true,
      opacity: 0.75,
      width: 2
    },
    move: {
      enable: true,
      outModes: 'bounce',
      random: false,
      speed: 3
    },
    number: {
      value: 50
    },
    opacity: {
      value: 0.75
    },
    shape: {
      type: 'circle'
    },
    size: {
      value: 5
    }
  }
})
</script>

雪花

查看代码
vue
<template>
  <div>
    <ClientOnly>
      <vue-particles
        style="z-index: -1; height: 400px; width: 100%"
        id="tsparticles-snow"
        @particles-loaded="particlesLoaded"
        :options
      />
    </ClientOnly>
  </div>
</template>
<script lang="ts" setup>
import type { Container } from '@tsparticles/engine'
import Particles from '@tsparticles/vue3'
import { loadFull } from 'tsparticles'
import { getCurrentInstance } from 'vue'
import { createParticlesOption } from './particles'
import snowSvg from './snow.svg'

const particlesLoaded = (container: Container) => {
  console.log('Particles container loaded', container)
}

const options = createParticlesOption({
  background: {
    color: '#000'
  },
  particles: {
    color: {
      value: '#fff'
    },
    move: {
      direction: 'bottom',
      enable: true,
      random: false,
      speed: 3
    },

    number: { value: 60 },
    opacity: {
      value: { max: 0.8, min: 0.4 }
    },
    shape: { type: 'image', options: { image: { src: snowSvg } } },
    // 暂时无效,不知原因
    wobble: { distance: 10, enable: true, speed: { angle: 10, move: 10 } },
    size: {
      //random: true,
      value: { max: 14, min: 4 }
    }
  }
})
</script>

漂浮

查看代码
vue
<template>
  <div>
    <ClientOnly>
      <vue-particles
        style="z-index: -1; height: 400px; width: 100%"
        id="tsparticles-fly"
        @particles-loaded="particlesLoaded"
        :options
      />
    </ClientOnly>
  </div>
</template>
<script lang="ts" setup>
import type { Container } from '@tsparticles/engine'
import Particles from '@tsparticles/vue3'
import { loadFull } from 'tsparticles'
import { getCurrentInstance } from 'vue'
import { createParticlesOption } from './particles'
import ufoSvg from './ufo.svg'

const particlesLoaded = (container: Container) => {
  console.log('Particles container loaded', container)
}

const options = createParticlesOption({
  background: { color: '#000' },
  particles: {
    color: { value: '#fff' },
    groups: {
      z5000: { number: { value: 70 }, zIndex: { value: 50 } },
      z7500: { number: { value: 30 }, zIndex: { value: 75 } },
      z2500: { number: { value: 50 }, zIndex: { value: 25 } },
      z1000: { number: { value: 40 }, zIndex: { value: 10 } }
    },
    move: {
      angle: { offset: 0, value: 10 },
      direction: 'right',
      enable: true,
      speed: 5
    },
    number: { value: 200 },
    opacity: { value: 1 },
    shape: { close: true, fill: true, options: {}, type: 'circle' },
    size: { value: 3 }
    // zIndex: { value: 5, opacityRate: 0.5, sizeRate: 1, velocityRate: 1 },
  },
  // 弹射
  emitters: {
    autoPlay: true,
    life: { wait: false },
    rate: { quantity: 1, delay: 7 },
    particles: {
      shape: { type: 'image', options: { image: { src: ufoSvg } } },
      size: { value: 60 },
      move: {
        speed: 10,
        outModes: { default: 'out', right: 'destroy' },
        straight: true
      },
      zIndex: { value: 0 }
      //rotate: {
      //  value: { min: 0, max: 360 },
      //  animation: { enable: true, speed: 10, sync: true }
      //}
    },
    position: { x: -5, y: 50 }
  }
})
</script>

星空

查看代码
vue
<template>
  <div>
    <ClientOnly>
      <vue-particles
        style="z-index: -1; height: 400px; width: 100%"
        id="tsparticles-star"
        @particles-loaded="particlesLoaded"
        :options
      />
    </ClientOnly>
  </div>
</template>
<script lang="ts" setup>
import type { Container } from '@tsparticles/engine'
import Particles from '@tsparticles/vue3'
import { loadFull } from 'tsparticles'
import { getCurrentInstance } from 'vue'
import { createParticlesOption } from './particles'
import ufoSvg from './ufo.svg'

const particlesLoaded = (container: Container) => {
  console.log('Particles container loaded', container)
}

const options = createParticlesOption({
  background: { color: '#000' },
  manualParticles: [
    {
      options: {
        shape: { type: 'image', options: { image: { src: ufoSvg } } },
        size: {
          value: { min: 54, max: 80 },
          animation: {
            enable: true,
            mode: 'auto',
            speed: 50
          }
        },
        move: { enable: false },
        opacity: { value: 1 },
        zIndex: { value: 0 }
      },
      position: { x: 50, y: 50 }
    }
  ],
  particles: {
    color: { value: '#fff' },
    groups: {
      z5000: { number: { value: 70 }, zIndex: { value: 50 } },
      z7500: { number: { value: 30 }, zIndex: { value: 75 } },
      z2500: { number: { value: 50 }, zIndex: { value: 25 } },
      z1000: { number: { value: 40 }, zIndex: { value: 10 } }
    },
    move: {
      straight: true,
      direction: 'left',
      enable: true,
      speed: 5
    },
    number: { value: 200 },
    opacity: {
      value: { max: 1, min: 0.4 },
      animation: {
        count: 0,
        enable: true,
        speed: 2,
        sync: false,
        mode: 'auto',
        startValue: 'random'
      }
    },
    shape: { type: 'star' },
    size: { value: 3 }
  }
})
</script>