swarm-build は何をしますか?
swarm-buildは、依存関係のないESモジュール1つ(src/swarm-build.js、約600行)と、オプションのCSSファイル(src/swarm-build.css)で構成されています。
2つのモードがあります。
- Imageモード、
swarmBuild(img):<img>(またはCSSbackground-imageを持つ要素)を白紙のキャンバスで覆い、各領域がカーソルによって矩形が引き出された後、ライブのW × Hバッジと共にスイープインします。 - Pageモード、
swarmBuildPage("[data-swarm-block]"):ライブページのマークされた要素が非表示になり、その後カーソルがそれらを1つずつ再構築します。プレーンテキストのブロック(見出し、短い段落)はタイプライターで入力されます。他のブロック内にあるブロックは、親ブロックの完了を待ちます。画面外のブロックはスクロールして表示されるまで待機するため、群衆はページを読み進めるユーザーに追従します。
両モードとも、最後にカーソルは消え、ライブラリが行った変更はすべて元に戻されます。
1. インストール
このリポジトリから src/swarm-build.js、src/swarm-build.d.ts、src/swarm-build.css をプロジェクトにコピーし(例:lib/swarm-build/)、相対パスでインポートします。
依存関係はなく、ビルド手順も必要ありません。
npm view swarm-build でパッケージが公開されている場合にのみ:npm install swarm-build を実行し、import { swarmBuild } from "swarm-build" および import "swarm-build/style.css" でインポートします。
2a. Pageモード:ブロックをマークする
- ページのマークアップを読み、デザイナーが描くだろうブロック(ナビバー、ヒーロー見出し、ヒーロー段落、各ボタン、各カード、チャート、テーブル、フッター)を選びます。一般的なランディングページでは25から60個程度になります。
- 各ブロックに
data-swarm-blockを追加します。- コンテナ(カード)とその内部のテキスト(見出しや段落)をマークすると、「枠を先に、その後で中身を埋める」動作になります。
- 内容がプレーンテキストの見出しや短い段落をマークすると、タイプライター入力されます。インラインマークアップ(リンク、
<b>)を含む要素はスイープインします。 - レイアウト専用のラッパー(グリッド、背景を持たないセクションなど)はスキップします。これらを非表示にすると、内部のすべてが一度に非表示になります。
- 訪問者がすぐに必要とする固定またはスティッキーなUI(Cookieバナーやチャットウィジェットなど)はマークしないでください。
- コンテンツがDOMに存在した後に呼び出します:
import { swarmBuildPage } from "./lib/swarm-build/swarm-build.js";
swarmBuildPage("[data-swarm-block]", { speed: 1.5 });
スクリプト実行前にブロックを非表示にするには(完成したページが一瞬表示されるのを防ぐ)、各ブロックに data-swarm-pending も追加し、CSSファイルをインクルードします。
2b. Imageモード:領域を選択する
領域は何がどの順序で構築されるかを決定します。 適切な領域がアニメーションを意図的に見せます。この手順は省略しないでください。
- 画像を開いて見てください(画像ファイルを読むか、ページのスクリーンショットを撮ってください)。
- デザイナーが描くだろう意味のあるブロックを6~15個リストアップします:ナビゲーションレール、ヘッダー、検索バー、カード、チャート、リスト、フッター。
- 各ブロックを画像の幅と高さのパーセンテージ(0~100)で
x y w hとして記述します。ピクセルサイズから測定します:x = left / imageWidth * 100。 - 人が画面を構築する順序に並べます:クローム(レール、ヘッダー)、次にコンテンツを上から下へ。
- 小さな隙間は残しておきます。どの領域もカバーしない部分は、最後にキャンバスがフェードアウトするときに表示されます。そのため、すべてのピクセルを領域でカバーする必要はありません。
画像に data-swarm-regions="0 0 18 100, 21 3.5 47 5.5, ..." として配置するか、JavaScriptで regions: [{ x, y, w, h }] を渡します。
領域がない場合、ライブラリは汎用的なサイドバー、ヘッダー、カードのレイアウトを使用しますが、これは画像に合致することはほとんどありません。
3. 組み込む
素のHTML:
<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またはNext.js(クライアントコンポーネント):
"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} />;
}
Vue、Svelte、Astroの場合も同様に、マウント後に swarmBuild(el) を呼び出し、アンマウント時に destroy() を呼び出します。
複数の画像(例:iPadとスマートフォン)で1つの群衆を共有することもできます:swarmBuild([ipadImg, phoneImg])。
4. オプション
| オプション | デフォルト | 用途 |
|---|---|---|
regions(ターゲットごと) | data-swarm-regions、次に汎用 | 何をどの順序で構築するか |
agents | 6つの組み込みの名前と色 | [{ name, color, text }]、サイトのパレットとオーディエンスに合わせる |
speed | 1 | 2 で所要時間が半分になる(10~15領域で約6~8秒、1の場合) |
start | "visible" | "now" にすると画像がスクロールして表示されるのを待たずに即座に開始 |
canvas | "#fff" | 未構築のキャンバスの色。スクリーンショットの背景に合わせる |
zIndex | 2147483000 | 固定カーソルレイヤーの重なり順。モーダルやヘッダーを前面に表示する必要がある場合は下げる |
onDone | なし | ビルドが完了するか finish() が呼び出されたときに1回呼び出される |
typeSpeed(Pageモード) | 1 | タイプライター入力の速度。2 で2倍速 |
戻り値には finished、finish()、destroy() があります:
finishedは、群衆の終了方法に関わらず常に解決されるPromise。finish()は完成した画像にジャンプし、onDoneが実行されます。destroy()は即座にすべてを削除し、onDoneは実行されません。アンマウント時に呼び出します。setSpeed(n)は実行中に速度を変更します。
Pageモードでは start のデフォルトは "now" になり、Imageモードでは "visible" になります。
5. 守るべきルール
data-swarm-pendingとCSSファイルにより、画像はスクリプトが開始するまで非表示に保たれるため、完成した状態が一瞬表示されることはありません。スクリプトが実行されなかった場合、CSSは4秒後に画像を表示します。画像がページのLCP要素であり、読み込み時間がエフェクトよりも重要な場合は、属性を外してください。- 動きの減少設定(
prefers-reduced-motion: reduce)は、完成した画像を表示し、アニメーションは行いません。これを上書きしないでください。 - カーソルはページの上に固定された、全ビューポートのレイヤーに配置されます(
pointer-events: none)。 - 実行中、ライブラリは画像の直後、画像の親要素内にオーバーレイを挿入します。親がstaticだった場合、
position: relativeも設定されます。両方とも最後に元に戻されます。- 画像には独自のブロックレベル親要素(
<div>や<figure>など)を指定してください。<a>や<picture>などのインライン親要素ではオーバーレイの位置がずれる可能性があります。 :last-childや Tailwind のspace-y-*などの兄弟CSSは、オーバーレイが存在する間に一時的に変化する可能性があります。
- 画像には独自のブロックレベル親要素(
- 画像は画像のcontent boxに引き伸ばされたCSS背景としてコピーされます。画像のボーダーやパディングは問題ありませんが、
object-fit: coverやcontainによるトリミングは再現されません。 - 厳格なCSPやTrusted Typesの下では変更は不要です。ライブラリはすべての要素をDOM呼び出しで構築し、CSSOMを通じてスタイルを設定し、
innerHTML、<style>、インラインスクリプトは使用しません。画像URLはimg-srcで許可されている必要があります。
6. 確認
- ページを読み込み、1回の完全なビルドを確認します。各ボックスが画像の実際のUIブロックに配置され、空きスペースや2つのブロックにまたがっていないことを確認します。
- スマートフォン幅(375px)で確認します。バッジや名前タグは固定サイズのため、非常に小さな画像では密集して見える可能性があります。その場合は、より少ない、より大きな領域を使用します。
- OSまたはブラウザの開発者ツールで動きの減少設定をオンにします:画像はカーソルなしで即座に表示される必要があります。
- コンソールにエラーがないこと、また完了後に
aria-hiddenの群衆要素がDOMに残っていないことを確認します。
