Inspiration
Ever thought of creating an image cube?π€ Well, I didn't until I was scrolling through a website one day, and I came across this ad:
It wasn't my first time seeing this ad, but that day, it caught my attention. funny enough, I happened to be working on a project at that time where I needed to implement an image slideshow in a lightbox, and I thought to myself, how cool would it be to have the images displayed in a cube-like shape just like this, rather than the plain ol slideshows we're used to? I wasn't quite sure on how I was going to implement it, but after some hours of trial and error, I eventually came up with thisπ:
I call it the Cube-Showπ€. You can check it out on the live website
The most challenging part of this project was timing the cube rotation perfectly to update the images on each side of the cube and how to keep the cube rotation stable when the cube is rotated forward or backward.
Interested in how I was able to implement this? Well, let's find out then. To get started, we simply need to create three files which are the HTML, CSS, and JS files.
Setting up the HTML
The first thing we need to do is declare the cube structure in the HTML file.
<div id="container">
<ion-icon name="chevron-back" id="back-icon"></ion-icon>
<div id="cube">
<div class="side front"><img src="https://source.unsplash.com/500x500?car" alt="front" class="side-img" /></div>
<div class="side left"><img src="https://source.unsplash.com/500x500?lake" alt="left" class="side-img" /></div>
<div class="side back"><img src="https://source.unsplash.com/500x500?man" alt="back" class="side-img" /></div>
<div class="side right"><img src="https://source.unsplash.com/500x500?dog" alt="right" class="side-img" /></div>
</div>
<ion-icon name="chevron-forward" id="forward-icon"></ion-icon>
</div>
In the code above, we declared a container
div; inside that div, we have 3 elements: the left icon to rotate the cube backward, another div element for the cube, and the right icon to rotate the cube forward. Inside the cube
div, we have 4 more divs for the sides of the cube, and in each of them, we have an image tag to display the images.
That is all we need in our HTML file. The real work is yet to comeπ¬. Next up, let's style the elements.
Styling with CSS
Inside our CSS file, we first need to declare the general page styles as follows:
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: grid;
place-items: center;
padding: 5px;
}
Next, we will style the container div and the back and forward icons.
#container {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
}
ion-icon {
font-size: 50px;
cursor: pointer;
z-index: 100;
}
ion-icon:hover {
color: brown;
}
Moving on, let's style the cube itself, along with the forward and backward animation that we will apply to the cube to rotate it either left or right.
#cube {
width: min(55vw, 500px);
aspect-ratio: 1;
margin: 40px;
border-radius: 15px;
position: relative;
transform-style: preserve-3d;
animation: turnRight 30s linear infinite;
cursor: pointer;
}
@keyframes turnRight {
0% {
transform: rotateX(5deg) rotateY(0deg);
}
100% {
transform: rotateX(5deg) rotateY(360deg);
}
}
@keyframes turnLeft {
0% {
transform: rotateX(5deg) rotateY(360deg);
}
100% {
transform: rotateX(5deg) rotateY(0deg);
}
}
Now, let's style the four sides of the cube along with the img
tags in them.
.side {
position: absolute;
width: 100%;
height: 100%;
border: 1px solid black;
text-align: center;
line-height: 200px;
border-radius: 15px;
-webkit-box-reflect: below 40px linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.2));
}
.side-img {
width: 100%;
height: 100%;
object-fit: cover;
}
Now, this is a very important step in achieving the cube-like effect. We need to calculate the rotate and translate angle of each side of the cube according to the width of the cube itself. This also ensures the sides do not get larger or smaller than the cube when viewed in different screen sizes.
.front {
transform: translateZ(calc(min(55vw, 500px) / 2));
}
.back {
transform: translateZ(calc(-1 * min(55vw, 500px) / 2)) rotateY(180deg);
}
.right {
transform: rotateY(90deg) translateZ(calc(min(55vw, 500px) / 2));
}
.left {
transform: rotateY(-90deg) translateZ(calc(min(55vw, 500px) / 2));
}
That is a wrap for our CSS file. We now have a nice rotating cube with static images. Now, let's move into our JavaScript file to add functionality and dynamic image viewing to the cube.
Adding functionality with JavaScript
To get started in our JS file, we first need to get all the HTML elements we will be working with, declare an array containing the source of the images we will be updating the cube with. and declare a few variables that we will be making use of as we move forward.
const cube = document.getElementById("cube");
const leftButton = document.getElementById("back-icon");
const rightButton = document.getElementById("forward-icon");
const cubeSides = document.querySelectorAll(".side-img");
let imagesFilePath = [
"https://source.unsplash.com/500x500?car",
"https://source.unsplash.com/500x500?lake",
"https://source.unsplash.com/500x500?man",
"https://source.unsplash.com/500x500?dog",
"https://source.unsplash.com/500x500?pond",
"https://source.unsplash.com/500x500?city",
"https://source.unsplash.com/500x500?trees",
"https://source.unsplash.com/500x500?volcano",
"https://source.unsplash.com/500x500?ice",
"https://source.unsplash.com/500x500?cat",
"https://source.unsplash.com/500x500?flowers",
"https://source.unsplash.com/500x500?koala",
"https://source.unsplash.com/500x500?bird",
"https://source.unsplash.com/500x500?girl",
"https://source.unsplash.com/500x500?valley"
];
let imgtag = 0;
let srcIndex = 4;
let intervalID;
let isAnimating = true;
Next, we'll create two functions to update each side of the cube with images sequentially every 7.6 seconds. We calculate the interval duration by dividing the CSS animation duration by 4, ensuring all sides are updated before the animation completes.
// Moves through the image source array forwards and updates the cube's next side
function rotateForwards() {
clearInterval(intervalID); // Clear previous interval
intervalID = setInterval(() => {
cubeSides[imgtag].src = imagesFilePath[srcIndex];
imgtag = (imgtag + 1) % cubeSides.length;
srcIndex = (srcIndex + 1) % imagesFilePath.length;
}, 7500);
}
rotateForwards();
// Moves through the image source array backwards and updates the cube's previous side
function rotateBackwards() {
clearInterval(intervalID); // Clear previous interval
intervalID = setInterval(() => {
imgtag = (imgtag - 1 + cubeSides.length) % cubeSides.length;
srcIndex = (srcIndex - 1 + imagesFilePath.length) % imagesFilePath.length;
cubeSides[imgtag].src = imagesFilePath[srcIndex];
}, 7500);
}
After that, we need to attach the functions we just created to the left and right arrow buttons, respectively, and set the cube animation style to turn either left or right.
leftButton.onclick = () => {
cube.style.animation = "turnLeft 30s linear infinite";
rotateBackwards();
};
rightButton.onclick = () => {
cube.style.animation = "turnRight 30s linear infinite";
rotateForwards();
};
Now, we want to be able to pause the cube rotation so the user can view a photo for longer. To do that, we attach an onclick
event to the cube and check if the animation is already running. If it is, we pause it, and if it's not running, we resume the cube rotation.
cube.onclick = (e) => {
if (isAnimating) {
// If animation is running, pause it
clearInterval(intervalID);
cube.style.animationPlayState = "paused";
isAnimating = false;
} else {
// If animation is paused, resume it
rotateForwards();
cube.style.animationPlayState = "running";
isAnimating = true;
}
};
And with that ladies and gentlemen, we have successfully created the amazing image cube-showπ€. Check out the complete source code of this project on GitHub
Let me know what you guys think. Feedback is highly welcomeπ€β¨, and please do not hesitate to ask me any questions or point out any concerns. Till then, happy coding guys!β¨β
Top comments (8)
Brilliant!
Thank you for sharing.
You're welcome :)
This is awesome, thank you. Will try to add it to one of my sites today. :)
:) Glad you found it awesome π₯
FYI integrated easily into my Astro site, looks great, thanks.
That's awesome πͺπ₯π₯π«. If the site is live, I would love to take a look at it π
Sure Alpha Tree Service. Frontend Astro on cloudflare, backend Rust and sqlite to persist comments on google compute engine.
It looks really great π€©. Although when viewed on mobile screens, the cube sides does have gaps between them. To fix this, simple make sure the
translate
value of each side is exactly half of the cube width and set a margin between the cube and the arrow icons to prevent it from over lapping as it turns