Técnicas CSS para efeito cascata de material

Um guia sobre diferentes técnicas para o efeito cascata usando CSS e JavaScript

Recentemente, tive que implementar o efeito cascata do design de material em um aplicativo da web. E então percebi que não tinha ideia de como isso foi implementado. Isso me levou a uma jornada para estudar as implementações existentes e até criar uma nova técnica que pode ser útil para você.

O que é esse efeito cascata?

Aguarde, você não conhece o efeito cascata do Design de materiais do Google? Você mora em uma caverna há quantos anos?

O efeito cascata é usado quando você pressiona um botão. Funciona da mesma maneira para interações de mouse ou toque.

A posição em que você clica ou toca no botão é chamada de ponto de contato. A partir daí, uma ondulação é enviada se movendo para fora, perdendo a opacidade à medida que cresce até que ele preenche o botão inteiro. Então desaparece completamente.

A dinâmica desse efeito cascata é semelhante às ondulações que você recebe quando toca uma superfície líquida ou quando joga uma pedra em um lago.

As ondulações que você encontrará na web

Depois de fazer algumas pesquisas, pude encontrar duas técnicas principais usadas para implementar o efeito cascata em aplicativos da web.

Using :: after pseudo-element

Usando essa técnica, o pseudoelemento :: after do botão é denominado como um círculo semitransparente e animado para crescer e desaparecer. O botão do contêiner precisa ter transbordamento: oculto para que o círculo nunca transbordar para fora da superfície do botão e posição: relativa para facilitar o posicionamento do círculo dentro do botão. Você pode ler mais detalhes dessa técnica neste artigo de Ionuț Colceriu.

Uma das grandes vantagens dessa técnica é que ela é uma solução CSS pura para o efeito cascata. No entanto, o efeito cascata sempre começa no centro do botão, em vez do ponto de contato. Esse não é o feedback mais natural.

Pode ser aprimorado usando JavaScript para armazenar o ponto de contato e usá-lo para posicionar a ondulação. Foi exatamente isso que o material.io fez pelo componente de ondulação da Web. Ele usa variáveis ​​CSS para armazenar o ponto de contato, e o pseudoelemento :: after usa essas variáveis ​​para posicionamento.

Usando elementos filho

Em essência, essa técnica usa a mesma estratégia de antes. Mas, em vez de um pseudoelemento, ele adiciona um elemento de amplitude dentro do botão, que pode ser posicionado através do JavaScript. Esta técnica é descrita neste artigo por Jhey Tompkins.

A implementação mais simples cria uma extensão para cada clique no botão e usa a posição do mouse no evento click para alterar a posição da extensão. Uma animação CSS faz com que a extensão cresça e diminua até se tornar totalmente transparente. Podemos optar por remover a extensão do DOM assim que a animação terminar, ou simplesmente deixá-la lá embaixo do tapete - ninguém notará realmente uma extensão transparente por aí.

Eu encontrei outra variação disso, na qual o elemento filho é um svg em vez de um intervalo, e o svg é animado por JavaScript. Essa variação é explicada por Dennis Gaebel, mas, em essência, parece ser a mesma, e talvez permitindo o uso de formas e efeitos SVG complexos.

Um problema com entradas de envio

As duas técnicas descritas acima parecem ótimas. Mas é isso que acontece quando tentei aplicá-los em elementos de entrada com o tipo = submit:

Por que eles não funcionam?

O elemento de entrada é um elemento substituído. Em resumo, isso significa que há muito pouco que você pode fazer com esses elementos em relação ao DOM e CSS. Especificamente, eles não podem ter elementos filhos nem pseudo-elementos. Agora está claro por que essas técnicas falham.

Portanto, se você estiver usando o Design de materiais, é melhor ficar longe da entrada [type = submit] e seguir os elementos do botão. Ou apenas continue lendo.

Adicionando ondulações para enviar entradas

No aplicativo da web em que eu estava trabalhando, já tínhamos muitos botões de envio. Alterar todos eles para se tornarem um elemento diferente exigiria muito trabalho e um alto risco de quebrar as folhas de estilo e a lógica JavaScript. Então eu tive que descobrir como adicionar ondulações aos botões de envio existentes.

Usando um recipiente de embalagem

Percebi rapidamente que poderia envolver o botão de envio dentro de um elemento de bloco embutido e usar o elemento de bloco embutido como superfície de ondulação. Aqui está uma demonstração rápida:

Embora eu goste desta solução por sua simplicidade, ela ainda exigiu que eu alterasse a marcação em muitos lugares. E eu sabia que seria uma solução frágil - novos desenvolvedores entrariam no projeto e criariam botões de envio sem envolvê-los adequadamente em uma superfície ondulada. Por isso, continuei procurando outras soluções que não exigiam a alteração do DOM.

Gradientes radiais

A sintaxe do gradiente radial permite controlar o centro e o tamanho do gradiente. Obviamente, também me permite controlar a cor do gradiente, incluindo cores semitransparentes. E nunca excede o elemento ao qual é aplicado. Parece que já faz tudo o que preciso!

Não é tão rápido ... falta uma coisa: a propriedade de imagem de fundo não é animada. Não pude fazer o gradiente crescer e desbotar para transparente usando animações CSS. Consegui fazê-lo crescer animando a propriedade tamanho do plano de fundo, mas isso foi tudo o que pude fazer.

Tentei algumas outras coisas, como ter um círculo desbotado como uma imagem animada (usando o formato apng), e apliquei-o como uma imagem de fundo. Mas não consegui controlar quando o loop da imagem começou e terminou.

Finalmente, uma solução com JavaScript

O que você não pode fazer em CSS, em JavaScript. Depois de gastar mais tempo do que estou disposto a admitir, tentando fazer esse efeito funcionar usando animações CSS, desisti e decidi escrever a animação em JavaScript.

Comecei com a solução de gradiente radial acima e usei o window.requestAnimationFrame para fazer uma animação fluida do gradiente radial, aumentando e diminuindo. Aqui está minha solução final:

Conclusão

Portanto, é possível ter efeitos de ondulação nos botões de envio, mas não apenas com CSS.

Não consegui encontrar essa técnica documentada em nenhum lugar da Web, por isso estou chamando a minha. A técnica de ondulação de Leonardo não requer alterações no DOM e funciona para qualquer elemento porque não depende de pseudoelementos ou elementos filho. No entanto, não é uma solução perfeita.

Primeiro, há desempenho. Ao animar o gradiente com JavaScript, você perde muitas otimizações do navegador. Mas, como a única propriedade que está sendo alterada é a imagem de plano de fundo, eu suspeitaria que os navegadores não precisariam refletir novamente e exigiria apenas reaplicar os estilos e repintar o elemento. Na prática, é exatamente isso que acontece, e o desempenho é realmente bom. A exceção a essa afirmação é o Firefox Mobile, que por algum motivo não acompanha a animação. (editar: a animação é suave nas versões modernas do Firefox Mobile)

Segundo, a técnica usa a propriedade background-image do botão. Se seu design exigir que seus botões tenham uma imagem aplicada ao plano de fundo, o efeito cascata substituirá isso. Se você realmente precisar dessa imagem em seu design, o JavaScript poderá ser modificado para desenhar o gradiente radial sobre a imagem de plano de fundo existente.

Terceiro, isso parece não funcionar no Internet Explorer. No entanto, não vejo nenhum motivo para não funcionar com o IE10 e superior. Talvez seja porque o IE usa uma sintaxe diferente para gradiente radial. Mas, quem se importa com o IE hoje em dia? (editar: esse método funciona sem problemas no Internet Explorer 11)