DEV Community

Cover image for Image slideshows are boring. Let's create cubes.
sahra πŸ’«
sahra πŸ’«

Posted on

Image slideshows are boring. Let's create cubes.

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:

cube-show inspo

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😁:

image cube-show demo

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>
Enter fullscreen mode Exit fullscreen mode

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;
  }
Enter fullscreen mode Exit fullscreen mode

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;
  }
Enter fullscreen mode Exit fullscreen mode

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);
    }
  }
Enter fullscreen mode Exit fullscreen mode

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;
  }
Enter fullscreen mode Exit fullscreen mode

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));
  }
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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); 
}
Enter fullscreen mode Exit fullscreen mode

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();
};
Enter fullscreen mode Exit fullscreen mode

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;
  }
};
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
dtechies profile image
Roopesh Jain

Brilliant!
Thank you for sharing.

Collapse
 
sarahokolo profile image
sahra πŸ’«

You're welcome :)

Collapse
 
cjsmocjsmo profile image
Charlie J Smotherman

This is awesome, thank you. Will try to add it to one of my sites today. :)

Collapse
 
sarahokolo profile image
sahra πŸ’«

:) Glad you found it awesome πŸ’₯

Collapse
 
cjsmocjsmo profile image
Charlie J Smotherman

FYI integrated easily into my Astro site, looks great, thanks.

Thread Thread
 
sarahokolo profile image
sahra πŸ’«

That's awesome πŸ’ͺπŸ’₯πŸ’₯πŸ’«. If the site is live, I would love to take a look at it 😊

Thread Thread
 
cjsmocjsmo profile image
Charlie J Smotherman

Sure Alpha Tree Service. Frontend Astro on cloudflare, backend Rust and sqlite to persist comments on google compute engine.

Thread Thread
 
sarahokolo profile image
sahra πŸ’« • Edited

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