- canvas-confetti é uma biblioteca cliente para executar animações de confete baseadas em canvas em páginas web, com suporte tanto à instalação via NPM quanto à inclusão direta por CDN
- A API básica
confetti() permite ajustar número de partículas, ângulo, dispersão, velocidade, gravidade, cores, formas, posição, z-index e mais com um único objeto de opções; em ambientes com suporte a Promise, também é possível receber o momento em que a animação termina
- Para usuários de Reduced Motion, há a opção
disableForReducedMotion; atualmente seu valor padrão é false, mas isso pode mudar em uma futura major release
- É possível criar formas personalizadas com SVG Path e texto, e implementar efeitos como emoji confetti além das formas padrão
square, circle e star
confetti.create() cria uma instância em um canvas específico e oferece opções globais como resize e useWorker, mas com useWorker: true o controle do canvas é transferido para um web worker, e manipulá-lo na thread principal causará erro
Instalação e formas de execução
- É possível verificar o funcionamento da biblioteca na página de demonstração
- Pode ser instalada como pacote NPM
npm install --save canvas-confetti
- Em builds do projeto, pode ser usada com
require('canvas-confetti')
- Esta biblioteca é um componente cliente e não roda em Node
- O README orienta que o projeto deve ser compilado com uma ferramenta como webpack
- Em páginas HTML, também pode ser incluída diretamente com um script de CDN
<script src="https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.4/…;
- Ao usar CDN, recomenda-se usar a versão mais recente disponível no momento da inclusão no projeto; a lista completa pode ser consultada na releases page
Suporte a Reduced Motion
- Alguns usuários podem não querer movimento em sites ou preferir reduzi-lo, e o navegador pode comunicar isso com
prefers-reduced-motion
- Ao usar a opção
disableForReducedMotion, é possível não exibir confete para usuários que tenham dificuldade com animações confusas
- Atualmente, o valor padrão dessa opção é
false
- Há a possibilidade de mudar o padrão em uma futura major release, e opiniões fortes sobre isso podem ser enviadas em uma issue
- Se
disableForReducedMotion desativar o confete, a Promise de confetti() será resolvida imediatamente
API básica e comportamento de Promise
- Quando instalada via NPM, pode ser carregada em builds do projeto como componente cliente; na versão via CDN, fica exposta como a função
confetti em window
confetti([options]) recebe um único objeto opcional de opções
- Se
window.Promise existir, a função retorna uma Promise que informa quando a animação termina
- Em ambientes sem Promise, como o IE, ela retorna
null
- É possível usar um polyfill de Promise
- Também é possível fornecer manualmente uma implementação com
confetti.Promise = MyPromise
- Se
confetti for chamado várias vezes antes da conclusão, a mesma Promise será retornada em todas elas
- Internamente, o mesmo elemento canvas é reutilizado, e novos confetes são adicionados enquanto a animação existente continua
- A Promise retornada por cada chamada será resolvida depois que todas as animações terminarem
Principais opções
particleCount: quantidade de confetes lançados, padrão 50
angle: ângulo de lançamento, padrão 90, em que 90 significa para cima
spread: amplitude de dispersão a partir do centro, padrão 45
startVelocity: velocidade inicial, padrão 45
decay: taxa de redução da velocidade, padrão 0.9
- Deve permanecer entre 0 e 1; fora desse intervalo, a velocidade pode aumentar
gravity: intensidade com que as partículas são puxadas para baixo, padrão 1
0.5 representa meia gravidade, e não há limite, então também é possível fazê-las subir
drift: desvio lateral, padrão 0
- Negativo significa para a esquerda, positivo para a direita
flat: desativa o efeito de inclinação e oscilação típico de um confete 3D realista; padrão false
ticks: número de movimentos do confete, padrão 200
origin: posição inicial do lançamento
origin.x: posição x na página, 0 é esquerda, 1 é direita, padrão 0.5
origin.y: posição y na página, 0 é topo, 1 é base, padrão 0.5
colors: array de strings de cor em formato HEX
shapes: array de formas de confete
- Os valores embutidos padrão são
square, circle, star
- Por padrão,
square e circle são misturados em partes iguais
- É possível ajustar a proporção com arrays como
['circle', 'circle', 'square']
scalar: escala de cada partícula, padrão 1
zIndex: camada de exibição do confete, padrão 100
disableForReducedMotion: desativa o confete para usuários com preferência por Reduced Motion
Criando formas personalizadas
confetti.shapeFromPath({ path, matrix? }) cria uma forma de confete personalizada a partir de uma SVG Path string
- Há algumas restrições para formas baseadas em Path
- Todos os paths são tratados como formas preenchidas; path com stroke não é implementado
- O path é limitado a uma única cor
- Todo path precisa de uma transform matrix válida
- Como o cálculo da matrix tem custo, é recomendável calculá-la uma vez por path durante o desenvolvimento e armazená-la em cache
- A matrix é sempre a mesma para o mesmo valor de path
- Ao atualizar a biblioteca, é recomendável gerar e armazenar a matrix novamente para manter a forward compatibility
- Confetes baseados em path ficam limitados a navegadores com suporte a
Path2D
- O valor retornado é um objeto
Shape, que pode ser inserido diretamente no array shapes
var triangle = confetti.shapeFromPath({ path: 'M0 10 L5 0 L10 10z' });
confetti({
shapes: [triangle]
});
confetti.shapeFromText({ text, scalar?, color?, fontFamily? }) cria formas de confete baseadas em texto e pode usar emoji Unicode padrão
- Formas baseadas em texto são ideais para emoji confetti
- Como o confete oscila, geralmente funciona melhor com um único caractere quase quadrado, especialmente emoji
- Como o texto é rasterizado em vez de desenhado toda vez, mudar muito a escala depois da criação pode deixá-lo borrado
- Se você pretende usar
scalar nas opções do confete, é melhor usar o mesmo valor de scalar ao criar a forma
- As opções de texto são
text, scalar, color, fontFamily
- O padrão de
fontFamily segue as convenções nativas de renderização de emoji do sistema operacional, com fallback para sans-serif
- Ao usar web fonts, a fonte deve estar carregada antes da renderização do confete
var scalar = 2;
var pineapple = confetti.shapeFromText({ text: '🍍', scalar });
confetti({
shapes: [pineapple],
scalar
});
Canvas personalizado e renderização com worker
confetti.create(canvas, [globalOptions]) cria uma instância da função de confete que usa um canvas específico
- Isso é útil quando se quer limitar o confete a uma área específica da página
- Por padrão, esse método não modifica o canvas além de desenhar nele
- O tamanho visual do canvas pode ser alterado com CSS, mas isso não muda o tamanho real da imagem do canvas, o que pode fazer com que ela fique esticada e borrada
- Ao ativar a opção
resize, a biblioteca ajusta o tamanho da imagem do canvas e também responde a mudanças no tamanho da janela ou rotação em dispositivos móveis
- Não se deve inicializar várias instâncias de confete no mesmo elemento canvas; o ideal é manter a instância personalizada criada
-
Opções globais
resize: define se o tamanho da imagem do canvas será ajustado e mantido conforme mudanças na janela; padrão false
useWorker: renderiza a animação de confete em um web worker assíncrono quando possível; padrão false
- No padrão, a animação sempre roda na thread principal
- Se o navegador oferecer suporte, a animação pode rodar fora da thread principal para não bloqueá-la
- Em navegadores sem suporte, esse valor é ignorado
disableForReducedMotion: faz com que essa instância de confete sempre respeite a preferência de Reduced Motion do usuário
-
Cuidados com useWorker: true
- Ao usar
useWorker: true, o controle do canvas é transferido para o web worker
- Nesse caso, manipulá-lo na thread principal causará erro, exceto removê-lo do DOM
- Se for necessário manipular o canvas diretamente, não se deve usar a opção
useWorker
var myCanvas = document.createElement('canvas');
document.body.appendChild(myCanvas);
var myConfetti = confetti.create(myCanvas, {
resize: true,
useWorker: true
});
myConfetti({
particleCount: 100,
spread: 160
});
Interrompendo a animação e padrões de exemplo
confetti.reset() interrompe a animação, remove todo o confete e resolve imediatamente qualquer Promise pendente
- Instâncias separadas criadas com
confetti.create() têm seu próprio método reset
confetti();
setTimeout(() => {
confetti.reset();
}, 100);
- A execução básica consiste em chamar
confetti() sem argumentos
- É possível lançar muito confete com
particleCount: 150
- É possível criar um efeito mais espalhado com
spread: 180
- Usar
Math.random() em origin permite criar pequenos efeitos de explosão em posições aleatórias da página
- O exemplo do README mostra um padrão que usa
requestAnimationFrame para lançar confete continuamente nas bordas esquerda e direita durante 30 segundos
1 comentários
Opiniões no Hacker News
O truque aqui para criar animações com bom desempenho é desenhar em um canvas e então colocar esse canvas à frente de todos os outros elementos, mas com eventos de ponteiro desativados para que ainda seja possível interagir com a página
Lembra os bons tempos de 2015, quando eu fazia desenvolvimento web no ensino médio. Criei um sitezinho com confete para convidar uma garota para ir comigo ao homecoming; olhando para trás, foi extremamente nerd
Na época, fazer um site para alguém parecia um superpoder. Pelo período, acho que não era este pacote, mas a animação era bem boa
Gosto desses pequenos projetos puramente divertidos. Foi por isso que comecei a programar e ainda hoje continua sendo uma grande motivação
Gostei desta parte da página de demonstração:
Esse tipo de obsessão por detalhes é raro e, sempre que o encontro, seja em visualização estatística, adereços de cinema ou confete em sites, acho precioso
Como solução, eu tentaria alterar a própria distribuição aleatória. Eu checaria na prática, mas suspeito que a distribuição real seja mais próxima de uma distribuição gaussiana
Adicionei confete no painel administrativo que aparece quando um vendedor fecha uma venda, e é surpreendentemente divertido e motivador
Teria sido melhor se a função reset se chamasse confetti.resetti()
"confetti.resetti = confetti.reset"Essa abordagem talvez tenha algum custo de engenharia de software, mas, como qualquer observador atento perceberia claramente, os benefícios superam esmagadoramente os custos, então acho que é só fazer
Além de ser uma biblioteca legal e útil, é um bom exemplo de módulo profundo, como John Ousterhout descreve em Philosophy of Software Design
A versão mais básica, ou seja, invocar confete, é muito fácil de usar, mas, olhando as opções, dá para obter bastante coisa: neve, cores específicas, vários efeitos de confete etc.
Legal e impressionante
Ao mesmo tempo, não quero ver isso rodando em nenhum site que eu uso. Especialmente não quero confete acompanhando pop-ups de newsletter ou quando adiciono um produto ao carrinho
Curiosamente, esse efeito pode ser usado de forma bem eficaz. Não sei quanto a esse formato de tela inteira, mas em um software de gerenciamento de projetos usado por um cliente que visitei recentemente, ao fechar um item o botão ficava verde e esse efeito aparecia
Era sutil, mas perceptível, e depois da reunião outro desenvolvedor e eu comentamos: “foi um efeito bem legal”. Passava a sensação de “ótimo, há progresso!”
Só precisa ser opcional
Um uso legítimo talvez seja algo como o botão de curtir do YouTube. Tem uma animação legal e, no app móvel, o aparelho também vibra. É uma experiência do usuário muito agradável
É possível configurar o navegador para preferir redução de movimento. Operadores de sites e mantenedores de bibliotecas devem respeitar isso ao implementar coisas como confete. Esta biblioteca, em particular, tem a opção
disableForReducedMotionHá lugares em que esse tipo de efeito combina. Por exemplo, ao concluir um jogo
Usamos esta biblioteca quando alguém atende a determinada qualificação. Dá um efeito bem bom ao fluxo de onboarding
Também existe a biblioteca Party.js: https://party.js.org/
Então qual das duas é menor?
10,4 kB minificado, 4,2 kB minificado + Gzip
https://bundlephobia.com/package/canvas-confetti@1.9.2
28,3 kB minificado, 7,4 kB minificado + Gzip
https://bundlephobia.com/package/party-js@2.2.0
Dito isso, não sei bem como o bundlephobia funciona. Talvez não mostre da melhor forma o tamanho final do pacote. Provavelmente não reflete code splitting nem importar apenas o necessário. Vejo só como uma visão geral rápida e aproximada
Pelo Gzip, parece que o confetti vence por alguns KB, então, a menos que você realmente precise espremer esses poucos KB, ambos servem dependendo de qual deles tem os recursos de que você precisa
O script do post original parece ter desempenho muito melhor no celular
A biblioteca do post original parece ter desempenho muito melhor. No meu computador de trabalho antigo, o Party.js já mostra um pouco de atraso depois de apenas 3 cliques
O canvas-confetti só começa a atrasar depois de eu clicar sem parar por alguns segundos, provavelmente criando mais de 30 instâncias de confete e muitas partículas
Resolvo palavras cruzadas no downforacross.com e, quando completo um quebra-cabeça, aparece confete
Talvez eles possam usar parte deste código com melhor desempenho para deixar a experiência mais leve
Dito isso, a menos que seja um site “divertido” ou algo de uso raro, não quero ver esse tipo de animação aparecendo por toda parte
Acho que não precisava colocar useful no título