tsparticles / tsparticles

tsParticles - 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://particles.js.org
MIT License
7.18k stars 798 forks source link

[Bug]: Responsive object not working in options object for ReactJS #5343

Closed SajawalHassan closed 3 weeks ago

SajawalHassan commented 3 weeks ago

Contact Details

sajawalhassan.1feb@gmail.com

What happened?

Using

Problem

The responsive object does not seem to work In my options, I want to disable interactivity below a screen width of 1024px, I am trying to accomplish this by adding a responsive array in the options but it has no effect.

I am loading the options object here (being passed as props) particles-background.tsx

"use client";

import { useEffect, useState } from "react";
import Particles, { initParticlesEngine } from "@tsparticles/react";
import { IOptions, RecursivePartial, type Container, type ISourceOptions } from "@tsparticles/engine";
import { loadSlim } from "@tsparticles/slim";
import { cn } from "@/lib/utils";

interface Props {
  options: RecursivePartial<IOptions>;
  className?: string;
}

export const ParticleBackground = ({ options, className }: Props) => {
  const [init, setInit] = useState(false);

  useEffect(() => {
    initParticlesEngine(async (engine) => {
      await loadSlim(engine);
    }).then(() => {
      setInit(true);
    });
  }, []);

  const particlesLoaded = async (container?: Container): Promise<void> => {};

  if (init) {
    return (
      <>
        <Particles id="tsparticles" particlesLoaded={particlesLoaded} options={options} className={cn(className)} />
      </>
    );
  }

  return <></>;
};

Expected results

Interactivity is disabled below a screen width of 1024px

Actual results

No effect is made to interactivity at all no matter the screen size

Tried

tsParticles Version

tsParticles Configuration

`particles-data.ts`

import type { ISourceOptions } from "@tsparticles/engine";

const theme = {
  particleColor: "#E44805",
  linkColor: "#B41817",
  zindex: {
    value: -50,
  },
  background: {
    color: "#0F0909",
  },
};

// Object containing code for the interactivity being enabled
const interactivityEnabled = {
  events: {
    onHover: {
      enable: true,
      mode: "grab",
      parallax: {
        enable: true,
        smooth: 10,
        force: 60,
      },
    },
    onClick: {
      enable: true,
      mode: "push",
    },
    onDiv: {
      enable: true,
      mode: "repulse",
    },
  },
  modes: {
    grab: {
      distance: 400,
      links: {
        opacity: 1,
      },
    },
    bubble: {
      distance: 400,
      size: 40,
      duration: 2,
      opacity: 0.8,
    },
    repulse: {
      distance: 200,
    },
    push: {
      quantity: 4,
    },
    remove: {
      quantity: 2,
    },
  },
};

// Object containing code for the interactivity being enabled
const interactivityDisabled = {
  events: {},
  modes: {},
};

// Actual options being passed to the Particles component
export const particlesConfig: ISourceOptions = {
  key: "parallax",
  name: "Parallax",
  responsive: [
    {
      maxWidth: 1024,
      mode: "canvas",
      options: {
        interactivity: interactivityEnabled,
      },
    },
  ],
  interactivity: interactivityDisabled,
  particles: {
    zIndex: {
      value: -50,
    },
    number: {
      value: 70,
      density: {
        enable: true,
      },
    },
    color: {
      value: theme.particleColor,
    },
    shape: {
      type: "circle",
    },
    opacity: {
      value: {
        min: 0.1,
        max: 0.5,
      },
      animation: {
        enable: true,
        speed: 3,
        sync: false,
      },
    },
    size: {
      value: {
        min: 1,
        max: 10,
      },
      animation: {
        enable: true,
        speed: 20,
        sync: false,
      },
    },
    links: {
      enable: true,
      distance: 150,
      color: theme.linkColor,
      opacity: 0.4,
      width: 1,
    },
    move: {
      enable: true,
      speed: 2,
    },
  },
  background: theme.background,
};

You can see in the `responsive` object I am defining a `maxLength` of 1024 pixels. I want the interactivity to be enabled after 1024 pixels and be disabled below 1024 pixels however no such effect is made.

What browsers are you seeing the problem on?

Firefox, Chrome, Safari, Microsoft Edge

Relevant log output

No response

Code of Conduct

SajawalHassan commented 3 weeks ago

I've found a workaround for now which is simply render two different components using javascript / typescript. Instead of using css to display none on the components (which doesn't work for some reason) I am rendering two entirely different components based on the window.innerWidth. Thought this might help!

"use client";

import { useEffect, useState } from "react";
import Particles, { initParticlesEngine } from "@tsparticles/react";
import { IOptions, RecursivePartial, type Container, type ISourceOptions } from "@tsparticles/engine";
import { loadSlim } from "@tsparticles/slim";
// import { loadFull } from "tsparticles";
import { cn } from "@/lib/utils";

interface Props {
  optionsMobile: RecursivePartial<IOptions>;
  optionsLaptop: RecursivePartial<IOptions>;
  className?: string;
}

export const ParticleBackground = ({ optionsMobile, optionsLaptop, className }: Props) => {
  const [init, setInit] = useState(false);

  useEffect(() => {
    initParticlesEngine(async (engine) => {
      await loadSlim(engine);
    }).then(() => {
      setInit(true);
    });
  }, []);

  const particlesLoaded = async (_container?: Container): Promise<void> => {};

  if (init) {
    return window.innerWidth > 1024 ? (
      <Particles id="tsparticles" particlesLoaded={particlesLoaded} options={optionsLaptop} className={cn(className)} />
    ) : (
      <Particles id="tsparticles" particlesLoaded={particlesLoaded} options={optionsMobile} className={cn(className)} />
    );
  }

  return <></>;
};
matteobruni commented 3 weeks ago

I've tested this in a vanilla environment, and it worked. I don't know if it's some issues with the React environment or Next.js but nice that you find a workaround.