CSS 3D Slider

Let’s write image sliders in pure HTML and CSS. We only change the CSS, the markup in the HTML remains unchanged. The appearance due to different CSS is strikingly different, and an unlimited number of pictures can be inserted into the sliders. First, we created a circle slider with infinite rotation, similar to an image spinner widget. Then we made a slider that scrolls through a stack of photos. Continuation – to the start of the course on Fullstack-development in Python.

Now let’s dive into the third dimension. It may seem complicated at first, but some of the code in this tutorial is copied from the first two articles with minor changes. If you haven’t read the previous articles, I encourage you to read them to better understand the concepts we use.

At first glance, it seems that a rotating cube with four pictures-faces is presented. In fact, there are six sides. Look at the same slider from a different angle:

Now that the location of the pictures is well understood, let’s start writing the code.

basic settings

Let’s start with the HTML code we’ve used for other sliders:

<div class="gallery">
  <img src="" alt="">
  <img src="" alt="">
  <img src="" alt="">
  <img src="" alt="">
  <img src="" alt="">
</div>

We’re using CSS Grid again to stack the images, one on top of the other:

.gallery {
  display: grid;
}
.gallery > img {
  grid-area: 1 / 1;
  width: 160px;
  aspect-ratio: 1;
  object-fit: cover;
}

Animation

The logic of this project is similar to the logic of the circular slider from our first article. If you watch the video above again, you will realize that the pictures form a polygon. When the rotation is completed, the polygon returns to the first picture.

For the circular slider, we used the CSS property transform-origin and animation-delay. The same animation was applied to all images rotating around a certain point. Then, to arrange the images around the circle, we included various delays.

For the 3D slider, the logic will have to change a bit. take advantage transform-origin will not work, because we will work with three dimensions. Instead, the properties are applicable transformto place all the images properly, and then rotate the container.

To loop through images and apply to each property transform let’s work with SASS:

@for $i from 1 to ($n + 1) {
  .gallery > img:nth-child(#{$i}) {
     transform: 
       rotate(#{360*($i - 1) / $n}deg) /* 1 */
       translateY(50% / math.tan(180deg / $n)) /* 2 */ 
       rotateX(90deg); /* 3 */
  }
}

You may ask why start using SASS in the first place. In previous articles, we first worked with the same number of images using pure CSS, and only then moved to SASS to work with a variable number of images. N. It seems to me that you have already figured out the topic enough, and you can immediately move on to implementing the idea on SASS.

Here you can see that the property transform takes three values:

Demonstration of the three states of the image slider layout

First, rotate the pictures in a stack. The rotation angle depends on the number of pictures. For N images, the change in angle will be 360deg/N. Then we apply the property translate to all images so that they intersect with central dots on the sides.

Demonstration of a stack of images arranged in a circle.  The red line passes through the center points of the images.

Let’s not go into geometry. Just take it for granted that the distance is 50%/tan(180deg/N). We encountered a similar equation when we created a circular slider ( transform-origin: 50% 50%/sin(180deg/N) ).

Next, we rotate the images along the X axis by 90 degrees. Here you can see what this turn leads to:

Finally, we need to give the container a rotation, and our slider will work.

.gallery {
  transform-style: preserve-3d;
  --_t: perspective(280px) rotateX(-90deg);
  animation: r 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite;
}
@keyframes r {
  0%, 3% {transform: var(--_t) rotate(0deg); }
  @for $i from 1 to $n {
    #{($i/$n)*100 - 2}%, 
    #{($i/$n)*100 + 3}% {
      transform: var(--_t) rotate(#{($i / $n) * -360}deg);
    }  
  }
  98%, 100% { transform: var(--_t) rotate(-360deg); }
}

You may not understand how this code works, so let’s look at the animation for the circular slider again. Here is the code from the first article in the series:

.gallery {
  animation: m 12s cubic-bezier(.5, -0.2, .5, 1.2) infinite;
}
@keyframes m {
  0%, 3% { transform: rotate(0); }
  @for $i from 1 to $n {
    #{($i / $n) * 100 - 2}%,
    #{($i / $n) * 100 + 3}% { 
      transform: rotate(#{($i / $n) * -360}deg);
    }  
  }
  98%, 100% { transform: rotate(-360deg); }
}

The keyframes are almost the same – repeat percentages, cycle, and rotate.

Why are they repeated? Because the logic is the same. In both cases, the images are placed in a circle, and the container must be rotated to display each image. That’s why I can copy the keyframe code from the circular slider to the 3D slider. The only difference is the angle of rotation of the container. You need to rotate it along the X axis and -90 degrees (-90deg) to make the pictures visible, because we have already rotated the container 90 degrees along the same X axis. Now we add the property perspective and get a 3D image.

That’s all. Our slider is ready. Below you see a demo. You only need to add any number of images and update one variable.

Vertical 3D Slider

Since we’re getting into 3D space, let’s create a vertical version of our slider. Our slider rotates on the Z axis, but we can make it rotate on the X axis.

When comparing the code of a vertical and horizontal slider, the difference is not immediately evident – after all, the difference is only one letter! I replaced rotate() on rotateX() in the code responsible for the transformation (transform) image keyframes. That’s all!

It is worth noting that rotate() (rotation) rotates on the Z axis, and changing the axis from Z on X we have created a vertical slider from a horizontal one.

cube slider

When it comes to 3D in CSS, you can’t help but mention cubes. And yes, now we will write another version of the slider.

Let’s create a cube from pictures and rotate it along different axes. We will work with six faces – each image will become a face of the cube. No SASS – just pure CSS.

Don’t you think that this animation is too complicated? Where to start?

We see six faces, and we need to make at least six rotations to see each image. But in fact, we need five rotations – the last rotation returns us to the first face. If you take a Rubik’s Cube, or any other cube-shaped object, like a die, and start spinning it, you’ll see what we’re doing.

.gallery {
  --s: 250px; /* the size */

  transform-style: preserve-3d;
  --_p: perspective(calc(2.5*var(--s)));
  animation: r 9s infinite cubic-bezier(.5, -0.5, .5, 1.5);
}

@keyframes r {
  0%, 3%   { transform: var(--_p); }
  14%, 19% { transform: var(--_p) rotateX(90deg); }
  31%, 36% { transform: var(--_p) rotateX(90deg) rotateZ(90deg); }
  47%, 52% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); }
  64%, 69% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg); }
  81%, 86% { transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg); }
  97%, 100%{ transform: var(--_p) rotateX(90deg) rotateZ(90deg) rotateY(-90deg) rotateX(90deg) rotateZ(90deg) rotateY(-90deg); }
}

First property transform does not rotate the element, and then each new property adds a rotation along a certain axis until we have done six rotations. We then return to the first image.

Don’t forget about the location of the pictures. Each image becomes a face of a cube using the property transform:

.gallery img {
  grid-area: 1 / 1;
  width: var(--s);
  aspect-ratio: 1;
  object-fit: cover;
  transform: var(--_t,) translateZ(calc(var(--s) / 2));
}
.gallery img:nth-child(2) { --_t: rotateX(-90deg); }
.gallery img:nth-child(3) { --_t: rotateY( 90deg) rotate(-90deg); }
.gallery img:nth-child(4) { --_t: rotateX(180deg) rotate( 90deg); }
.gallery img:nth-child(5) { --_t: rotateX( 90deg) rotate( 90deg); }
.gallery img:nth-child(6) { --_t: rotateY(-90deg); }

Perhaps it seems to you that there is some complex logic behind the rotation values ​​\u200b\u200bprescribed here? Not at all. I just opened the developer tools and played around with the degrees of rotation for each image until everything worked as it should. It may sound silly – but it works, especially considering that we have a constant number of images. There is no desire to create a slider for an arbitrary number of pictures.

As an exercise, try to arrange the pictures on the faces of the cube yourself. Start by stacking images, open the developer tools, and start experimenting! You will most likely end up with different code, but that’s okay. Images can be arranged in different ways.

What does a comma mean in var()? It’s a typo?

It’s not a typo, don’t remove the comma! If you delete it, the location of the first picture will change. Look at my code – it contains a variable --_t is responsible for all images except the first one. For the first picture, you only need to apply translateZ. The comma sets the variable to null. No comma assignment null does not occur, and the value of the variable becomes invalid.

From specifications:

Note: var(--a,) is a function. If the custom property --a incorrect or missing, then var()` is removed.

Random cube slider

Let’s change the cube’s rotation order to random.

I like this option better. It is more interesting to implement it, and it is more interesting to observe it. And the values ​​can be changed to get your own version of the random cube slider!

In fact, there is no coincidence here. On each keyframe you define a property transform to demonstrate a certain facet … and that’s it. You can choose any sequence for displaying faces.

@keyframes r {
  0%, 3%   { transform: var(--_p) rotate3d( 0, 0, 0,  0deg); }
  14%,19%  { transform: var(--_p) rotate3d(-1, 1, 0,180deg); }
  31%,36%  { transform: var(--_p) rotate3d( 0,-1, 0, 90deg); }
  47%,52%  { transform: var(--_p) rotate3d( 1, 0, 0, 90deg); }
  64%,69%  { transform: var(--_p) rotate3d( 1, 0, 0,-90deg); }
  81%,86%  { transform: var(--_p) rotate3d( 0, 1, 0, 90deg); }
  97%,100% { transform: var(--_p) rotate3d( 0, 0, 0,  0deg); }
}

Here I am using rotate3d() and select the appropriate values ​​using developer tools. Don’t try to figure out the logic behind the keyframe values ​​- it just doesn’t exist. I define individual transformations and look at the “random” result. Make sure that the first image is present on the first and last keyframe, and other images are shown on the remaining frames.

You don’t have to use transform rotate3d(). You can link different rotations together, as in the previous example. Experiment, see what happens and don’t forget to share your code in the comments!

Conclusion

I hope you enjoyed this series of articles. We’ve created a number of interesting sliders, and in the process we’ve learned a lot about various CSS concepts, from grid position and overlay context to delays in animations and transforms. We even played with SASS a bit as we looped through the array of elements.

And while we did not change the HTML code. CSS is a powerful tool, you can achieve a lot with it without resorting to JavaScript.

Brief catalog of courses

Data Science and Machine Learning

Python, web development

Mobile development

Java and C#

From basics to depth

As well as

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *