Como criar um componente carrossel em Angular
Aprenda a criar um componente em Angular no estilo carrossel para exibir uma imagem por vez, efetuando a transição com o efeito fade.

O carrossel é um componente essencial no design de um site. Ele ajuda a exibir diversas informações em um espaço limitado. Criar um componente no formado carrossel não é tão complicado. Claro, depende da complexidade dos efeitos e interações.
Neste texto será abordado como construir um pequeno componente, simples, para exibir uma sequência de imagens com o efeito fade.
Criando o projeto
Versões utilizadas para criar o projeto:
$ ng version
_ _ ____ _ ___
/ \ _ __ __ _ _ _| | __ _ _ __ / ___| | |_ _|
/ △ \ | '_ \ / _` | | | | |/ _` | '__| | | | | | |
/ ___ \| | | | (_| | |_| | | (_| | | | |___| |___ | |
/_/ \_\_| |_|\__, |\__,_|_|\__,_|_| \____|_____|___|
|___/
Angular CLI: 14.2.3
Node: 18.12.0 (Unsupported)
Package Manager: npm 8.19.2
OS: darwin arm64
Angular:
...
Package Version
------------------------------------------------------
@angular-devkit/architect 0.1402.3 (cli-only)
@angular-devkit/core 14.2.3 (cli-only)
@angular-devkit/schematics 14.2.3 (cli-only)
@schematics/angular 14.2.3 (cli-only)
Criando o projeto:
$ ng new consolelog-carrossel
? Would you like to add Angular routing? No
? Which stylesheet format would you like to use? CSS
Executando o projeto para verificar se tudo está funcionando:
$ cd consolelog-carrossel/
$ npm start
Acessando através do navegador podemos ver que tudo está funcionando corretamente:

Criando o componente carrossel
O primeiro passo para a construção do componente é criar os arquivos que o compõem. Uma das formas de fazermos isto é através da própria CLI do Angular:
$ ng generate component carrossel
CREATE src/app/carrossel/carrossel.component.css (0 bytes)
CREATE src/app/carrossel/carrossel.component.html (24 bytes)
CREATE src/app/carrossel/carrossel.component.spec.ts (620 bytes)
CREATE src/app/carrossel/carrossel.component.ts (287 bytes)
UPDATE src/app/app.module.ts (408 bytes)
O comando acima cria 4 novos arquivos e modifica um. O arquivo modificado é o app.module.ts
, que ficou da seguinte forma:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { CarrosselComponent } from './carrossel/carrossel.component';
@NgModule({
declarations: [
AppComponent,
CarrosselComponent,
],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
Para testar se o novo componente está funcionando basta modificar o conteúdo do arquivo app.component.html
para o seguinte:
<app-carrossel></app-carrossel>
Na imagem abaixo é possível observar que o componente está configurado corretamente. Então agora podemos partir para o desenvolvimento da lógica do carrossel.

Desenvolvendo a lógica
Por uma decisão arbitrária, o componente deverá receber um array de imagens, assim será possível exibir uma imagem por vez. Então podemos escrever o seguinte no app.component.html
:
<app-carrossel
[imagens]="[
'https://picsum.photos/id/17/2500/1667',
'https://picsum.photos/id/18/2500/1667',
'https://picsum.photos/id/19/2500/1667'
]"
></app-carrossel>
Na sequência criei uma pequena lógica envolvendo um timer
(temporizador) e algumas variáveis e métodos para controlar qual imagem deve ser exibida. A ideia é que toda vez que o timer
for finalizado, nós trocamos para a próxima imagem. Deixei alguns comentários no código abaixo para facilitar o entendimento:
import {
Component,
Input,
OnDestroy,
OnInit,
} from '@angular/core';
import { Subscription, timer } from 'rxjs';
@Component({
selector: 'app-carrossel',
templateUrl: './carrossel.component.html',
styleUrls: ['./carrossel.component.css'],
})
export class CarrosselComponent
implements OnInit, OnDestroy
{
// Guarda a referência do temporizador.
// Assim conseguimos interromper o temporizador
// a qualquer momento
timerSubs!: Subscription;
// Array com a URL das imagens que serão exibidas
// no carrossel
@Input() imagens: string[] = [];
// Guarda a posição no array "imagens" que
// corresponde a imagem que está sendo exibida
// no carrossel
private _indexImagemAtiva: number = 0;
get indexImagemAtiva() {
return this._indexImagemAtiva;
}
set indexImagemAtiva(value: number) {
this._indexImagemAtiva =
value < this.imagens.length ? value : 0;
}
ngOnInit(): void {
this.iniciarTimer();
}
ngOnDestroy(): void {
this.pararTimer();
}
iniciarTimer(): void {
this.timerSubs = timer(1000).subscribe(() => {
this.ativarImagem(
this.indexImagemAtiva + 1
);
});
}
pararTimer(): void {
this.timerSubs?.unsubscribe();
}
ativarImagem(index: number): void {
this.indexImagemAtiva = index;
this.iniciarTimer();
}
}
No template apenas renderizei os valores das variáveis para ver se o funcionamento está como o planejado:
<div>Imagens: {{ imagens | json }}</div>
<div>
indexImagemAtiva: {{ indexImagemAtiva }}
</div>
<div>
imagens[indexImagemAtiva]:
{{ imagens[indexImagemAtiva] }}
</div>
Visualizando no navegador é possível observar que a cada 1 segundo, valor definido no timer(1000)
, o valor do trecho imagens[indexImagemAtiva]
é modificado.

Observação: deixei o timer(1000)
para agilizar a transição já que o objetivo inicial é validar a lógica. Ao término do desenvolvimento este valor deve ser alterado para algo que atenda seus requisitos.
Ajustando o estilo visual
Com a lógica implementada, precisamos de um pouco de CSS e HTML para ter um primeiro resultado:
.item {
background-size: cover;
background-repeat: no-repeat;
height: 300px;
transition: all 0.5s ease-in-out;
}
<div
class="item"
[style.backgroundImage]="
'url(' + imagens[indexImagemAtiva] + ')'
"
></div>

Observando a imagem acima é possível notar que a transição com o efeito fade está acontecendo, porém, repare que no primeiro ciclo de transição de imagem existe um certo atraso (delay). Isto ocorre porque na primeira exibição de cada imagem o navegador tem que fazer uma requisição para obter a imagem.
Uma forma simples de contornar isto, é criar um cenário que force o navegador a baixar todas as imagens previamente. Bom, não é tão elegante, mas incluir logo "de cara" no seu template um <img src="url da imagem">
vai ajudar a resolver este problema. Para não atrapalhar no layout é só fixar o tamanho da imagem em 1 pixel, deixá-la totalmente transparente e com a posição fixed
:
<div
class="item"
[style.backgroundImage]="
'url(' + imagens[indexImagemAtiva] + ')'
"
></div>
<!--
Cria imagens que ficam "escondidas"
para forçar o navegador a baixar as
imagens assim que o HTML é interpretado.
Isto evitar o delay de carregamento
das imagens do carrossel na primeira
vez que cada imagem é mostrada na tela.
-->
<img
*ngFor="let imagem of imagens"
[src]="imagem"
width="1"
height="1"
style="opacity: 0; position: fixed"
/>
Veja que quando a página é carregada, o navegador já faz as 3 requisições (3 imagens) e resolve o problema do atraso (delay):

Adicionando controles de transição no carrossel
Para finalizar este simples componente, vamos adicionar botões para que o usuário possa escolher aleatoriamente uma imagem:
<div
class="item"
[style.backgroundImage]="
'url(' + imagens[indexImagemAtiva] + ')'
"
></div>
<!-- Botões de controle -->
<div class="botoes-controle">
<button
*ngFor="let imagem of imagens; index as i"
[class.ativo]="i === indexImagemAtiva"
(click)="pararTimer(); ativarImagem(i)"></button>
</div>
<!--
Cria imagens que ficam "escondidas"
para forçar o navegador a baixar as
imagens assim que o HTML é interpretado.
Isto evitar o delay de carregamento
das imagens do carrossel na primeira
vez que cada imagem é mostrada na tela.
-->
<img
*ngFor="let imagem of imagens"
[src]="imagem"
width="1"
height="1"
style="opacity: 0; position: fixed"
/>
.item {
background-size: cover;
background-repeat: no-repeat;
height: 300px;
transition: all 0.5s ease-in-out;
}
.botoes-controle {
display: flex;
gap: 10px;
justify-content: center;
margin-top: 8px;
}
.botoes-controle button {
background: #ccc;
border: none;
border-radius: 50%;
cursor: pointer;
height: 12px;
width: 12px;
}
.botoes-controle button.ativo {
background: #ff0000;
cursor: default;
}

Link com o exemplo completo:
https://stackblitz.com/edit/como-criar-componente-carrossel-angular?file=src/app/app.component.ts
Considerações
A construção deste componente envolveu o conhecimentos em Angular e também um pouco de HTML e CSS. Apesar de ser um componente simples, é muito funcional.