在我的 typescript next js 项目中,我有这个粒子组件:
"use client"
import { Container, ISourceOptions, MoveDirection, OutMode } from "@tsparticles/engine";
import Particles, { initParticlesEngine } from "@tsparticles/react";
import { loadSlim } from "@tsparticles/slim";
import React, { useEffect, useMemo, useState } from "react";
const ParticlesComponent: React.FC = ({}) => {
const [init, setInit] = useState(false)
useEffect(() => {
initParticlesEngine(async (engine) => {
await loadSlim(engine)
}).then(() => {
setInit(true)
})
}, [])
const particlesLoaded = async (container?: Container): Promise<void> => {
console.log(container);
};
const options: ISourceOptions = useMemo(
() => (
{
background: {
color: {
value: "#0F0F11",
},
},
fpsLimit: 60,
particles: {
color: {
value: ["#ffffff"],
},
move: {
direction: MoveDirection.none,
enable: true,
outModes: {
default: OutMode.out,
},
random: false,
speed: 0.1,
straight: false,
},
number: {
density: {
enable: true,
},
value: 1800,
},
opacity: {
value: { min: 0.1, max: 0.5 },
},
shape: {
type: "circle",
},
size: {
value: { min: 0.1, max: 1 },
},
},
}
),
[],
);
return (
<Particles options={options} particlesLoaded={particlesLoaded} className="-z-20 absolute"></Particles>
)
}
export default ParticlesComponent
我想通过单击按钮以编程方式更改各种选项参数,例如速度和颜色。
我可以将速度设置为状态变量:
const [speed, setSpeed] = useState(0.1)
并在选项中实现它,同时将其提供给
useMemo
依赖项。这可行,但问题是它会导致粒子“突然”刷新。有没有办法在不突然改变的情况下更改选项参数?
我个人使用编程 API 而不是框架组件(在我的例子中是 Vue)实现了它,如此处所述 https://github.com/tsarticles/tsarticles/issues/180#issuecomment-604711173
在
stopConfetti()
功能中,我在动画停止之前淡化五彩纸屑
let confettiContainer: ReturnType<typeof tsParticles.domItem>
export async function startConfetti(options: Record<string, unknown> = {}) {
if (confettiContainer) {
console.info(`Confetti already started`)
return
}
confettiContainer = await tsParticles.load({
id: `tsparticles`,
options: {
particles: {
"number": {
"value": 0,
},
"shape": {
"type": [
// `circle`,
// `square`,
`emoji`,
],
"options": {
"emoji": {
"particles": {
"size": {
"value": 8,
},
},
"value": [
`⭐️`,
],
},
},
},
"size": {
"value": {
"min": 2,
"max": 4,
},
},
"links": {
"enable": false,
},
"life": {
"duration": {
"sync": true,
"value": 5,
},
"count": 1,
},
"move": {
"enable": true,
"gravity": {
"enable": true,
"acceleration": 10,
},
"speed": {
"min": 10,
"max": 20,
},
"decay": 0.1,
"direction": `none`,
"straight": false,
"outModes": {
"default": `destroy`,
"top": `none`,
},
},
"rotate": {
"value": {
"min": 0,
"max": 360,
},
"direction": `random`,
"move": true,
"animation": {
"enable": true,
"speed": 60,
},
},
"roll": {
"darken": {
"enable": true,
"value": 25,
},
"enable": true,
"speed": {
"min": 15,
"max": 25,
},
},
},
"emitters": {
"life": {
"count": 0,
"duration": 0.1,
"delay": 0.4,
},
"rate": {
"delay": 0.1,
"quantity": 100,
},
"size": {
"width": 0,
"height": 0,
},
},
...options,
},
})
}
let stopConfettiTimeout: ReturnType<typeof setTimeout> | undefined
export function stopConfetti(duration: number = 2) {
if (! confettiContainer) {
console.info(`No confetti started`)
return
}
if (stopConfettiTimeout) {
console.info(`Confetti already stopping`)
return
}
confettiContainer.options.duration = duration
confettiContainer.options.emitters.rate.delay = duration
confettiContainer.options.particles.opacity = {
value: {
min: 0,
max: 1,
},
animation: {
enable: true,
// delay: 8,
speed: duration,
sync: true,
startValue: `max`,
count: 1,
destroy: `min`,
},
}
confettiContainer.refresh()
stopConfettiTimeout = setTimeout(() => {
if (! confettiContainer) {
return
}
confettiContainer.stop()
confettiContainer.destroy()
confettiContainer = undefined
stopConfettiTimeout = undefined
}, duration * 1000)
}