Como Criar um Carrossel Responsivo no React

Thalita César
5 min readJul 28, 2022

--

Um carrossel ou slider é um componente que guarda um agrupamento de divs. O carrossel possui funcionalidades de prev, next e indicadores. Para construí-lo utilizamos animações e transitions CSS juntamente com funções javascript.

Na foto abaixo podemos ver um carrossel com 3 itens e apenas um visível no Viewport.

Cada item é uma div. A div Carousel é onde andará os items para nossa viewport e a div inner é onde estará as divs ocultas que passaram na viewport.

No react criamos uma pasta components onde dentro dela criamos uma pasta Carousel com dois arquivos: Carousel.jsx, CarouselItem.jsx e styles.js.

No arquivo CarouselItem criaremos nossos itens que serão chamados no Carousel.jsx.

Vamos a prática.

O arquivo Carousel.jsx:

import React from "react"export const Carousel=({children})=> {return (
<>
<CarouselContainer>
{/* Vamos controlar o item ativo pelo translateX */}
<Inner style={{transform: "translateX(-0%"}}>
{/* A propriedade children para renderizar nossos itens do
CarouselItem. O width está 100%, pois ficará apenas um item
amostra. Eu vou colocar 20% para ficar 4 items na viewport */}
{React.Children.map(children,(child, index)=>{
return React.cloneElement(child, {width:"20%%"})
})}
</Inner>
</CarouselContainer>
</>
)}

Agora passamos essas propriedades para o nosso CarouselItem.jsx:

export const CarouselItem=({children, width})=> {return (
<>
<DivCarouselItem style={{width:width}}>
{children}
</DivCarouselItem>
</>
)}

Construindo o estilo no styles.js:

import styled from "styled-components";
// ocultando o que está fora da viewport
export const CarouselContainer = styled.div`
overflow: hidden;
`;
//nowrap para não quebrar linha dos items,
// o transision faz a animação para o item ativo de 0.3s
export const Inner = styled.div`
white-space:nowrap;
transition: transform 0.3s;
margin:60px;
`;// o inline-flex segue a mesma lógica do nowrap
// o restante é a gosto
export const DivCarouselItem = styled.button`
display:inline-flex;
align-items: center;
justify-content:center;
height: 300px;
backgorund-color: #080808;
color: #fffff;
margin: 25px;
`;

Vocês perceberam que o CarouselItem não foi importado dentro do arquivo Carousel.jsx, isso porque ele será renderizado em uma página. Criaremos uma pasta chamada pages e nela um arquivo chamado Home.jsx. Ele ficará assim:

import { Carousel } from "../components/Carousel/Carousel"
import {CarouselItem} from "../components/Carousel/CarouselItem"
export const Home=()=> {return (
<>
<Carousel>
<CarouselItem>1</CarouselItem>
<CarouselItem>2</CarouselItem>
<CarouselItem>3</CarouselItem>
<CarouselItem>4</CarouselItem>
<CarouselItem>5</CarouselItem>
<CarouselItem>6</CarouselItem>
<CarouselItem>7</CarouselItem>
<CarouselItem>8</CarouselItem>
<CarouselItem>9</CarouselItem>
<CarouselItem>10</CarouselItem>
</Carousel>
</>
)}

Já concluímos a primeira etapa do nosso carrossel, agora podemos colocar nele as funcionalidades e animações que desejamos. Ele ficou assim:

Só ficaram 4 items ativos, o que faremos agora é a funcionalidade de poder ver os outros items que estão ocultos. Se tivéssemos colocado o width:100% lá no início só veríamos 1 item.

O translateX está -0%, ou seja, inativo. Se eu colocasse 20% o carrossel afastaria 20% na tela, mas não é isso que eu quero. Por isso precisamos fazer essa funcionalidade no javascript. Usaremos o hook useState(não esqueça de importar) no arquivo Carousel.jsx:

const [active, setActive] = useState(0);//O translateX está assim: 
//<Inner style={{transform: "translateX(-0%"}}>
//Colocaremos o active no lugar do 0 usando o template string

<Inner style={{transform: `translateX(-${active*20}%`}}>
// essa numeração que multiplicamos é a largura que queremos deslizar, ou seja, se deixar 100 deslizará todo o viewport, se eu quiser deslizar item por item eu coloco a mesma quantidade de width do item. No caso, eu vou colocar 20%.

