360° Product View
Tags: javascript, webdev, html, tutorial
Published: 2021-12-03 16:15:33

I am always impressed by the websites that showcase products in unique ways on scroll of the page. Transitions, fades, warps, zooms so on. One very particular animation I want to try is the 360 degree rotation of the product on scroll.

Like this from [Nuro](https://www.nuro.ai/)

![nuro car rotate](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vdzdbpbwxegs2wlmdhkd.gif)

It gives complete 360 view of the product.

## The Nuro implementation
After a bit of digging around the side I see the website downloads each and every image required to create the full view of the vehicle. That's a lot of images, 127 images to be precise.


![Images](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/847luflsme2rjh25cyae.gif)

And these images are loaded onto a canvas to create an animation. Long story short, I want an easier solution.

Then it flashed, **Sprite sheet**

## Sprite sheet
Using a sprite sheet, I can load a single image file with all the individual frames required to complete the 360 view of the product

[More on spritesheets](https://www.codeandweb.com/what-is-a-sprite-sheet#:~:text=A%20sprite%20sheet%20is%20an,loading%20time%20of%20the%20game.)

Hoping someone had already thought of the sprite sheet method, My next task was to find a relevant sprite sheet of a car.

[Found one](https://www.cssscript.com/demo/product-viewer-animate-sprite/car.jpg)

Now let's begin the animation

## Concept

The idea is simple, one used since the dawn of cinema. Create a view window that only sees a certain section of the whole image, and move new frame into the view on each scroll, hence creating an 360 view effect  

Think of red box as the only part visible at the time and we move image on each scroll step
![move](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pb6kpsejst80viizas8c.gif)

## Implementation

First thing to know is what is the height and width of the individual image.
The image used here each frame is 
height : 600px
width : 350px

Let us create a div and set the background as the image along with height and width. also let us set the height of body so we have something to scroll

``` css

body {
    height: 2000px;
}

#car {
    width: 600px;
    height: 350px;
    position: fixed;
    background: transparent url(https://www.cssscript.com/demo/product-viewer-animate-sprite/car.jpg);
  }
```
That should complete the CSS.

Lets move on to the javascript part

First let's query select the html elements that will be used,the div that holds the car image and a counter that we will use for debugging (not necessary for the implementation)

``` javascript
const car = document.querySelector("#car");

// only used for debugging
const countIndicator = document.querySelector(".counter");
```

Next the height and width of the single image, we will also need number of individual images(sprites) in the the entire image, in this case it is 61. this is so that we can loop back to first image when we reach the end.

Things would have been easier if all images were in the same line, but we have 13 images each line, so let us keep that in `imagePerLine` which will help us know to jump to next line of images

```javascript
const imageCount = 61;
const imageWidth = 600;
const imageHeight = 350;
const imagePerLine = 13;
``` 

All initialization done, let's create the scroll event listener

`window.addEventListener("scroll", function (e) {})`

Now that we have a scroll event listener, we can get all the scroll related properties, what we want is the `scrollTop` property

`const { scrollTop } = e.target.scrollingElement;`

Next task : count from 0-61 then back to 0. 
Performing a mod `%` operation on the scrollTop value we can ensure a value stays within our image count
i.e 
```javascript
let stepCount = scrollTop % imageCount
``` 

Test the update of `stepCount` by assigning it to the `countIndicator` on scroll.

![abrupt](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qmur7z10bwjmwu8xyt4i.gif)

The steps increment way too many times per scroll we need reduce steps per scroll.

Dividing the `scrollTop` by some number say 10 will give 'smoother' transition values, also a `Math.Floor` to avoid float values.
We finally have the `stepCount` as

```javascript
let stepCount = Math.floor((scrollTop / 10) % imageCount);
```

Giving us smoother transition of `stepCount` per scroll 

![smoother](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hknxidygazitl9c677j4.gif)

Now that `stepCount` is calculated we need the row and column index to the select the image from sprite sheet, this is where the image per line `imagePerLine` comes into play.

```javascript
 let columnIndex = stepCount % imagePerLine;
 let rowIndex = Math.floor(stepCount / imagePerLine);
```

Now that we have the row and column indexes, we can calculate our x and y position in pixels by multiplying with `imageWidth` and `imageHeight` respectively, this value can be used to set div css `backgroundPosition`. 

Read more about [backgroundPosition](https://developer.mozilla.org/en-US/docs/Web/CSS/background-position)

```javascript
x = columnIndex * imageWidth * -1;
y = rowIndex * imageHeight * -1;
car.style.backgroundPosition = x + "px " + y + "px";
```

And voila!! we now have a 360 degree view of the car on scroll

![rotate](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wrh7lah5pmexf38vq7ni.gif)

You can find the demo and complete code [here](https://codepen.io/jayanthracharya/pen/OJxyPpa)

If you have any questions, reach out to me on [Twitter](https://twitter.com/JayanthRAcharya) or check my [Github](https://github.com/jayanthra)