close
HyperFramesとAnime.js 4.4でカッコいいテキストアニメーションを作ってみる

HyperFramesとAnime.js 4.4でカッコいいテキストアニメーションを作ってみる

2026.05.07

どうも!オペ部の西村祐二です!

Anime.js v4.4.0に、テキストをランダムな文字列から徐々に確定表示する scrambleText() が追加されました。

今回は、HyperFrames と Anime.js 4.4 を使って、クラスメソッドの経営理念をベースにした短いタイトルアニメーションを作ってみました。テキストは経営理念ページの文面を動画向けに短い英語へ置き換えています。

参考にしたのは、Anime.js作者のJulian GarnierさんがまとめているAnime.js v4 examples collection on CodePenです。CodePenのサンプルはWeb上で動かす前提のため、今回はHyperFramesでMP4にレンダリングできる形へ寄せています。

作ったもの

6秒のテキストアニメーションです。

classmethod-blog

動画の流れは次のようにしました。

  1. OpenCuriousCraftedDeep などの短い単語をグリッド状に表示する
  2. 中央の Open Thinking をスクランブル表示する
  3. 周囲の単語を消しながら、中央の文字を classmethod に変化させる
  4. 最後に Empowering Creativity for Everyone. を表示する

使ったもの

検証環境は以下です。

ツール バージョン
Node.js v25.8.2
pnpm 10.33.0
FFmpeg 8.1
Anime.js 4.4.0

HyperFrames側の構成

HyperFramesでは、HTMLのルート要素にcomposition情報を持たせます。今回の動画は1280x720、6秒にしました。

<div
  id="root"
  data-composition-id="main"
  data-start="0"
  data-duration="6"
  data-width="1280"
  data-height="720"
>

中身は大きく分けると、単語グリッドと最後のタグラインです。

<div class="grid" id="grid">
  <div class="row"><p>Open</p><p>Curious</p></div>
  <div class="row"><p>Crafted</p><p>Deep</p><p>Refined</p></div>
  <div class="row">
    <p>Build</p>
    <p class="center"><span class="grad" id="hero">Open Thinking</span></p>
    <p>Make</p>
  </div>
  <div class="row"><p>Inspire</p><p>Empower</p><p>Bold</p></div>
  <div class="row"><p>Free</p><p>Create</p></div>
</div>

<p id="tagline" class="tagline">Empowering Creativity for Everyone.</p>

CodePenのサンプルでは、グリッド、stagger、テキスト分割、タイムラインなどの見せ方が紹介されています。今回はその雰囲気を参考にしつつ、会社紹介の動画向けに要素数を絞り、見出し中心の構成にしました。

data-composition-id="main" の値は、後述する window.__timelines['main'] のキーと一致させる必要があります。

Anime.js 4.4のscrambleTextを使う

今回の主役はscrambleText()です。Anime.js 4.4のUMD版を読み込み、createTimeline()scrambleText()stagger() を使っています。

<script src="https://cdn.jsdelivr.net/npm/animejs@4.4.0/dist/bundles/anime.umd.min.js"></script>
const { createTimeline, text, stagger } = anime;
const { scrambleText } = text;

const tl = createTimeline({ autoplay: false, defaults: { ease: 'inOut(3)' } });

中央の Open Thinking は、ランダムな文字から確定していくようにしています。

tl.add('#hero', {
  scale: { from: 2.2 },
  innerHTML: scrambleText({
    override: ' ',
    from: 'center',
    duration: 500,
    cursor: '░▒▓█',
    ease: 'inQuad',
  }),
}, '<<');

周囲の単語は、中心から外側へ広がるようにstaggerさせました。

tl.add('#grid p:not(.center)', {
  innerHTML: scrambleText({
    override: ' ',
    from: 'center',
    duration: 500,
    revealDelay: 180,
    cursor: '░▒▓',
    perturbation: 0.3,
  }),
}, stagger([200, 600], { grid: true, from: 'center', ease: 'out(3)', start: '<<' }));

最後は中央の文字を classmethod に変化させ、タグラインへ切り替えます。

tl.add('#hero', {
  ease: 'inOutExpo',
  duration: 1300,
  innerHTML: scrambleText({
    text: 'classmethod',
    chars: 'shades',
    override: false,
    from: 'random',
    duration: 1000,
    perturbation: 0.4,
    revealRate: 24,
    settleRate: 28,
    settleDuration: 280,
  }),
}, '<<');

tl.set('#grid', { opacity: 0 }, 3300);
tl.set('#tagline', { opacity: 1 }, 3300);

動画レンダーで気をつけたところ

Webページ上のアニメーションなら、ページロード後にそのまま再生すれば十分です。一方、HyperFramesでMP4にする場合は、フレーム単位で任意の時刻へ移動できる必要があります。

そのため、Anime.jsのタイムラインは autoplay: false にし、GSAP のpaused timelineをHyperFrames用の時計として使いました。

window.__hfAnime = window.__hfAnime || [];
window.__timelines = window.__timelines || {};

tl.init();
tl.seek(0);

window.__hfAnime.push({ instance: tl, offsetMs: 0, durationMs: tl.duration });

const masterDur = 6;
const master = gsap.timeline({ paused: true });
master.to({}, { duration: masterDur });

master.eventCallback('onUpdate', () => {
  const t = master.time();
  for (const item of window.__hfAnime) {
    const localMs = t * 1000 - item.offsetMs;
    const clamped = Math.max(0, Math.min(item.durationMs, localMs));
    if (typeof item.instance.seek === 'function') {
      item.instance.seek(clamped);
    }
  }
});

window.__timelines['main'] = master;

HyperFramesから見るとGSAPがメインの時間軸で、Anime.jsはその時間に合わせて seek() されるテキストエフェクト担当、という分担です。これにより、ブラウザ上の実時間ではなく、動画のフレーム時刻に同期できます。

window.__timelines のキー('main')は、HTMLの data-composition-id と同じ値にしておく必要があります。

ブラウザで動作確認する

scrambleText() のオプションを微調整する場合は、毎回MP4にレンダーすると時間がかかります。HyperFramesにはStudio(プレビュー画面)が付いていて、ブラウザ上でフレーム単位の確認ができました。

cd <作業ディレクトリ>
pnpm dlx hyperframes preview

起動すると http://localhost:3002 でStudioが開きます。タイムラインバーをドラッグすればGSAPの seek() 経由でAnime.jsも追従するため、perturbationrevealRate を動かしたときの見た目を即座に比較できます。

Pasted image 20260507175051

調整サイクルとして次の流れが扱いやすかったです。

  1. Studioを起動したまま、index.html のscrambleTextオプションを書き換える
  2. Studioが自動リロードするので、タイムラインを再生 or スクラブして確認する
  3. 納得した値になったら次節のレンダーコマンドでMP4化する

scrambleTextのオプションは値の効き方がパッと見では予想しにくいため、Studioでスクラブしながらの確認は微調整の効率が良かったです。

レンダーコマンド

調整が済んだら、ブログ用のMP4としてレンダーします。

pnpm dlx hyperframes render -o renders/classmethod-blog.mp4 --quality draft

Claude Codeと組み合わせて良かったところ

Claude Codeと組み合わせて一番ありがたかったのは、Anime.jsやHyperFramesの使い方を細かく知らなくても、ひとまず動くものができあがってくるところでした。今回もAnime.jsを触るのは初めてでしたが、「グリッド状にテキストを並べてスクランブル表示してほしい」と伝えるだけで、scrambleText() のオプションを選びつつ動くHTMLを返してくれて、そこから手を入れていく形で進められました。

参考までに、最初に渡したプロンプトはこのような内容です。

HyperFramesとAnime.js 4.4で、会社理念を英語化したテキストアニメーションを作ってください。
CodePenのAnime.js v4 examples collectionを参考に、グリッドとscrambleTextを使ってください。
出力は renders/classmethod-blog.mp4 にしてください。

このくらいの粒度でもWeb上では動くHTMLが上がってきます。

ただ、そのままレンダーすると、Webアニメーションとしては動いても動画レンダーで扱いにくい構成(タイムラインがフレーム時刻に同期しない、フレーム抜けが起きる、など)になりがちで、そこから先はAIエージェントが「レンダーしてみる → 出力フレームを見て不整合を調査 → コードを直す」というサイクルを何度も繰り返していました。

前述の window.__timelines への登録のような動画化の前提条件は、プロンプトの最初に「HyperFramesでMP4にレンダリングする前提で書いてください」と一言添えておくと、その往復回数を減らせそうです。

もちろん、perturbationrevealRate などの値を自分の好みに寄せて微調整するには、最終的にライブラリの使い方を知る必要があります。それでも、最初から仕様を読み込んで一から書くよりは、動くものを起点に挙動を観察しながら覚えていけるので、初めて触るライブラリのはじめの一歩としてはとても入りやすかったです。

まとめ

今回Anime.jsを触るのは初めてだったのですが、scrambleText() 以外にも splitText() や stagger、SVGモーション、CSSプロパティアニメーションなど、短いコードで作れる表現が一通り揃っていて、思っていた以上にいろいろなことができそうな印象でした。CodePenのexamples collectionを眺めているだけでも作りたいものが浮かんでくるくらいで、これからも長く付き合えそうなライブラリだと感じました。

そのなかでも今回試したAnime.js 4.4のscrambleText()は、短いブランドメッセージのリビール演出を少ないコードで実装でき、chars プリセットや perturbationrevealRate などの調整値の組み合わせで雰囲気を変えやすいのが好みでした。

CodePenのようなWeb向けサンプルを動画化する場合の学びとしては、Anime.jsとHyperFramesを直接つなぐのではなく、GSAPのpaused timelineを挟んで seek() で同期させる形が扱いやすいというところでした。次は、複数のテキストエフェクトを組み合わせたシーンチェンジや、ロゴアニメーションへの応用も試してみたいです。

誰かの参考になれば幸いです。


関連リンク:

この記事をシェアする

関連記事