Lexington has been awarded a grant from Astro, to celebrate. Get a 30% discount. Apply code LEXINGTON30 ( uppercase ) at checkout.
Hello everyone, today we are going to create a simple particle text effect with Tailwind CSS and JavaScript. This effect is great for creating a fun and playful animation.
A particle text effect is an animation where individual particles move and come together to form text. These particles can start off scattered randomly across a container and gradually reform into specific characters, giving a dynamic visual impact. This technique often involves JavaScript to control particle movement and a framework like Tailwind CSS for styling the particles and text.
ID’s
id="text-container"
: This line of code will define the id of the container. This id will be used to target the container in the JavaScript code.
Classesh-96
: This class will make the container element have a height of 96 pixels.lg:h-32
: This class will make the container element have a height of 32 pixels on screens with a width of 1024 pixels or greater.We need to add a height to the container so that the particles can be positioned correctly.
flex
: This class will make the container element use flexbox.items-center
: This class will center the container element vertically.<div
id="text-container"
class="h-96 lg:h-32 flex items-center">
</div>
Parameters
containerId
: This parameter will be used to get the container element with the id of “text-container” from the document.initialText
: This parameter will be used to set the initial text of the particles.finalText
: This parameter will be used to set the final text of the particles.particleCount
: This parameter will be used to set the number of particles.
Return Valuethis.container
: This line of code will return the container element with the id of “text-container” from the document.this.initialText
: This line of code will return the initial text of the particles.this.finalText
: This line of code will return the final text of the particles.this.particleCount
: This line of code will return the number of particles.this.particles
: This line of code will return an empty array.this.animationFrame
: This line of code will return null
.
Initializationthis.init()
: This line of code will call the init
function.this.createParticles()
: This line of code will call the createParticles
function.this.addEventListeners()
: This line of code will call the addEventListeners
function.this.startAnimation()
: This line of code will call the startAnimation
function.constructor(containerId, initialText, finalText, particleCount = 500) {
this.container = document.getElementById(containerId);
this.initialText = initialText;
this.finalText = finalText;
this.particleCount = particleCount;
this.particles = [];
this.animationFrame = null;
this.init();
}
init() {
this.createParticles();
this.addEventListeners();
this.startAnimation();
}
Variables
const fragment
: This line of code will define a variable called fragment
and set its value to document.createDocumentFragment()
.const containerRect
: This line of code will define a variable called containerRect
and set its value to this.container.getBoundingClientRect()
.Loop
for (let i = 0; i < this.particleCount; i++)
: This line of code will create a loop that will iterate this.particleCount
times.
Variablesconst particle
: This line of code will define a variable called particle
and set its value to document.createElement("span")
.particle.textContent
: This line of code will set the textContent
of the particle
element to this.getRandomChar()
.particle.className
: This line of code will set the className
of the particle
element to "absolute opacity-0 transition-all duration-1000 ease-out"
.fragment.appendChild(particle)
: This line of code will append the particle
element to the fragment
element.this.particles.push({ element: particle, x: Math.random() * containerRect.width, y: Math.random() * containerRect.height, speedX: Math.random() * 2 - 1, speedY: Math.random() * 2 - 1 });
: This line of code will push an object with the element
property set to the particle
element, the x
property set to a random number between 0 and the width of the containerRect
, the y
property set to a random number between 0 and the height of the containerRect
, the speedX
property set to a random number between -1 and 1, and the speedY
property set to a random number between -1 and 1.this.container.appendChild(fragment);
: This line of code will append the fragment
element to the this.container
element.createParticles() {
const fragment = document.createDocumentFragment();
const containerRect = this.container.getBoundingClientRect();
for (let i = 0; i < this.particleCount; i++) {
const particle = document.createElement("span");
particle.textContent = this.getRandomChar();
particle.className =
"absolute opacity-0 transition-all duration-1000 ease-out";
fragment.appendChild(particle);
this.particles.push({
element: particle,
x: Math.random() * containerRect.width,
y: Math.random() * containerRect.height,
speedX: Math.random() * 2 - 1,
speedY: Math.random() * 2 - 1,
});
}
this.container.appendChild(fragment);
}
getRandomChar()
: This line of code will define a function called getRandomChar
that will be called when the createParticles
function is called.
Return Valuereturn this.initialText[Math.floor(Math.random() * this.initialText.length)]
: This line of code will return a random character from the this.initialText
array.getRandomChar() {
return this.initialText[
Math.floor(Math.random() * this.initialText.length)
];
}
Variables
const containerRect
: This line of code will define a variable called containerRect
and set its value to this.container.getBoundingClientRect()
.
Loopthis.particles.forEach((particle) => {
: This line of code will iterate over each particle in the this.particles
array and pass the particle to the anonymous function.particle.x += particle.speedX;
: This line of code will add the speedX
property of the particle
object to the x
property of the particle
object.particle.y += particle.speedY;
: This line of code will add the speedY
property of the particle
object to the y
property of the particle
object.particle.x < 0 || particle.x > containerRect.width
: This line of code will check if the x
property of the particle
object is less than 0 or greater than the width of the containerRect
.particle.y < 0 || particle.y > containerRect.height
: This line of code will check if the y
property of the particle
object is less than 0 or greater than the height of the containerRect
.Object.assign(particle.element.style, { transform:
translate(${particle.x}px, ${particle.y}px), opacity: "1" });
: This line of code will assign the transform
and opacity
styles to the particle
element.
Return Valuethis.animationFrame = requestAnimationFrame(this.animateParticles.bind(this));
: This line of code will set the animationFrame
property of the this
object to the result of calling the requestAnimationFrame
function with the animateParticles
function as an argument.animateParticles() {
const containerRect = this.container.getBoundingClientRect();
this.particles.forEach((particle) => {
particle.x += particle.speedX;
particle.y += particle.speedY;
if (particle.x < 0 || particle.x > containerRect.width)
particle.speedX *= -1;
if (particle.y < 0 || particle.y > containerRect.height)
particle.speedY *= -1;
Object.assign(particle.element.style, {
transform: `translate(${particle.x}px, ${particle.y}px)`,
opacity: "1",
});
});
this.animationFrame = requestAnimationFrame(
this.animateParticles.bind(this)
);
}
Variables
const containerRect
: This line of code will define a variable called containerRect
and set its value to this.container.getBoundingClientRect()
.const fontSize
: This line of code will define a variable called fontSize
and set its value to window.innerWidth < 1024 ? 16 : 54
. This line of code will check if the window.innerWidth
is less than 1024 pixels and if so, it will set the fontSize
to 16.const letterSpacing
: This line of code will define a variable called letterSpacing
and set its value to fontSize * 0 .8
. This line of code will multiply the fontSize
by 0.8 to get the letterSpacing
.const textWidth
: This line of code will define a variable called textWidth
and set its value to this.finalText.length * letterSpacing
.startX
: This line of code will define a variable called startX
and set its value to (containerRect.width - textWidth) / 2
. This line of code will calculate the startX
by subtracting the textWidth
from the containerRect.width
and then dividing by 2.startY
: This line of code will define a variable called startY
and set its value to containerRect.height / 2
. This line of code will calculate the startY
by dividing the containerRect.height
by 2.
Loopthis.particles.forEach((particle, index) => {
: This line of code will iterate over each particle in the this.particles
array and pass the particle and the index to the anonymous function.if (index < this.finalText.length) {
: This line of code will check if the index
is less than the length of the this.finalText
array.const targetX = startX + index * letterSpacing;
: This line of code will define a variable called targetX
and set its value to startX + index * letterSpacing
.const targetY = startY + (Math.random() - 0.5) * (fontSize / 2);
: This line of code will define a variable called targetY
and set its value to startY + (Math.random() - 0.5) * (fontSize / 2)
.Object.assign(particle.element.style, { transform:
translate(${targetX}px, ${targetY}px), opacity: "1" });
: This line of code will assign the transform
and opacity
styles to the particle
element.particle.element.textContent = this.finalText[index];
: This line of code will set the textContent
of the particle
element to the this.finalText[index]
.} else {
: This line of code will close the if statement if index
is less than the length of the this.finalText
array.particle.element.style.opacity = "0";
: This line of code will set the opacity
style of the particle
element to “0”.reformText() {
const containerRect = this.container.getBoundingClientRect();
const fontSize = window.innerWidth < 1024 ? 16 : 54;
const letterSpacing = fontSize * 0.8;
const textWidth = this.finalText.length * letterSpacing;
const startX = (containerRect.width - textWidth) / 2;
const startY = containerRect.height / 2;
this.particles.forEach((particle, index) => {
if (index < this.finalText.length) {
const targetX = startX + index * letterSpacing;
const targetY = startY + (Math.random() - 0.5) * (fontSize / 2);
Object.assign(particle.element.style, {
transform: `translate(${targetX}px, ${targetY}px)`,
opacity: "1",
});
particle.element.textContent = this.finalText[index];
} else {
particle.element.style.opacity = "0";
}
});
}
cancelAnimationFrame(this.animationFrame);
: This line of code will cancel the animationFrame
property of the this
object.this.createParticles();
: This line of code will call the createParticles
function.this.animateParticles();
: This line of code will call the animateParticles
function.setTimeout(() => {
: This line of code will use the setTimeout
function to delay the execution of the following code by 2000 milliseconds.cancelAnimationFrame(this.animationFrame);
: This line of code will cancel the animationFrame
property of the this
object.this.reformText();
: This line of code will call the reformText
function.}, 2000);
: This line of code will close the setTimeout
function.startAnimation() {
cancelAnimationFrame(this.animationFrame);
this.createParticles();
this.animateParticles();
setTimeout(() => {
cancelAnimationFrame(this.animationFrame);
this.reformText();
}, 2000);
}
const containerRect = this.container.getBoundingClientRect();
: This line of code will define a variable called containerRect
and set its value to this.container.getBoundingClientRect()
.this.particles.forEach((particle) => {
: This line of code will iterate over each particle in the this.particles
array and pass the particle to the anonymous function.particle.x = Math.random() * containerRect.width;
: This line of code will set the x
property of the particle
object to a random number between 0 and the width of the containerRect
.particle.y = Math.random() * containerRect.height;
: This line of code will set the y
property of the particle
object to a random number between 0 and the height of the containerRect
.cancelAnimationFrame(this.animationFrame);
: This line of code will cancel the animationFrame
property of the this
object.this.animateParticles();
: This line of code will call the animateParticles
function. handleResize() {
const containerRect = this.container.getBoundingClientRect();
this.particles.forEach((particle) => {
particle.x = Math.random() * containerRect.width;
particle.y = Math.random() * containerRect.height;
});
cancelAnimationFrame(this.animationFrame);
this.animateParticles();
}
this.container.addEventListener("click", this.startAnimation.bind(this));
: This line of code will add a click event listener to the this.container
element that will call the startAnimation
function when the element is clicked.window.addEventListener("resize", this.handleResize.bind(this));
: This line of code will add a resize event listener to the window
object that will call the handleResize
function when the window is resized. addEventListeners() {
this.container.addEventListener("click", this.startAnimation.bind(this));
window.addEventListener("resize", this.handleResize.bind(this));
}
new ParticleTextAnimation("text-container", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", "HELLO EVERYONE");
: This line of code will create a new instance of the ParticleTextAnimation
class with the text-container
id, the ABCDEFGHIJKLMNOPQRSTUVWXYZ
initial text, and the HELLO EVERYONE
final text.document.addEventListener("DOMContentLoaded", () => {
new ParticleTextAnimation(
"text-container",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"HELLO EVERYONE"
);
});
This is how the full script will look like when you’re done.
class ParticleTextAnimation {
constructor(containerId, initialText, finalText, particleCount = 500) {
this.container = document.getElementById(containerId);
this.initialText = initialText;
this.finalText = finalText;
this.particleCount = particleCount;
this.particles = [];
this.animationFrame = null;
this.init();
}
init() {
this.createParticles();
this.addEventListeners();
this.startAnimation();
}
createParticles() {
const fragment = document.createDocumentFragment();
const containerRect = this.container.getBoundingClientRect();
for (let i = 0; i < this.particleCount; i++) {
const particle = document.createElement("span");
particle.textContent = this.getRandomChar();
particle.className =
"absolute opacity-0 transition-all duration-1000 ease-out";
fragment.appendChild(particle);
this.particles.push({
element: particle,
x: Math.random() * containerRect.width,
y: Math.random() * containerRect.height,
speedX: Math.random() * 2 - 1,
speedY: Math.random() * 2 - 1,
});
}
this.container.appendChild(fragment);
}
getRandomChar() {
return this.initialText[
Math.floor(Math.random() * this.initialText.length)
];
}
animateParticles() {
const containerRect = this.container.getBoundingClientRect();
this.particles.forEach((particle) => {
particle.x += particle.speedX;
particle.y += particle.speedY;
if (particle.x < 0 || particle.x > containerRect.width)
particle.speedX *= -1;
if (particle.y < 0 || particle.y > containerRect.height)
particle.speedY *= -1;
Object.assign(particle.element.style, {
transform: `translate(${particle.x}px, ${particle.y}px)`,
opacity: "1",
});
});
this.animationFrame = requestAnimationFrame(
this.animateParticles.bind(this)
);
}
reformText() {
const containerRect = this.container.getBoundingClientRect();
const fontSize = window.innerWidth < 1024 ? 16 : 54;
const letterSpacing = fontSize * 0.8;
const textWidth = this.finalText.length * letterSpacing;
const startX = (containerRect.width - textWidth) / 2;
const startY = containerRect.height / 2;
this.particles.forEach((particle, index) => {
if (index < this.finalText.length) {
const targetX = startX + index * letterSpacing;
const targetY = startY + (Math.random() - 0.5) * (fontSize / 2);
Object.assign(particle.element.style, {
transform: `translate(${targetX}px, ${targetY}px)`,
opacity: "1",
});
particle.element.textContent = this.finalText[index];
} else {
particle.element.style.opacity = "0";
}
});
}
startAnimation() {
cancelAnimationFrame(this.animationFrame);
this.createParticles();
this.animateParticles();
setTimeout(() => {
cancelAnimationFrame(this.animationFrame);
this.reformText();
}, 2000);
}
handleResize() {
const containerRect = this.container.getBoundingClientRect();
this.particles.forEach((particle) => {
particle.x = Math.random() * containerRect.width;
particle.y = Math.random() * containerRect.height;
});
cancelAnimationFrame(this.animationFrame);
this.animateParticles();
}
addEventListeners() {
this.container.addEventListener("click", this.startAnimation.bind(this));
window.addEventListener("resize", this.handleResize.bind(this));
}
}
document.addEventListener("DOMContentLoaded", () => {
new ParticleTextAnimation(
"text-container",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"HELLO EVERYONE"
);
});
This is a simple particle text effect that you can recreate with Tailwind CSS and JavaScript. You can customize the colors, number of particles, and text to your liking.
Hope you enjoyed this tutorial and have a good day!
/Michael Andreuzza
Get lifetime access to every theme available today for $199 and own
them forever. Plus, new themes, lifetime updates, use on unlimited
projects and enjoy lifetime support.
— No subscription required!