tsParticles External Particle Interaction 插件:在鼠标/点击位置生成自定义粒子的实现与配置详解
【免费下载链接】tsparticlestsParticles - Easily create highly customizable JavaScript particles effects, confetti explosions and fireworks animations and use them as animated backgrounds for your website. Ready to use components available for React.js, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Inferno, Solid, Riot and Web Components.项目地址: https://gitcode.com/GitHub_Trending/ts/tsparticles
本篇指南围绕 tsParticles 仓库中@tsparticles/interaction-external-particle交互插件展开:介绍它如何通过 CDN、ESM/CommonJS 三种方式接入,如何把particle模式挂到onHover/onClick事件上,以及pauseOnStop、stopDelay、replaceCursor、options四个核心参数的确切行为。读完并结合仓库源码后,你可以直接在项目中复现“鼠标划过留下一个跟随光标的粒子、点击生成粒子”的交互效果,并理解插件内部的粒子生成、销毁与延时清理机制。
插件定位:一个“外部交互”(External Interaction)插件
@tsparticles/interaction-external-particle是 tsParticles 交互插件族中的一员,官方定位是:“interaction plugin for particle effect around mouse or HTML elements”,即让粒子效果出现在鼠标或 HTML 元素周围。与其他外部交互(bubble、repulse、attract等“作用于已有粒子”的模式不同,particle模式属于“生成型”交互:它在光标位置额外创建一个独立的粒子,该粒子的外观由你单独配置的粒子选项决定,与画布上主粒子群的选项互不干扰。
从源码结构看,该插件只有一个核心类InteractivityParticleMaker(继承自@tsparticles/plugin-interactivity提供的ExternalInteractorBase),插件的全部行为都集中在这个类里,见 InteractivityParticleMaker。插件包的元信息(版本 4.3.3、peer 依赖等)见 package.json,其 README 原文见 interactions/external/particle/README.md。
接入前的必备清单(Quick checklist)
README 给出的接入步骤可以归纳为三步,这三步的顺序是硬性要求:
- 安装
@tsparticles/engine(或使用 CDN 全家桶); - 在调用
tsParticles.load(...)之前,调用本包的 loader 函数(以及交互基础设施插件loadInteractivityPlugin); - 在
tsParticles.load(...)的配置中应用本插件的选项(见下文“选项映射”一节)。
之所以第 2 步必须放在load之前,是因为 loader 做的事情是把InteractivityParticleMaker注册到引擎的插件管理器中。从 index.ts 可以看到:
export async function loadExternalParticleInteraction(engine: Engine): Promise<void> { engine.checkVersion(__VERSION__); await engine.pluginManager.register((e: InteractivityEngine) => { ensureInteractivityPluginLoaded(e); e.pluginManager.addInteractor?.("externalParticle", container => { return Promise.resolve(new InteractivityParticleMaker(container)); }); }); }两个关键点:
ensureInteractivityPluginLoaded(e):强制要求@tsparticles/plugin-interactivity已加载,因此loadInteractivityPlugin(tsParticles)必须先行;addInteractor("externalParticle", ...):以externalParticle为键注册交互器工厂。之后每当particle模式被事件触发时,引擎会用容器实例化InteractivityParticleMaker。
另外,仓库还提供了懒加载入口 index.lazy.ts(对应 npm 包的./lazy子路径),它在注册时才动态import("./InteractivityParticleMaker.js"),适合按需加载以减小首屏体积;浏览器端的全局挂载逻辑见 browser.ts,它会把loadExternalParticleInteraction挂到globalThis上,这就是 CDN 场景下能直接调用全局函数的原因。
三种接入方式
方式一:CDN / Vanilla JS / jQuery
CDN 或纯 HTML 场景下,需要引入tsparticles.interaction.external.particle.min.js这个文件,它会向全局导出:
loadExternalParticleInteraction;加载脚本后即可按如下方式初始化(与 README 中的官方示例一致):
(async () => { await loadInteractivityPlugin(tsParticles); await loadExternalParticleInteraction(tsParticles); await tsParticles.load({ id: "tsparticles", options: {/* options */}, }); })();方式二:ESM 模块
$ npm install @tsparticles/interaction-external-particle或
$ yarn add @tsparticles/interaction-external-particleimport { tsParticles } from "@tsparticles/engine"; import { loadInteractivityPlugin } from "@tsparticles/plugin-interactivity"; import { loadExternalParticleInteraction } from "@tsparticles/interaction-external-particle"; (async () => { await loadInteractivityPlugin(tsParticles); await loadExternalParticleInteraction(tsParticles); })();方式三:CommonJS
const { tsParticles } = require("@tsparticles/engine"); const { loadInteractivityPlugin } = require("@tsparticles/plugin-interactivity"); const { loadExternalParticleInteraction } = require("@tsparticles/interaction-external-particle"); (async () => { await loadInteractivityPlugin(tsParticles); await loadExternalParticleInteraction(tsParticles); })();三种方式的公共依赖是一致的:从 package.json 的peerDependencies可以看到,本包硬性依赖@tsparticles/engine和@tsparticles/plugin-interactivity两个 workspace 包。若使用@tsparticles/slim及以上的 CDN 全家桶,interactivity 插件已内置,可省掉loadInteractivityPlugin一步;裸装@tsparticles/engine时则两者缺一不可。
选项映射:particle模式如何被事件触发
README 中的“Option mapping”一节明确了本插件的两个配置键位,这是使用时最容易被忽视的部分:
- 事件键:
interactivity.events.onHover.mode或interactivity.events.onClick.mode,取值"particle"; - 模式选项键:
interactivity.modes.particle。
最小可用配置:
{ "interactivity": { "events": { "onHover": { "enable": true, "mode": "particle" } }, "modes": { "particle": {} } } }如果想让点击也生成粒子,把onClick同样配置即可(mode支持数组,可同时命中多个模式):
{ "interactivity": { "events": { "onHover": { "enable": true, "mode": "particle" }, "onClick": { "enable": true, "mode": "particle" } }, "modes": { "particle": {} } } }触发判定逻辑就在 InteractivityParticleMaker.ts 的isEnabled方法 中,可以精确对应到配置语义:
return ( !!events && ((mouse.clicking && mouse.inside && !!mouse.position && isInArray(particleMode, events.onClick.mode)) || (mouse.inside && !!mouse.position && isInArray(particleMode, events.onHover.mode))) );即:onClick分支要求“正在点击 + 鼠标在容器内 + 有位置 +onClick.mode数组包含"particle"”;onHover分支只要求“鼠标在容器内 + 有位置 +onHover.mode包含"particle"”。注意events优先取粒子级的particle.interactivity覆写,否则回退到全局options.interactivity。
modes.particle的四个参数:默认值与源码级语义
选项类定义见 InteractivityParticleOptions,接口定义见 IInteractivityParticleOptions。四个参数及默认值如下:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
options | 粒子选项(RecursivePartial<IParticlesOptions>) | 无 | 用于生成“交互粒子”的外观选项(颜色、形状、大小等),与主粒子群配置隔离 |
pauseOnStop | boolean | false | 鼠标停止移动时暂停粒子(实际是启动延时销毁计时器,见下文) |
replaceCursor | boolean | false | 用生成的粒子替换系统光标(隐藏光标) |
stopDelay | number | 0(毫秒) | 鼠标停止后、粒子被销毁前的延时 |
逐项结合源码看它们的实际行为:
options:独立的一套粒子外观
在interact方法中,真正创建粒子的代码是:
const particleOptions = deepExtend(interactivityParticleOptions.options, { move: { enable: false, }, }) as RecursivePartial<ParticlesOptions>; this.#particle = container.particles.addParticle(this.#lastPosition, particleOptions);两点值得注意:
- 传入的是
interactivityParticleOptions.options,即modes.particle.options,不是画布主配置particles——所以交互粒子可以拥有完全不同的颜色、形状和尺寸; - 源码强制叠加了
move.enable: false,意味着无论你在options.move里怎么写,交互粒子自身都不会运动,它只会跟随光标位置被逐帧搬运(见下文“位置搬运”)。
一个典型配置示例(生成一个跟随光标的彩色圆点):
{ "interactivity": { "events": { "onHover": { "enable": true, "mode": "particle" } }, "modes": { "particle": { "pauseOnStop": true, "stopDelay": 500, "replaceCursor": true, "options": { "color": { "value": ["#ff3300", "#402243"] }, "opacity": { "value": 0.8 }, "size": { "value": 10 }, "shape": { "type": "circle" } } } } } }replaceCursor:隐藏光标,让粒子“变成”指针
当replaceCursor为true时,粒子创建的同时会把目标元素的cursor设为"none"(作用对象是交互容器对应的HTMLElement,若是Window/Document则作用于document.body);粒子销毁时再把cursor还原为空字符串。源码见 InteractivityParticleMaker.ts 中 replaceCursor 的两处分支。这解释了为什么该参数适合做“自定义光标”效果:粒子本身不运动(move.enable: false),但每一帧都被强制对齐到鼠标坐标(this.#particle.position.x = this.#lastPosition.x),视觉等效于一个可自定义外观的鼠标指针。
pauseOnStop+stopDelay:延时销毁机制
这两个参数配合实现了“鼠标停住 → 延时 → 粒子消失”的行为。interact中先比较当前鼠标坐标与上一帧坐标#lastPosition,若完全一致且pauseOnStop为true,视为mouseStopped,随后:
this.#clearTimeout = setTimeout(() => { if (!this.#particle) return; // 还原光标(如果 replaceCursor 开启) this.#particle.destroy(true); this.#particle = undefined; }, clearDelay);即stopDelay毫秒后销毁粒子并还原光标;若鼠标再次移动,会先clearTimeout取消这次销毁、继续搬运现有粒子。stopDelay默认为0,表示鼠标一停(下一帧)就销毁;pauseOnStop默认为false,表示不做停止检测、粒子常驻跟随直到clear()。
一次交互的完整生命周期
把interact方法串起来,可以梳理出particle模式每一帧的完整流程:
- 前置检查:容器不存在
retina.reduceFactor或actualOptions.interactivity.modes.particle未配置时直接返回; - 记录坐标:把当前鼠标坐标存入
#lastPosition,无坐标时返回; - 停止检测:若命中
mouseStopped,启动/维持stopDelay延时销毁计时器后返回; - 取消旧计时:若鼠标在动,先清除可能挂起的销毁计时器;
- 创建粒子:
#particle不存在时,用modes.particle.options(叠加move.enable: false)在鼠标位置调用container.particles.addParticle创建;replaceCursor开启时同步隐藏光标; - 位置搬运:每帧把粒子的
position.x/y直接赋值为鼠标坐标,实现“吸附跟随”。
此外,该类声明了readonly maxDistance = 0,clear()、init()、reset()均为空实现——结合maxDistance的含义可以推断:该模式不受“影响半径”限制(它本就不作用于已有粒子),引擎在交互器重置时的通用清理也不会影响它持有的粒子,销毁完全由上面的延时逻辑负责。
常见坑(Common pitfalls)与排障建议
README 列出的三条排障经验,结合源码可以给出更具体的解释:
tsParticles.load(...)先于loadInteractivityPlugin(...)/loadExternalParticleInteraction(...)调用:loader 是通过engine.pluginManager.register延迟注册的,但particle模式必须依赖已注册的externalParticle交互器和plugin-interactivity基础设施;顺序颠倒时,事件命中"particle"模式却找不到对应交互器,表现为配置了却不生成粒子。- 启用高级选项前先核对 peer 依赖:
@tsparticles/engine与@tsparticles/plugin-interactivity是 peerDependencies(见 package.json),缺失其一会直接抛加载错误。 - 一次只改一组选项:
particle模式的配置面包含events(何时触发)、modes.particle.options(粒子长什么样)、pauseOnStop/stopDelay(何时消失)、replaceCursor(是否替换光标)四个相对独立的维度,分组修改可以快速定位回归。
若配置了onHover.mode: "particle"但看不到粒子,可依次检查:鼠标是否真正在容器内(isEnabled要求mouse.inside)、modes.particle是否声明(哪怕空对象)、modes.particle.options的颜色/尺寸是否可见(透明色或 0 尺寸不会报错但不可见)。
延伸:仓库中可继续深入的入口
- 插件 README 与本文依据的原始文档:interactions/external/particle/README.md
- 交互器核心实现:interactions/external/particle/src/InteractivityParticleMaker.ts
- 注册入口(标准 / 懒加载 / 浏览器全局):index.ts、index.lazy.ts、browser.ts
- 选项类与接口:InteractivityParticleOptions.ts、IInteractivityParticleOptions.ts
- 引擎侧交互文档(
interactivity总览、事件与模式):markdown/Options/Interactivity.md、markdown/Options/Interactivity/Events.md、markdown/Options/Interactivity/Modes.md
markdown/Options/Interactivity/Modes.md中还收录了push(点击在光标附近添加粒子)等相近模式,可与本插件的particle模式对照:push是在主粒子群参数基础上“追加粒子”,而particle是用独立选项生成一个专属粒子,两者适用场景不同,选型时可据此区分。
【免费下载链接】tsparticlestsParticles - Easily create highly customizable JavaScript particles effects, confetti explosions and fireworks animations and use them as animated backgrounds for your website. Ready to use components available for React.js, Vue.js (2.x and 3.x), Angular, Svelte, jQuery, Preact, Inferno, Solid, Riot and Web Components.项目地址: https://gitcode.com/GitHub_Trending/ts/tsparticles
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考