From 4f2b2b5a931e5d1b71bc8f642c9a968cd792134b Mon Sep 17 00:00:00 2001 From: Romain Date: Fri, 4 Aug 2023 12:29:46 +0200 Subject: [PATCH] perf: improve performance using confetti --- src/Components/Confetti.jsx | 131 +++++++++++++++++++++--------------- src/Experience.jsx | 3 +- 2 files changed, 78 insertions(+), 56 deletions(-) diff --git a/src/Components/Confetti.jsx b/src/Components/Confetti.jsx index 982de25..5b7c892 100644 --- a/src/Components/Confetti.jsx +++ b/src/Components/Confetti.jsx @@ -1,53 +1,73 @@ -// CONFETTI COMPONENT BY ANDERSON MANCINI FIXED BY RHERAULT -// Thanks <3 +// CONFETTI COMPONENT BY ANDERSON MANCINI AND ROMAIN HERAULT +// Based on: https://github.com/JamesChan21/threejs-confetti +// Based on: https://github.com/daniel-lundin/dom-confetti -import React, { useRef } from 'react' +import React, { useRef, useState, useEffect } from 'react' import { useFrame } from '@react-three/fiber' import * as THREE from 'three' -import { useState } from 'react' -export default function ExplosionConfetti({ isExploding }) { +/** + * @param {Object} options + * @param {Boolean | undefined} options.isExploding Enable exploding + * @param {Number | undefined} options.amount The amount of particles + * @param {Number | undefined} options.rate Increases or decreases the frequency for particles. Don't set it too high. + * @param {Number | undefined} options.radius The radius of each explosion. + * @param {Number | undefined} options.areaWidth The qrea width for explosion. + * @param {Number | undefined} options.areaHeight The qrea height for explosion. + * @param {Number | undefined} options.fallingHeight Height for the particles to fall from + * @param {Number | undefined} options.fallingSpeed The speed of particles + * @param {(Number)[] | undefined} options.colors Array of Hex color codes for particles. Example: [0x0000ff, 0xff0000, 0xffff00] + * @param {Number | String | undefined} options.duration Duration of the particles in Milliseconds. Set as 'forever' string for infinity explosion + * @param {Boolean | undefined} options.enableShadows Enable particle shadows. Set false for better performance. + * + */ + +export default function ExplosionConfetti( + { + isExploding = false, + amount = 100, + rate = 3, + radius = 15, + areaWidth = 3, + areaHeight = 1, + fallingHeight = 10, + fallingSpeed = 8, + colors = ["#a864fd", "#29cdff", "#78ff44", "#ff718d", "#fdff6a"], + duration = 10000, + enableShadows = false + }, + props +) { const groupRef = useRef() const [booms, setBooms] = useState([]) - const options = { - amount: 200, - rate: 2, - radius: 15, - areaWidth: 5, - areaHeight: 3, - fallingHeight: 6, - fallingSpeed: 8, - colors: ["#a864fd", "#29cdff", "#78ff44", "#ff718d", "#fdff6a"] - } - - options.rate = options.rate / 100 + rate = rate / 100 const geometry = new THREE.PlaneGeometry(0.03, 0.03, 1, 1) - const explode = () => { + function explode() { const boom = new THREE.Object3D() boom.life = Math.random() * 5 + 5 - boom.position.x = -(options.areaWidth / 2) + options.areaWidth * Math.random() - boom.position.y = options.fallingHeight + options.areaHeight - options.fallingSpeed - boom.position.z = -(options.areaWidth / 2) + options.areaWidth * Math.random() + boom.position.x = -(areaWidth / 2) + areaWidth * Math.random() + boom.position.y = fallingHeight + areaHeight - fallingSpeed + boom.position.z = -(areaWidth / 2) + areaWidth * Math.random() groupRef.current.add(boom) booms.push(boom) - for (let i = 0; i < options.amount; i++) { + for (let i = 0; i < amount; i++) { const material = new THREE.MeshBasicMaterial({ - color: options.colors[Math.floor(Math.random() * options.colors.length)], + color: colors[Math.floor(Math.random() * colors.length)], side: THREE.DoubleSide }) const particle = new THREE.Mesh(geometry, material) - particle.castShadow = false + particle.castShadow = enableShadows boom.add(particle) particle.life = 1 particle.destination = {} - particle.destination.x = (Math.random() - 0.5) * (options.radius * 2) * Math.random() - particle.destination.y = (Math.random() - 0.5) * (options.radius * 2) * Math.random() - particle.destination.z = (Math.random() - 0.5) * (options.radius * 2) * Math.random() + particle.destination.x = (Math.random() - 0.5) * (radius * 2) * Math.random() + particle.destination.y = (Math.random() - 0.5) * (radius * 2) * Math.random() + particle.destination.z = (Math.random() - 0.5) * (radius * 2) * Math.random() particle.rotation.x = Math.random() * 360 particle.rotation.y = Math.random() * 360 @@ -61,7 +81,7 @@ export default function ExplosionConfetti({ isExploding }) { particle.rotateSpeedZ = Math.random() * 0.8 - 0.4 } - boom.dispose = () => { + boom.dispose = function () { for (let i = 0; i < boom.children.length; i++) { const particle = boom.children[i] particle.material.dispose() @@ -73,43 +93,46 @@ export default function ExplosionConfetti({ isExploding }) { } useFrame(() => { - if (isExploding && Math.random() < options.rate) explode() + if (isExploding && Math.random() < rate) explode() + + let particleAmount = 0 for (let i = 0; i < booms.length; i++) { - const boom = booms[i] + const boom = booms[i] - for (let k = 0; k < boom.children.length; k++) { - let particle = boom.children[k] + for (let k = 0; k < boom.children.length; k++) { + let particle = boom.children[k] - particle.destination.y -= THREE.MathUtils.randFloat(0.1, 0.3) - particle.life -= THREE.MathUtils.randFloat(0.005, 0.01) + particle.destination.y -= THREE.MathUtils.randFloat(0.1, 0.3) + particle.life -= THREE.MathUtils.randFloat(0.005, 0.01) - const speedX = (particle.destination.x - particle.position.x) / 200 - const speedY = (particle.destination.y - particle.position.y) / 200 - const speedZ = (particle.destination.z - particle.position.z) / 200 + const speedX = (particle.destination.x - particle.position.x) / 200 + const speedY = (particle.destination.y - particle.position.y) / 200 + const speedZ = (particle.destination.z - particle.position.z) / 200 - particle.position.x += speedX - particle.position.y += speedY - particle.position.z += speedZ + particle.position.x += speedX + particle.position.y += speedY + particle.position.z += speedZ - particle.rotation.y += particle.rotateSpeedY - particle.rotation.x += particle.rotateSpeedX - particle.rotation.z += particle.rotateSpeedZ + particle.rotation.y += particle.rotateSpeedY + particle.rotation.x += particle.rotateSpeedX + particle.rotation.z += particle.rotateSpeedZ - particle.material.opacity -= THREE.MathUtils.randFloat(0.005, 0.01) + particle.material.opacity -= THREE.MathUtils.randFloat(0.005, 0.01) - if (particle.position.y < -options.fallingHeight) { - particle.material.dispose() - particle.geometry.dispose() - boom.remove(particle) - particle = null - } + if (particle.position.y < -fallingHeight) { + particle.material.dispose() + particle.geometry.dispose() + boom.remove(particle) + particle = null } + } - if (boom.children.length <= 0) { - boom.dispose() - setBooms(booms.filter((b) => b !== boom)) - } + if (boom.children.length <= 0) { + boom.dispose() + setBooms(booms.filter((b) => b !== boom)) + } + particleAmount += boom.children.length } }) diff --git a/src/Experience.jsx b/src/Experience.jsx index 0205840..3ea1e10 100644 --- a/src/Experience.jsx +++ b/src/Experience.jsx @@ -48,10 +48,9 @@ const Experience = () => { // rotation={[0, -Math.PI / 8, 0]} azimuth={[-Math.PI / 2, Math.PI / 2]} > - }> - +