O que swarm-build faz?
swarm-build é um módulo ES sem dependências (src/swarm-build.js, cerca de 600 linhas) mais um arquivo CSS opcional (src/swarm-build.css).
Possui dois modos:
- Modo Imagem,
swarmBuild(img): uma tela em branco cobre uma<img>(ou um elemento combackground-imageCSS), e cada região é varrida após um cursor arrastar sua caixa com um badge ao vivoL × A. - Modo Página,
swarmBuildPage("[data-swarm-block]"): os elementos marcados de uma página ao vivo desaparecem, então os cursores os reconstroem um por um. Blocos de texto simples (títulos, parágrafos curtos) são digitados. Um bloco dentro de outro aguarda seu pai. Blocos abaixo da dobra esperam até serem rolados para visualização, então o enxame segue o leitor pela página.
Em ambos os modos, os cursores desaparecem no final e cada alteração feita pela biblioteca é desfeita.
1. Instalar
Copie src/swarm-build.js, src/swarm-build.d.ts e src/swarm-build.css deste repositório para o projeto (por exemplo, lib/swarm-build/) e importe-os com caminhos relativos.
Não há dependências e nenhuma etapa de build.
Apenas se npm view swarm-build mostrar que o pacote está publicado: npm install swarm-build, depois import { swarmBuild } from "swarm-build" e import "swarm-build/style.css".
2a. Modo Página: marque os blocos
- Leia a marcação da página e escolha os blocos que um designer desenharia: barra de navegação, título herói, parágrafo herói, cada botão, cada cartão, gráfico, tabela, rodapé. Uma landing page típica tem de 25 a 60.
- Adicione
data-swarm-blockem cada um.- Marque contêineres (um cartão) e o texto dentro deles (seu título e parágrafo) para obter "moldura primeiro, depois preenchimento".
- Marque títulos e parágrafos curtos cujo conteúdo seja texto simples: esses são digitados. Elementos com marcação inline (links,
<b>) são varridos. - Pule wrappers que fazem apenas layout (grades, seções sem fundo próprio). Escondê-los esconde tudo dentro de uma vez.
- Não marque UI fixa ou sticky que o visitante precisa imediatamente, como banner de cookies ou widget de chat.
- Chame a função após o conteúdo estar no DOM:
import { swarmBuildPage } from "./lib/swarm-build/swarm-build.js";
swarmBuildPage("[data-swarm-block]", { speed: 1.5 });
Para esconder os blocos antes do script rodar (sem flash da página finalizada), adicione também data-swarm-pending em cada bloco e inclua o arquivo CSS.
2b. Modo Imagem: escolha as regiões
As regiões decidem o que será construído e em qual ordem. Boas regiões fazem a animação parecer intencional, então não pule esta etapa.
- Abra a imagem e observe-a (leia o arquivo de imagem ou tire um print da página).
- Liste os 6 a 15 blocos significativos que um designer desenharia: barra de navegação, cabeçalho, barra de busca, cartões, gráfico, lista, rodapé.
- Escreva cada bloco como
x y w hem porcentagem da largura e altura da imagem (0 a 100). Meça a partir do tamanho em pixels:x = left / imageWidth * 100. - Ordene-os como uma pessoa construiria a tela: chrome primeiro (barra, cabeçalho), depois conteúdo de cima para baixo.
- Deixe pequenos espaços. Qualquer coisa não coberta por regiões aparece quando a tela desvanece no final, então as regiões não precisam cobrir cada pixel.
Coloque-os na imagem como data-swarm-regions="0 0 18 100, 21 3.5 47 5.5, ...", ou passe regions: [{ x, y, w, h }] em JavaScript.
Sem regiões, a biblioteca usa um layout genérico de barra lateral, cabeçalho e cartões, que raramente corresponde à imagem.
3. Conecte
HTML puro:
<link rel="stylesheet" href="/lib/swarm-build/swarm-build.css" />
<img src="/shot.png" alt="..." width="1200" height="800"
data-swarm-pending
data-swarm-regions="0 0 18 100, 21 3.5 47 5.5, 21 13 24 16" />
<script type="module">
import { swarmBuild } from "/lib/swarm-build/swarm-build.js";
swarmBuild(document.querySelector("img[data-swarm-regions]"));
</script>
React ou Next.js (componente cliente):
"use client";
import { useEffect, useRef } from "react";
import { swarmBuild } from "@/lib/swarm-build/swarm-build.js";
export function BuiltImage(props: React.ComponentProps<"img"> & { regions: string }) {
const ref = useRef<HTMLImageElement>(null);
useEffect(() => {
const swarm = swarmBuild(ref.current!);
return () => swarm.destroy();
}, []);
const { regions, ...img } = props;
return <img ref={ref} {...img} data-swarm-pending="" data-swarm-regions={regions} />;
}
Para Vue, Svelte ou Astro, faça o mesmo: chame swarmBuild(el) após a montagem e destroy() na desmontagem.
Várias imagens (por exemplo, um iPad e um telefone) podem compartilhar um enxame: swarmBuild([ipadImg, phoneImg]).
4. Opções
| Opção | Padrão | Uso |
|---|---|---|
regions (por alvo) | data-swarm-regions, depois genérico | O que construir, em ordem |
agents | 6 nomes e cores embutidos | [{ name, color, text }], combine com a paleta e público do site |
speed | 1 | 2 reduz pela metade a duração (cerca de 6 a 8s em 1 para 10 a 15 regiões) |
start | "visible" | "now" inicia imediatamente em vez de quando a imagem entra na tela |
canvas | "#fff" | Cor da tela não construída; combine com o fundo da captura de tela |
zIndex | 2147483000 | Empilhamento da camada fixa de cursores; reduza se um modal ou cabeçalho precisar ficar acima |
onDone | nenhum | Chamado uma vez quando a construção completa ou finish() é chamado |
typeSpeed (modo página) | 1 | Velocidade do texto digitado; 2 digita duas vezes mais rápido |
O valor de retorno possui finished, finish() e destroy():
finishedé uma Promise que sempre resolve, independentemente de como o enxame termina.finish()pula para a imagem finalizada, eonDoneé executado.destroy()remove tudo imediatamente, eonDonenão é executado. Chame-o na desmontagem.setSpeed(n)altera a velocidade enquanto executa.
No modo página, start padrão é "now"; no modo imagem, "visible".
5. Regras a manter
data-swarm-pendingmais o arquivo CSS mantêm a imagem oculta até o script iniciar, para que ela não apareça finalizada primeiro. Se o script nunca rodar, o CSS mostra a imagem após 4 segundos. Deixe o atributo de fora quando a imagem for o elemento LCP da página e o tempo de carregamento for mais importante que o efeito.- Movimento reduzido (
prefers-reduced-motion: reduce) mostra a imagem finalizada e nunca anima. Não sobrescreva isso. - Os cursores vivem em uma camada fixa de viewport inteira acima da página (
pointer-events: none). - Enquanto executa, a biblioteca insere uma sobreposição logo após a imagem, dentro do pai da imagem. Se o pai era estático, também define
position: relativenele. Ambos são desfeitos no final.- Dê à imagem um pai de nível de bloco próprio, como uma
<div>ou<figure>. Pais inline como<a>ou<picture>podem deslocar a sobreposição. - CSS de irmãos como
:last-childou Tailwindspace-y-*podem mudar brevemente enquanto a sobreposição está presente.
- Dê à imagem um pai de nível de bloco próprio, como uma
- A imagem é copiada como um background CSS esticado à caixa de conteúdo da imagem. Bordas e padding na imagem são aceitáveis, mas cortes como
object-fit: coveroucontainnão são reproduzidos. - Sob uma CSP restritiva ou Trusted Types, nenhuma alteração é necessária: a biblioteca constrói cada elemento com chamadas DOM, define estilos através do CSSOM e não usa
innerHTML,<style>ou script inline. A URL da imagem deve ser permitida porimg-src.
6. Verificar
- Carregue a página e assista a uma construção completa. Cada caixa deve cair em um bloco real de UI da imagem, não em espaço vazio ou cruzando dois blocos.
- Verifique uma largura de telefone (375px). Badges e tags de nome têm tamanho fixo, então imagens muito pequenas podem parecer cheias; use menos regiões maiores ali.
- Ative movimento reduzido no sistema operacional ou nas ferramentas de desenvolvedor do navegador: a imagem deve aparecer imediatamente, sem cursores.
- Verifique o console por erros e se nenhum elemento
aria-hiddendo enxame permanece no DOM após o término.
