Uma foto minha vestido com o uniforme da Grifinória do Harry Potter

Willian JustenSoftware Engineer

Instrutor na Udemy, escrevo sobre o mundo front end, viagens, vida pessoal e mais.

← Voltar na listagem

Criando botao animado com CSS e SVG

Aprenda a criar uma interação mais bonita para os botões de sua aplicação.

Introdução

Faaala pessoal, tentando manter a promessa de sempre ter um post por semana, lá vamos nós para mais um post. Eu achei esse botão lá no meu codepen e acho que é interessante de ensinar, é extremamente simples, mas bem legalzinho.

Neste post iremos utilizar animações com o famoso Keyframes do CSS e o efeitinho de desenhar em SVG que usa stroke-dashoffset e stroke-dasharray.

Enquanto vou escrevendo esse post, eu vou ouvindo a trilha sonora de Hellblade Senua's Sacrifice, na minha opinião um dos melhores jogos do ano, a trilha sonora mistura sons nórdicos e celtas, vale bastante a pena ouvir e também vale procurar por esse jogo =)

Criando o botão

Para quem gosta de ver o exemplo já funcionando antes, já vou deixando aqui embaixo a demo do Codepen.

See the Pen by Willian Justen de Vasconcellos (@willianjusten) on CodePen.

A ideia desse botão é basicamente para processos em que nós enviamos uma chamada para alguma API e esperamos um resultado para jogar um feedback para o usuário. No exemplo, eu vou colocar só o feedback de success, mas vocês estão mais do que livres de criar o error, warning e o que mais desejarem.

Vamos as etapas:

Antes do Envio

O primeiro de tudo é criar nosso botão limpo, ou seja, o estado antes do envio. Que vai ter o seguinte html e css:

<button id="send" class="send">Send</button>

Já estou adicionando o id pois ele será utilizado futuramente em nosso Javascript. E a classe será para adicionarmos os estilos, que serão:

.send {
  background: none;
  color: white;
  border: 1px solid #45981b;
  border-radius: 70px;
  cursor: pointer;
  font-size: 20px;
  outline: none;
  padding: 15px 70px;
}

Com isso, vamos ter nosso botão hiper mega simples na tela. Se você estiver partindo do zero, lembre-se de adicionar um background no body e alinhamentos/resets se desejar. Adicionei os seguintes detalhes:

* {
  margin: 0;
  padding: 0;
}

body {
  align-items: center;
  background: #333;
  display: flex;
  height: 100vh;
  justify-content: center;
}

Estado de Loading

Para o estado de loading, queremos fazer um efeito do botão diminuindo para ficar do tamanho de uma bola e criar um efeito como se estivesse "pulsando". E para isso farei o seguinte, irei encapsular o texto de send num spam, que assim posso adicionar um display: none nesse estado de loading. Outra coisa que irei fazer é diminuir o padding desse botão, transformando ele num círculo e vou utilizar o transition para que está mudança de tamanho seja suave.

<button id="send" class="send">
  <span>Send</span>
</button>

Acima é o nosso html e assim vai ficar o nosso css, utilizando uma classe .is-loading para indicar que está carregando/enviando o dado.

.send {
  // outras propriedades
  transition: padding 500ms ease-in-out;
}

.send.is-loading {
  padding: 15px 17px;
}

.is-loading span {
  display: none;
}

Adicionando a trigger

Como aqui já temos dois estados o normal e o is-loading eu vou adicionar já uma trigger para que quando eu clique no botão, ele adicione a classe is-loading, assim já vamos conseguir ser capazes de ver a mudança do botão para o círculo.

Para isso é bem simples, vou buscar o elemento e usar o addEventListener para verificar o click e usarei o classList.add para adicionar a nossa classe, ficando assim:

const btn = document.getElementById('send')

btn.addEventListener('click', () => {
  btn.classList.add('is-loading')
})

Feito isso, já podemos clicar no botão e veremos a animação simples de diminuir. Agora vamos fazer o pulse!

Criando o efeito de Pulse

Para fazer esse efeito, iremos utilizar o animation com o keyframes. Precisamos aumentar/diminuir o botão e para isso vamos usar o scale e o efeito da sombra expandida será feita com o box-shadow, o conjunto pronto então será:

.send {
  // outras propriedades
  box-shadow: 0 0 0 0 rgba(69, 152, 27, 0.5);
}

.send.is-loading {
  // outras propriedades
  animation: pulse 1.5s infinite;
}

// nossa animacao
@keyframes pulse {
  0% {
    transform: scale(0.9);
  }
  70% {
    transform: scale(1);
    box-shadow: 0 0 0 50px rgba(69, 152, 27, 0);
  }
  100% {
    transform: scale(0.9);
    box-shadow: 0 0 0 0 rgba(69, 152, 27, 0);
  }
}

Eu adicionei no estado 0% que é o .send puro um box-shadow sem mover nenhuma posição da cor verde e eu então criei 3 diferentes etapas na nossa animação. No primeiro momento, que é o 0%, eu diminuo a bolinha. Depois um pouco mais da metade da animação, em 70%, eu volto ao tamanho normal e crio a expansão do box-shadow mudando o blur (que é a última propriedade do box-shadow) para 50px e por fim, aos 100% eu volto o blur para zero e diminuo o botão. Isso com a opção de infinite na animação, faz com que o botão fique aumentando/diminuindo com a sombra até que eu remova essa classe.

Criando trigger para o Success

Como aqui é só exemplo, para que a gente possa simular um success vamos utilizar o setTimeout que vai adicionar a classe is-success e remover a is-loading depois de um tempo. Ficando assim:

const btn = document.getElementById('send')

btn.addEventListener('click', () => {
  btn.classList.add('is-loading')

  // fake API call
  setTimeout(() => {
    btn.classList.add('is-success')
    btn.classList.remove('is-loading')
  }, 4000)
})

Reparem que com esse código, ao final de 4s ou 4000ms, o botão vai voltar ao estado inicial, pois estaremos removendo a classe is-loading e ainda não temos estilos para o is-sucess, vamos fazê-lo então.

Criando o estado de Success

Para finalizar nosso botão, a classe is-success vai mudar o background para ter a cor sólida e vai permanecer também no mesmo formato de círculo que o is-loading, para isso vamos adicionar o background dentro da transition do nosso botão e adicionar os estilos do is-success ficando assim:

.send {
  // outras propriedades
  transition: background, padding 500ms ease-in-out;
}

.send.is-success {
  background: #45981b;
  padding: 15px 17px;
}

.is-success span {
  display: none;
}

Depois disso, vamos fazer o pequeno detalhe do sinal de ok/certo com SVG, para isso vamos adicionar o seguinte SVG dentro do nosso botão.

<button id="send" class="send">
  <svg viewBox="0 0 90.594 59.714">
    <polyline
      class="check"
      fill="none"
      stroke="#FFFFFF"
      stroke-width="10"
      stroke-miterlimit="10"
      points="1.768,23.532 34.415,56.179 88.826,1.768"
    />
  </svg>
  <span>Send</span>
</button>

E vamos fazer também uma edição no CSS para ele sumir/aparecer com o is-sucess.

svg {
  width: 0;
  height: 0;
}

.is-success svg {
  height: 30px;
  width: 30px;
}

Agora está faltando só fazer o efeito de desenhar, para isso, já temos uma classe check lá no nosso SVG, para que possamos fazer essa animação, que vai ser baseada em stroke-dashoffset e stroke-dasharray, que você pode ler melhor sobre aqui. O CSS dessa parte ficará:

.check {
  stroke-dasharray: 130px 130px;
  stroke-dashoffset: 130px;
  transition: stroke-dashoffset 500ms ease-in-out;
}

.is-success .check {
  stroke-dashoffset: 0px;
}

Reparem que inicialmente eu to movendo o meu desenho todo para fora de visão e no .is-success .check eu retorno para o zero, através de uma transition, criando o efeito final que queríamos, que é o símbolo ser desenhado na tela. Segue então nosso resultado final:

See the Pen by Willian Justen de Vasconcellos (@willianjusten) on CodePen.

Conclusão

Bom pessoal, foi um post bem simples, mas que pega alguns conceitos bacaninhas para criar um pequeno detalhe na interface, mas que faz a diferença. Espero que tenham gostado do post e estejam a vontade para criar edições do botão e mandar nos comentários =)