O que faremos agora é criar os indicadores para que a funcionalidade funcione. Utilizaremos o método updateIndex(). Vamos colocar essa função no arquivo Carousel abaixo do useState:

const [active, setActive] = useState(0);const updateIndex = (newIndex) =>{
if(newIndex <0){
newIndex = 0;
}
else if (newIndex>= React.Children.count(children)){
newIndex = React.Children.count(children)-1;
}
setActive(newIndex)
}

O que fizemos acima foi uma função para definir o índice dos items, se o item for menor do que 0 mantemos ele em 0. No else if verifica se o índice é igual ao número de items, se for true define para -1. Ela vai funcionar por meio de um onClick, para isso utilizamos o prev e o next.

Vamos criar os nosso prev e next.

Vai ficar assim no nosso documento:

<Indicadores><Prev onClick={()=>{
updateIndex(active -1)
}}>
Prev
</Prev>
<Inner style={{transform: `translateX(-${activeIndex*20}%`}}
{React.Children.map(children,(child, index)=>{
return React.cloneElement(child, {width:"20%"}
})}
</Inner>
<Next onClick={()=>{
updateIndex(active +1)
}}>
Next
</Next>
</Indicadores>//dentro dos botões eu importei icons

Vamos estilizar nosso Carrossel:

export const Indicadores = styled.div`
display: inline-flex;
padding: 14px 0px;
`;
export const Prev = styled.button`
position: absolute;
z-index: 4;
left: 30px;
align-self: center;
background: none;
border: none;
cursor: pointer;
margin-top: 2.5em;
&:hover {
transform: scale(1.2);
transition: all 0.3s ease;
}
`;
export const Next = styled.button`
position: absolute;
z-index: 4;
right: 30px;
align-self: center;
background: none;
border: none;
cursor: pointer;
margin-top: 2.5em;
&:hover {
transform: scale(1.2);
transition: all 0.3s ease;
}
`;

O carrossel já está funcionando e responsivo. Ele ficou assim:

Se eu quisesse fazer um slider automático com esse carrossel eu utilizaria o setInterval() do hook useEffect fazendo com que a posições mudem a cada tempo determinado. Se eu colocar apenas o setInterval o navegador vai entrar em loop infinito e vai dar problema na aplicação, como eu não quero isso eu vou usar um clear e a função vai ser colocada lá no arquivo Carousel,js assim:

useEffect(()=>{
const interval = setInterval(()=>{
updateIndex(activeIndex +1);
}, 6000); //6segundos
return()=>{
if(interval){
clearInterval(interval)
}}}
)

Para finalizar vamos deixar o nosso Carrossel com a função de touch screen para os usuários mobile.

O primeiro passo é criar um estado para pausar o carrossel quando ele for tocado e ele vai iniciar falso.

const [paused, setPaused] = useState(false);

Vamos deixar o useEffect assim:

useEffect(()=>{
const interval = setInterval(()=>{
if(!paused){
updateIndex(activeIndex +1);}},
6000);
return()=>{
if(interval){
clearInterval(interval)
}}}
)

Na primeira div do carrossel vamos colocar as duas funções para manipular o mouse:

<CarouselContainer
onMouseEnter={()=> setPaused(true)}
onMouseLeave={()=> setPaused(false)}
>

O que isso vai fazer: quando o mouse estiver em cima do carrossel ele ficará pausado, se ele sair do carrossel ele voltará a andar, isso é necessário para o touch ser aplicado.

Eu vou usar o react-swipeable que é uma lib de touch.

Eu vou importar a função da lib

import { useSwipeable } from "react-swipeable";

E agora criar a função dentro do componente:

const handlers = useSwipeable({
onSwipedLeft:()=>updateIndex(activeIndex+1),
onSwipedRight:()=>updateIndex(activeIndex-1)})

Quando o mouse for puxado para a direita passa +1, quando ele for puxado para a esquerda volta -1 no index. Passamos a função dentro da div principal:

<CarouselContainer
{...handlers}>

Repositório do GitHub: https://github.com/ThalitaCesar/Carrrossel-Responsivo-React

--

--