Creating a Cyclic Carousel using React Hooks

Dhilip Kumar
Mar 25 · 6 min read

(For those users who are restricted by Medium paywall, I have also shared this post in dev community).

The latest React packages include React Hooks which is a new React API. Using React Hooks are super easy. If you are new to React Hooks and you need to learn the basics of React Hooks please head over to my previous post.

What’s a Carousel?

Carousels allow us to represent a group or a pile of related information.

A couple of examples:

  • A set of images in Travel Blogs.
  • A set of offers that are available.

What are we going to build?
Our end product looks like the below gif:

Application Outline:

  • Splashbase gives the API response for getting images [You can use the API of your Choice]
  • We hit splashbase once Component Mounts.
  • Once we get a response we load a Carousel.
  • Carousel has a function to initiate the ability to change images automatically.
  • Providing Cyclic images in the carousel (First image lies next to the last image ).
  • Ability to load images out of order.

Understanding App.js:

  • We create an App component using React Hooks.
  • In line 8 we declare an imageList which is an array. It stores Splashbase’s API response.
  • [Line 10] :This is similar to componentDidMount().Here we call the API once the component loads and we update the variable accordingly.
  • At Line 27, we load the carousel if the list is not empty and pass some props to the carousel component created with React Hooks

Stepping Into Carousels…

In the above section, we have created a render method and have defined a method to loadCarousels.

Understanding the declared variables.

  • [Line 4- 8] Fallback constant values if the user does not specify the values for the properties.
  • [Line 11] : imgList -List of image passed from parent. Then, we add the Width and height of the image. Number of images that are visible.’autoNext’ -To automatically go to next image if ‘timeForNext’ milliseconds elapsed.
  • currFirstImg’ -Indicates the Current Middle Element/Primary element of our carousel
  • ‘actualFirst’ -If the Clicked Image is not the Immediate Next or Immediate Previous Image. As we make all the intermediate images as currFirstImg for some amount of time to create a carousel effect, need to store this. That creates a weird and unpleasant animation to the user , if we miss doing this.
  • ‘visibleItemsProps’ -Set Styles and ordering to Images currently visible. This is where we set opacity, coordinates of each image.
  • currMiddleImgRef’ -Reference for the image that is in the middle. We need a ref to current Middle image because once we set the timer the updated state variables won't be accessible inside settimeout.[Mainly to handle out of order image clicks]
  • intervalRef’ -Reference to setTimeInterval.For Removing the previous setTimeInterval from within/ before initiating new interval.
  • imgDifference’ -The difference between the middle image and the image that is clicked by the user to view next
  • durationRef’ -Animation Duration, it should be changed and split evenly among the skipped images when the image clicked by the user is not immediately next/prev
  • parentHeight’ -To help in avoiding overlap. Positioning images absolutely cause overlap with Carousel Component’s siblings.
  • ‘parentWidth’-To have thrice the width of an individual image.[A personal preference]
  • ‘elementsInLeft’ -Doing Floor to also include a middle image along with other images to its left.

loadCarousel:

This renders the JSX on the screen. It is interesting to see that the code to render the carousel is very less but under the hood loads of logics are there.

We add transition property to <li> element to create the animation.

We iterate through each image item in imgList and see if they are supposed to be visible in the screen by checking if they are in the order property of visibleItemsProps. ‘order’ holds the order in which images should appear in the screen.

Carousel Static Css:

While rendering our components, these are some of the classes that we should use. Positioning Images as ‘absolute’ to make them lie relative to their relative parent.

Effects:

Effect 1:

  • On every change to actualFirst state value Hooks gets fired. actualFirst is set when there is out of order click.We will be handling it through timeinterval. It allows all intermediate images to come into middle in the once, to avoid the cycling look jerky.
  • If the actually the clicked image is not the currentimage in middle gotoNext image function is called.
  • [Line 7] : When the actual clicked and middle image is the same we are all set to clear intervals, as they are unnecessary now.[Line 7]
  • To begin bringing the next image to the middle before the previous one settles down, an advance of 100ms has been introduced. It looks jerky otherwise.[Line 21]

Effect 2:

  • constructVisibleItemsProps() : This constructs all CSS properties to the elements in visibility.We need to do it whenever the image at center changes.
  • [line 19]: Need to change the ref for the current image, so while accessing it inside interval it will have the latest value.

NOTE: Variable binding with async methods like timeout, interval behaves differently in Hooks. It always is bound with values that the component has at the time of initiating setTimeout/setInterval. But this does not hold true for refs. That is why we use refs to get the current image while we are inside the timer.

Effect 3:

  • An interval is set to iterate through images automatically for the given interval(timeForNext) if the autoNext property is enabled.

Understanding how changing the center and image cycling works:

changeCenter:

On clicking the image, execution of changeCenter begins

Checking if the clicked item is immediately next/prev item. Because to induce a carousel effect we need to make the images move in sequence.

That won't be an issue if the clicked image lies next/previous to the current image. But in other cases where the user clicks out of order like while viewing image 1 he can click on image 5, in that case, we should handle it in such a way that cycle won't break.

Each image should come to the middle before the actually clicked image.

[Line 6]: Set the image index to clicked image’s index if there is a change in image index and if they are immediate previous or next.

[Line 9]: If it is not the subsequent image, then set the actual image clicked index to setActualFirst state value. Then change the middleImage to immediate next or previous image based on which side of the primary image he clicked. Then change CSS props and render it. Till the actual clicked image value and image in the middle are the same, it goes on. Hooks effect associated with setActualFirst fires when setActualFirst changes

[Line 16]: Place a callback, on clicking the middle image.

Constructing Visible Items’ Props:

src: https://giphy.com/

Declaration Meaning:

  • visibleItemsProps -To store config for items that are visible in the carousel
  • curr_center -Storing the Current Middle element in focus.
  • timesToIterate-To iterate through all visible number of images.
  • zIndex -We start from left to right and zIndex has to keep on increasing till middle then has to reduce.
  • xTranslate -To move the element with respect to x axis
  • zTranslate -To reduce image size for images apart from center
  • division -Specifies the length that next image has to move away from with respect to current image (1.6 times the current image).
  • opacityDivider -minimum opacity should be 0.3 (1–0.7)
  • leftEltCount -including middle element

Iteration:

  • The first if condition is to handle elements in the right side of the Middle image.
  • currImgIndex — Image index of right element.
  • There we calculate its opacity in increasing fashion.
  • xTranslate with respect to the image in the middle.
  • zTranslate to increase the size of the image from lowest to maximum till center and back to small again.

Similarly, we do it for elements on the left side.

Assigned a value to durationRef.current. If the next image is clicked by the user, the duration.current’s value will be user given time. If not, we divide the duration by the number of images skipped. Then, we set the visibleItemsProps and the component re-renders.

Check out my code at GitHub.

Clap If it helped you :)

Connect with me here -> LinkedIn and Twitter

THAT’S ALL FOLKS!!!

src: https://giphy.com/

    Dhilip Kumar

    Written by

    Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
    Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
    Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade