Webデザイン
投稿日:

動きすぎないのが心地いい!さりげないJSアニメーションの作り方

動きすぎないのが心地いい!さりげないJSアニメーションの作り方

こんにちは、Webディレクター兼フロントエンドエンジニアのBigViです。ホーチミンから日本に移り住んで、現在は東京で暮らしてます。今日はWeb制作でよく使う「アニメーションの使い方」について話したいと思います。いっぱいアニメーションをつけることもよくありますが、今回は「さりげなさ」をキーワードに、JavaScriptを使ったアニメーションの実装方法を紹介します。

目立ちすぎない、心地よいUIを目指して

Web制作をしていると、クライアントから「もっと目立たせたい」や「動きを加えたい」との依頼がよくあります。アニメーションは確かに目を引きますが、動きが多すぎるとユーザーが混乱したり、ページの表示が重くなったりします。そこで使うのが、軽くてさりげないアニメーションです。

タイピング風アニメーション

文章が一気に表示されるより、タイプライター風に一文字ずつ表示されると、人は自然に注目して読むようになる。とくにキャッチコピーやサービス名の紹介で効果がある。

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>タイプライター風</title>
<style>
  body { font-family: system-ui, -apple-system, sans-serif; padding: 24px; }
  .typewriter { font: 700 28px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace; white-space: pre; }
  .controls { margin-top: 12px; }
  @media (prefers-reduced-motion: reduce) {
    .cursor { animation: none !important; }
  }
  .cursor { display: inline-block; width: 1ch; }
  .cursor::after { content: "▍"; animation: blink 1s steps(2, jump-none) infinite; }
  @keyframes blink { 50% { opacity: 0; } }
</style>
</head>
<body>
  <div class="typewriter" id="tw"><span class="cursor"></span></div>
  <div class="controls">
    <button id="restart">Restart</button>
  </div>

<script>
(function(){
  const el = document.getElementById('tw');
  const text = "静かな動きで、読みやすさを助ける。";
  const speed = 80; // ms per char
  function typeWriter() {
    el.textContent = "";
    let i = 0;
    function step(){
      if (i <= text.length) {
        el.textContent = text.slice(0, i);
        i++;
        setTimeout(step, speed);
      } else {
        // 末尾にカーソルっぽい見た目
        el.insertAdjacentHTML("beforeend", '<span class="cursor"></span>');
      }
    }
    step();
  }
  document.getElementById('restart').addEventListener('click', typeWriter);
  typeWriter();
})();
</script>
</body>
</html>

背景グラデーションのゆるやかな変化

要素の背景色がとてもゆっくりグラデーションで変わると、落ち着いた雰囲気になります。動きは小さいけど、印象は大きいです。

<div class="card">これはハッキリ動くグラデーション!</div>
/* 画面中央に配置するための最低限 */
html, body {
  height: 100%;
}
body {
  margin: 0;
  display: grid;
  place-items: center;
  font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
  background: #000;
}

/* はっきり動くグラデーション */
.card {
  padding: 40px 56px;
  border-radius: 16px;
  color: #fff;
  font-size: 24px;
  text-align: center;

  /* コントラスト強めの色を並べる */
  background: linear-gradient(270deg, red, blue, yellow, green);
  background-size: 200% 200%;

  /* 6秒で左右に往復(誰でも“動いてる”と分かる) */
  animation: gradientShift 6s linear infinite;

  box-shadow: 0 10px 30px rgba(0,0,0,.3);
}

@keyframes gradientShift {
  0%   { background-position: 0% 50%; }
  50%  { background-position: 100% 50%; }
  100% { background-position: 0% 50%; }
}

通知バッチ:数字更新で“ちょっと弾む”

ただ数字が変わるより、少しだけ弾むと「更新された」とすぐわかる。

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>通知バッジ:ほどよいバウンス</title>
<style>
  body { font-family: system-ui; padding: 24px; }
  .wrap { display: inline-flex; align-items: center; gap: 12px; }
  .badge {
    display: inline-grid; place-items: center;
    min-width: 32px; height: 32px; padding: 0 8px;
    border-radius: 16px;
    background: #111; color: #fff; font-weight: 700;
    will-change: transform;
    transform-origin: center center;
    font-size: 16px;
  }

  .bounce {
    animation: badge-bounce 0.6s cubic-bezier(.25,1.3,.5,1);
  }

  /* やりすぎないけどしっかり見える振れ幅 */
  @keyframes badge-bounce {
    0%   { transform: translateY(0) scale(1); }
    20%  { transform: translateY(-18%) scale(1.25); } /* 少し大きく上に */
    40%  { transform: translateY(8%) scale(0.9); }   /* ちょっと潰れる */
    60%  { transform: translateY(-8%) scale(1.1); }  /* 軽く跳ね返る */
    80%  { transform: translateY(3%) scale(0.97); }  /* 減衰 */
    100% { transform: translateY(0) scale(1); }      /* 戻る */
  }
</style>
</head>
<body>
  <div class="wrap">
    <button id="add">メッセージを追加</button>
    <span class="badge" id="badge">0</span>
  </div>

<script>
(function(){
  const badge = document.getElementById('badge');
  const add = document.getElementById('add');
  add.addEventListener('click', () => {
    const n = parseInt(badge.textContent || "0", 10) + 1;
    badge.textContent = String(n);
    badge.classList.remove('bounce');
    void badge.offsetWidth; // reflowでリセット
    badge.classList.add('bounce');
  });
})();
</script>
</body>
</html>

詳細情報の開閉に“気持ちいい矢印”

クリックで開くFAQなどは、矢印がわずかに回るだけで理解しやすくなります。CSSだけで実装できるので動作も軽いです。

<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>矢印が回転するサンプル</title>
<style>
  body { font-family: system-ui; padding: 24px; }
  details {
    width: 560px; border: 1px solid #ddd; border-radius: 12px; padding: 12px 16px;
  }
  summary {
    list-style: none; cursor: pointer; display: flex; align-items: center; gap: 8px; font-size: 18px;
  }
  summary::-webkit-details-marker { display: none; }

  .arrow {
    width: 32px; height: 32px; /* ← 少し大きめ */
    flex: 0 0 auto;
    transition: transform .3s ease; /* ← スムーズに回転 */
    transform-origin: 50% 50%;
    transform: rotate(0deg); /* 閉時は「右向き」 */
  }
  details[open] .arrow {
    transform: rotate(90deg); /* 開時は「下向き」 */
  }
</style>
</head>
<body>
  <details>
    <summary>
      <!-- 右向きシェブロン(Materialの形に近いパス) -->
      <svg class="arrow" viewBox="0 0 24 24" aria-hidden="true">
        <path d="M9 6l6 6-6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
      </svg>
      このアニメーション、重くない?
    </summary>
    <p>いいえ。CSSの transform だけ使うので軽いです。</p>
  </details>
</body>
</html>

まとめ

「さりげない」アニメーションは、控えめすぎて、提案する時だとお客様に理解してもらえないことが多いです。でも実際にコードにしてお客様に見ていただければわかるはずです。たくさん動作するアニメーションを入れることも良いのですが、たまにはこういう方法も試してみてください。

ぜひ次のプロジェクトで、これらの手法を試してみてください。サポートが必要な場合は、お問い合わせください。株式会社ファストコーディングでは、Webサイトのアニメーション実装をはじめとするさまざまなサポートを行っています。