Creating Stunning 3D Instagram Story Swipes with React: A Step-by-Step Tutorial
source link: https://dev.to/brainiacneit/creating-stunning-3d-instagram-story-swipes-with-react-a-step-by-step-tutorial-5938
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
Introduction
Have you ever used Instagram? If so, chances are you've come across Instagram Stories - a feature that has become increasingly popular in recent years. As someone who enjoys using Instagram, I found the animations used in Instagram Stories to be particularly amazing, and I decided to try and recreate them myself. And guess what? I succeeded! In this tutorial, I'm going to show you how to create a 3D Instagram story swipe using React, a popular JavaScript library for building user interfaces. With my help, you'll be able to add a creative and unique touch to your Instagram Stories that will make them stand out from the crowd. Let's get started!
Setup
We won't use any library other than React for this demo ( we can write this in plain JavaScript too )
Understand CSS 3D
Before going into the code. I'll explain some CSS attributes that are important with 3D animations:
transform with transformZ and Y value: The
transform
property is used to apply transformations such as rotation, scaling, and translation to an element. When used in 3D, you can also usetransformZ
andtransformY
values to control the depth and height of the element respectively. For example,transform: translateZ(-50px)
will move the element 50 pixels away from the viewer, creating a sense of depth.transform-style with preserve-3d value: The
transform-style
property is used to specify whether child elements should be flattened or preserve their 3D position. When set topreserve-3d
, child elements will maintain their 3D positioning relative to the parent element. This is essential for creating complex 3D animations and designs.perspective: The
perspective
property is used to set the distance between the z=0 plane and the viewer in 3D space. This creates a sense of depth and perspective, making objects appear further away or closer to the viewer. For example,perspective: 1000px
will set the viewer's perspective 1000 pixels away from the element, creating a greater sense of depth.
The Logic
States and constants
const [displayStories, setDisplayStories] = useState(data);
const [imagePosition, setImagePosition] = useState(
new Map(data.map((i) => [i.id, 0]))
);
const [cellSize, setCellSize] = useState(480);
const [currentStory, setCurrentStory] = useState(data[0].id);
const hold = useRef(0);
const radiusRef = useRef(240 / Math.tan(Math.PI / 4));
const carouselRef = useRef<HTMLDivElement | null>(null);
const currentStoryRef = useRef(data[0].id);
const [radius, setRadius] = useState(240 / Math.tan(Math.PI / 4));
let isDown = false;
let current = 0;
let rotateYref = 0;
This code block defines several state variables and references that are used to manage the 3D carousel effect.
const [displayStories, setDisplayStories] = useState(data);
- This line defines a state variable called displayStories
that holds an array of story objects. The useState
hook is used to initialize the state with the data
parameter passed in.
const [imagePosition, setImagePosition] = useState(new Map(data.map((i) => [i.id, 0])));
- This line defines a state variable called imagePosition
that holds a Map object where each key is a story ID and the value is the index of the currently displayed image for that story. The useState
hook is used to initialize the state with a new Map object that is created from the data
parameter passed in.
const [cellSize, setCellSize] = useState(480);
- This line defines a state variable called cellSize
that holds the size of each carousel cell. The useState
hook is used to initialize the state with a value of 480.
const [currentStory, setCurrentStory] = useState(data[0].id);
- This line defines a state variable called currentStory
that holds the ID of the currently displayed story. The useState
hook is used to initialize the state with the ID of the first story in the data
array.
const hold = useRef(0);
- This line defines a reference called hold
that is used to store a timeout ID.
const radiusRef = useRef(240 / Math.tan(Math.PI / 4));
- This line defines a reference called radiusRef
that is used to store the radius of the carousel circle. The useRef
hook is used to initialize the reference with a value calculated from the angle of view of the camera.
const carouselRef = useRef<HTMLDivElement | null>(null);
- This line defines a reference called carouselRef
that is used to reference the carousel div
.
const currentStoryRef = useRef(data[0].id);
- This line defines a reference called currentStoryRef
that is used to reference the ID of the currently displayed story.
const [radius, setRadius] = useState(240 / Math.tan(Math.PI / 4));
- This line defines a state variable called radius
that holds the radius of the carousel circle. The useState
hook is used to initialize the state with a value calculated from the angle of view of the camera.
let isDown = false;
- This line defines a boolean variable called isDown
that is used to determine if the mouse is currently pressed down.
let current = 0;
- This line defines a numeric variable called current
that is used to keep track of the current rotation of the carousel.
let rotateYref = 0;
- This line defines a numeric variable called rotateYref
that is used to keep track of the current Y-rotation of the carousel.
The Events
There are some events that we need to listen when the component started render:
useEffect(() => {
const carousel = document.getElementById("carousel") || ({} as Element);
if (window.screen.width < 480) {
setCellSize(window.screen.width);
setRadius(window.screen.width / 2 / Math.tan(Math.PI / 4));
}
if (carousel) {
carousel.addEventListener("mousedown", start as (e: Event) => void);
carousel.addEventListener("touchstart", start as (e: Event) => void);
carousel.addEventListener("mousemove", move as (e: Event) => void);
carousel.addEventListener("touchmove", move as (e: Event) => void);
carousel.addEventListener("mouseleave", end);
carousel.addEventListener("mouseup", end);
carousel.addEventListener("touchend", end);
}
}, []);
The event listeners for "mousedown", "touchstart", "mousemove", "touchmove", "mouseleave", "mouseup", and "touchend" are added to the carousel element. These event listeners call the start
, move
, and end
functions which are defined elsewhere in the code. These functions are used to handle the user's interactions with the carousel, such as clicking, dragging, and releasing.
The functions
There are 5 main functions that I want to cover in this demo:
-
prevStory
: a function to go to the previous story -
nextStory
: a function to go to the next story -
start
: the function that would be triggered when we start click or touch the component -
move
: the function that would be triggered everytime we move the mouse or our finger -
end
: the function that would be trigger when we stop our animation
Let's go to the first 2 functions:
const prevStory = (currentStoryIndex: number) => {
setCurrentStory(data[currentStoryIndex - 1].id);
rotateYref = hold.current + 90;
if (carouselRef.current) {
carouselRef.current.style.transform = `translateZ(-${
radiusRef.current
}px) rotateY(${hold.current + 90}deg)`;
}
hold.current = hold.current + 90;
};
const nextStory = (currentStoryIndex: number) => {
setCurrentStory(data[currentStoryIndex + 1].id);
rotateYref = hold.current - 90;
if (carouselRef.current) {
carouselRef.current.style.transform = `translateZ(-${
radiusRef.current
}px) rotateY(${hold.current - 90}deg)`;
}
hold.current = hold.current - 90;
};
These two functions, prevStory
and nextStory
, are used to navigate to the previous and next story in the carousel, respectively. The functions take in a currentStoryIndex
parameter, which represents the index of the currently displayed story in the data
array.
When prevStory
is called, it sets the currentStory
state variable to the ID of the previous story in the data
array. It also updates the rotateYref
variable to the current angle of rotation plus 90 degrees, and updates the carouselRef
element's style to reflect the new angle of rotation. Finally, it updates the hold
reference to reflect the new angle of rotation.
When nextStory
is called, it sets the currentStory
state variable to the ID of the next story in the data
array. It also updates the rotateYref
variable to the current angle of rotation minus 90 degrees, and updates the carouselRef
element's style to reflect the new angle of rotation. Finally, it updates the hold
reference to reflect the new angle of rotation.
Animation functions
const end = () => {
isDown = false;
if (carouselRef.current) {
carouselRef.current.style.transition = "transform 0.25s";
}
const currentStoryIndex = getCurrentIndex(currentStoryRef);
if (rotateYref > hold.current && !isFirst(currentStoryIndex)) {
prevStory(currentStoryIndex);
return;
}
if (rotateYref < hold.current && !isLast(currentStoryIndex, data.length)) {
nextStory(currentStoryIndex);
return;
}
if (carouselRef.current) {
carouselRef.current.style.transform = `translateZ(-${radiusRef.current}px) rotateY(${hold.current}deg)`;
}
};
const start = (e: MouseEvent | TouchEvent) => {
isDown = true;
current = "pageX" in e ? e.pageX : e.touches[0].pageX;
};
const move = (e: MouseEvent | TouchEvent) => {
if (!isDown || !carouselRef.current) return;
e.preventDefault();
carouselRef.current.style.transition = "none";
const dist = "pageX" in e ? e.pageX : e.touches[0].pageX;
const threshHold = Math.abs(dist - current);
const wrap = 3.6666666;
if (dist >= current) {
rotateYref = hold.current + threshHold / wrap;
carouselRef.current.style.transform = `translateZ(-${
radiusRef.current
}px) rotateY(${hold.current + threshHold / wrap}deg)`;
} else {
rotateYref = hold.current - threshHold / wrap;
carouselRef.current.style.transform = `translateZ(-${
radiusRef.current
}px) rotateY(${hold.current - threshHold / wrap}deg)`;
}
}
These three functions are used to handle the user's interactions with the carousel element, such as clicking, dragging, and releasing.
end
- This function is called when the user releases the mouse or touch on the carousel element. It sets the isDown
variable to false
to indicate that the mouse is no longer pressed. It also adds a transition effect to the carousel element, sets the currentStoryIndex
to the current index of the displayed story, and checks if the carousel should rotate to the previous or next story, based on the rotateYref
and hold.current
variables. Finally, it updates the carouselRef
element's style to reflect the new angle of rotation.
start
- This function is called when the user clicks or touches the carousel element. It sets the isDown
to true
to indicate that the mouse is pressed, and sets the current
to the current X position of the mouse or touch.
move
- This function is called when the user moves the mouse or touch on the carousel element. It checks if the mouse is currently pressed down, and if so, it prevents the default behavior of the event. It also updates the rotateYref
and carouselRef
element's style to reflect the new angle of rotation based on the distance moved by the mouse or touch.
The User Interface
We will put all the code above into a React Hook, then we can use it in the component like this:
const {
displayStories,
imagePosition,
cellSize,
carouselRef,
radius,
} = useCarousel();
The most important part right now is to put the radius into each element like this:
<div
key={story.id}
className="image-full absolute"
style={{
transform: `rotateY(${
index * 90
}deg) translateZ(${radius}px)`,
display: "flex",
alignItems: "center",
justifyContent: "center",
fontSize: 60,
}}
>
We will rotate the Y axis with index multiply with 90 degree. So we will have a cube.
With the wrapper, we need to set transform-style: preserve-3d;
to it. And this will be the result:
Conclusion
To sum up, developing a 3D Instagram story carousel with React can significantly enhance the visual appeal of your Instagram Stories. By employing 3D CSS transformations and React, a revolving image carousel can be created with the appearance of depth and perspective. In this guide, we examined the essential features of 3D CSS, such as transform
, transform-style
, and perspective
. Additionally, we discussed the React component code that constructs a 3D Instagram Stories carousel and explained the interplay of various functions and state variables in delivering an interactive, captivating user experience. With a touch of inventiveness and understanding of 3D CSS and React, you can develop your very own striking 3D Instagram story carousel that will astonish your audience and make your Stories more prominent.
Source Code: https://github.com/superdev163/3d-instagram
Live Demo: https://instagram-3d-poc.web.app/
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK