At present we are going to check out the way to create an exquisite animation in React Three Fiber. The thought is to indicate a 3D scene within the background that appears like a distorted glassy view of a textual content ring, consistently rotating round its personal axis. It’s an exquisite design ingredient that might be used as an animated background. The animation is just not very complicated, excellent for these all for getting began with 3D animation and extra.
This can be a React particular tutorial so you’ll want to have an preliminary React app arrange in order that we are able to begin.
First we might want to set up React Three Fiber:
npm set up three @react-three/fiber
After that we’ll put together the canvas for rendering our 3D scene.
We first import the canvas from @react-three/fiber
, type the primary ingredient to be 100% viewport width and peak and use the imported Canvas element. We’ll additionally type it to have a black background.
import { Canvas } from "@react-three/fiber";
export default perform App() {
return (
<primary className="h-screen w-screen">
<Canvas className="bg-black">
</Canvas>
</primary>
);
}
The following step is to arrange our 3D element that might be rendered contained in the Canvas
which we are going to name ring as a result of we’ll be wrapping the textual content within the type of a hoop. The way in which we are able to think about that is as if we took our textual content and wrapped it round a bottle which might be forming this so known as “TextRing”.
interface Props {
radius: quantity;
peak: quantity;
segments: quantity;
}
export default perform Ring({ radius, peak, segments }: Props) {
return (
<mesh>
<cylinderGeometry args={[radius, radius, height, segments]} />
<meshBasicMaterial />
</mesh>
);
}
After you may have written the element the one remaining factor left to do is add it to the primary scene in our Canvas:
import { Canvas } from "@react-three/fiber";
import Ring from "./ring";
export default perform Scene() {
return (
<Canvas className="bg-black">
<Ring radius={2} peak={4} segments={32} />
</Canvas>
);
}
After this you need to be seeing this in your display:
Within the subsequent step we should embody our textual content within the scene and to be able to to that we’ll set up one other bundle known as @react-three/drei
that has a a lot of superior issues. On this case we use the Textual content element.
npm set up @react-three/drei
After we put in our bundle we’ve got to regulate our element to be able to present our textual content and wrap it round our cylinder:
import { Textual content } from "@react-three/drei";
interface Props {
textual content: string;
radius: quantity;
peak: quantity;
segments: quantity;
}
export default perform Ring({ textual content, radius, peak, segments }: Props) {
// Calculate positions for textual content
const textPositions: { x: quantity; z: quantity }[] = [];
const angleStep = (2 * Math.PI) / textual content.size;
for (let i = 0; i < textual content.size; i++) {
const angle = i * angleStep;
const x = radius * Math.cos(angle);
const z = radius * Math.sin(angle);
textPositions.push({ x, z });
}
return (
<group>
<mesh>
<cylinderGeometry args={[radius, radius, height, segments]} />
<meshBasicMaterial />
</mesh>
{textual content.cut up("").map((char: string, index: quantity) => (
<Textual content
key={index}
place={[textPositions[index].x, 0, textPositions[index].z]}
rotation={[0, -angleStep * index + Math.PI / 2, 0]}
fontSize={0.3}
lineHeight={1}
letterSpacing={0.02}
colour="white"
textAlign="middle"
>
{char}
</Textual content>
))}
</group>
);
}
So, there’s numerous issues occurring right here, however to clarify it in primary terminology, we take every character and more and more place it round in a round place. We use the radius that we used for the cylinder to place all of the letters.
We cross any textual content into the Ring element after this, after which will probably be neatly wrapper across the cylinder like this:
export default perform Scene() {
return (
<Canvas className="bg-black">
<Ring
radius={2}
peak={4}
segments={32}
textual content="X X X X X X X X X X X X X X X X X X X X X X X X X X X X "
/>
</Canvas>
);
}
To start out animating issues, we are going to merely take the group and rotate it alongside its x,y,z axis. For that we’ll use the useFrame
hook from R3F and we are going to entry the ingredient through the useRef
that we’ll hyperlink to the group ingredient.
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { Textual content } from "@react-three/drei";
interface Props {
textual content: string;
radius: quantity;
peak: quantity;
segments: quantity;
}
export default perform Ring({ textual content, radius, peak, segments }: Props) {
const ref = useRef<any>();
// Rotate the textual content
useFrame(() => {
ref.present.rotation.y += 0.01;
ref.present.rotation.x += 0.01;
ref.present.rotation.z += 0.01;
});
// Calculate positions for textual content
const textPositions: { x: quantity; z: quantity }[] = [];
const angleStep = (2 * Math.PI) / textual content.size;
for (let i = 0; i < textual content.size; i++) {
const angle = i * angleStep;
const x = radius * Math.cos(angle);
const z = radius * Math.sin(angle);
textPositions.push({ x, z });
}
return (
<group ref={ref}>
<mesh>
<cylinderGeometry args={[radius, radius, height, segments]} />
<meshBasicMaterial />
</mesh>
{textual content.cut up("").map((char: string, index: quantity) => (
<Textual content
key={index}
place={[textPositions[index].x, 0, textPositions[index].z]}
rotation={[0, -angleStep * index + Math.PI / 2, 0]}
fontSize={0.3}
lineHeight={1}
letterSpacing={0.02}
colour="white"
textAlign="middle"
>
{char}
</Textual content>
))}
</group>
);
}
The one remaining step left to do is to barely distort the ingredient a bit and make it glass-like trying. Fortunately, drei has a cloth that helps us with that known as MeshTransmissionMaterial
:
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { MeshTransmissionMaterial, Textual content } from "@react-three/drei";
interface Props {
textual content: string;
radius: quantity;
peak: quantity;
segments: quantity;
}
export default perform Ring({ textual content, radius, peak, segments }: Props) {
const ref = useRef<any>();
// Rotate the textual content
useFrame(() => {
ref.present.rotation.y += 0.01;
ref.present.rotation.x += 0.01;
ref.present.rotation.z += 0.01;
});
// Calculate positions for textual content
const textPositions: { x: quantity; z: quantity }[] = [];
const angleStep = (2 * Math.PI) / textual content.size;
for (let i = 0; i < textual content.size; i++) {
const angle = i * angleStep;
const x = radius * Math.cos(angle);
const z = radius * Math.sin(angle);
textPositions.push({ x, z });
}
return (
<group ref={ref}>
<mesh>
<cylinderGeometry args={[radius, radius, height, segments]} />
<MeshTransmissionMaterial
bottom
backsideThickness={5}
thickness={2}
/>
</mesh>
{textual content.cut up("").map((char: string, index: quantity) => (
<Textual content
key={index}
place={[textPositions[index].x, 0, textPositions[index].z]}
rotation={[0, -angleStep * index + Math.PI / 2, 0]}
fontSize={0.3}
lineHeight={1}
letterSpacing={0.02}
colour="white"
textAlign="middle"
>
{char}
</Textual content>
))}
</group>
);
}
And with that we’ve got our lovely rotating glassy textual content ring:
I hope you loved this tutorial!
I’m open for collaborations and initiatives, so drop me a line for those who’d prefer to work with me!