スポンサーリンク

【コピペOK】CSSとJavaScriptのあるあるアニメーション5選

フリーランスの基礎知識

Webサイトにちょっとした動きを加えたい、でも複雑なライブラリはまだ難しい…。ひとまずコピペでさっと動きをつけたい!そんな風に思ったことはありませんか?

この記事では、コピペですぐに使える、よく見かけるWebサイトの動き(アニメーション)を5つご紹介!必要な記述や仕組みが分かれば、驚くほど簡単に実装できます✨️

  1. この記事でご紹介する5つのアニメーションはこれ!
    1. 【CSSだけ!】文字リンクのホバーエフェクト
    2. 【CSSだけ!】画像のホバーエフェクト
    3. スクロールされてから再生!ふわっと表示とスライドイン表示
    4. 虫めがねアイコンをクリックすると検索フォームが出てくるパーツ
    5. ハンバーガーメニュー(スライドインと全画面メニュー付き)
  2. 【CSSだけ!】文字リンクのホバーエフェクト
    1. 共通コピペ
    2. A:ホバーすると中央に向かって下線が消えます
        1. HTML
        2. CSS
    3. B:ホバーすると下線が消えて文字色が変わります
        1. HTML
        2. CSS
    4. C:ホバーすると下から背景色が現れます
        1. HTML
        2. CSS
    5. D:ホバーすると左から背景色が現れます
        1. HTML
        2. CSS
    6. ここは抑えておきたい!ポイント解説💡
      1. まず、デフォルトの設定は消しておこう
      2. CSSの『transition』は「じわっと動け!」の予約だと考えよう
      3. 『transition: all』を使って重くなったら面倒だけど細かく書こう
  3. 【CSSだけ!】画像のホバーエフェクト
    1. 画像幅の中で拡大する
        1. HTML
        2. CSS
    2. 横向きにクルッと回転
        1. HTML
        2. 回転用:共通スタイル
        3. CSS
    3. 縦向きにクルッと回転
        1. HTML
        2. 回転用:共通スタイル
        3. CSS
    4. ここだけは抑えておきたい!ポイント解説💡
      1. 動きを滑らかにするためにCSSに記述すべきは…
      2. 複数の画像を設置する時
  4. スクロールされてから再生!ふわっと表示とスライドイン表示
    1. A:シンプルなフェードイン要素
        1. HTML
        2. CSS
        3. JavaScript(Vanilla JS)
    2. B:ふわっと浮かび上がる要素
        1. HTML
        2. CSS
        3. JavaScript(Vanilla JS)
    3. C:左からスライドイン
        1. HTML
        2. CSS
        3. JavaScript(Vanilla JS)
    4. D:左からスライドイン
        1. HTML
        2. CSS
        3. JavaScript(Vanilla JS)
    5. ここだけは抑えておきたい!ポイント解説💡
      1. JavaScriptのコードは『DOMContentLoaded』の中に書こう
      2. フェードイン/アップ、左右のスライド全部使いたいって時
  5. 虫めがねアイコンをクリックすると検索フォームが出てくるパーツ
    1. A:【CSSだけ!】虫めがねアイコンをクリックすると検索フォームが出てくる
        1. HTML
        2. CSS
    2. B:虫めがねアイコンをクリックすると検索フォームが浮いて出てくる
        1. HTML
        2. CSS
        3. JavaScript(Vanilla JS)
    3. ここだけは抑えておきたい!ポイント解説💡
      1. 『Cocoon』テーマなど既存のスタイルでデザインが崩れる時
  6. ハンバーガーメニュー(スライドインと全画面メニュー付き)
    1. 共通コピペ
      1. ハンバーガーアイコン
      2. JavaScript
    2. A:ハンバーガーアイコンのみ
        1. HTML
    3. B:左からメニュー
        1. HTML
        2. CSS
    4. C:右からメニュー
        1. HTML
        2. CSS
    5. D:全画面メニュー
        1. HTML
        2. CSS
  7. CSSで実装すべき?それともJavaScript?と悩んだ時
      1. 1. CSSは『自己完結型の動き』
      2. 2. JavaScriptは『コントロールする動き』
  8. おまけ:アニメーションで酔う!?そんなユーザー様のための対処法!
  9. FrankuLのコピペで簡単シリーズはこちら✨️
  10. まとめ

この記事でご紹介する5つのアニメーションはこれ!

まずは、今回作成するアニメーションの完成形を一気にお見せします。「あ、これこれ!この動きが欲しかった!」というものの参考になれば幸いです。

【CSSだけ!】文字リンクのホバーエフェクト

See the Pen 【CSSだけ!】マウスオーバーで画像がちょっと拡大する by N Katsumata (@nk-codepen) on CodePen.

【CSSだけ!】画像のホバーエフェクト

See the Pen 【CSSだけ!】5つのリンクのホバーエフェクト by N Katsumata (@nk-codepen) on CodePen.

スクロールされてから再生!ふわっと表示とスライドイン表示

See the Pen ふわっと浮かび上がる要素 by N Katsumata (@nk-codepen) on CodePen.

虫めがねアイコンをクリックすると検索フォームが出てくるパーツ

See the Pen Untitled by N Katsumata (@nk-codepen) on CodePen.

ハンバーガーメニュー(スライドインと全画面メニュー付き)

See the Pen ハンバーガーメニュー by N Katsumata (@nk-codepen) on CodePen.

以上、今回の記事でご紹介するアニメーションです。

コードだけサクッとコピペしたい方は、上記CodePenからどうぞ✨️
詳しく知りたい方、エラーに困っている方、コーディングのコツを知りたい方は各段落を参照してくださいね☺️❗️

画面が小さくてコピペしづらい場合は、筆者のCodePenアカウントでも公開しています 👇️

CodePen
『N Katsumata』のCodePen一覧はこちら!

よくWebデザインのギャラリーサイトでご紹介されているサイトのあるあるな動きを厳選してみたのですが、どうでしょうか。作りたい動きは、ありましたか?

【CSSだけ!】文字リンクのホバーエフェクト

See the Pen 【CSSだけ!】あるあるな文字リンクのホバーエフェクト by N Katsumata (@nk-codepen) on CodePen.

共通コピペ

下記は必ず使用します。色や秒数などは適宜、ご調整くださいね!

.animated-link {
  position: relative;
  text-decoration: none;
  transition: color 0.3s ease-out;
  color: #3d4070; /* 同じ色を指定 */
  padding-bottom: 4px;
}

.animated-link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 1px;
  background-color: #3d4070; /* 同じ色を指定 */
  transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out, height 0.3s ease-in-out;
}

A:ホバーすると中央に向かって下線が消えます

👇️ マウスオーバーしてみてください 👇️

A:ホバーすると中央に向かって下線が消えます

HTML
<a href="#" class="animated-link link-a">ホバーすると中央に向かって下線が消えます</a>
CSS
.link-a:hover::after {
  transform: scaleX(0);
  transform-origin: center;
}

B:ホバーすると下線が消えて文字色が変わります

👇️ マウスオーバーしてみてください 👇️

B:ホバーすると下線が消えて文字色が変わります

HTML
<a href="#" class="animated-link link-b">ホバーすると下線が消えて文字色が変わります</a>
CSS
.link-b:hover {
  color: #c0392b;
}

.link-b:hover::after {
  opacity: 0;
  transition-duration: 0.05s;
}

C:ホバーすると下から背景色が現れます

👇️ マウスオーバーしてみてください 👇️

C:ホバーすると下から背景色が現れます

HTML
<a href="#" class="animated-link link-c">C:ホバーすると下から背景色が現れます</a>
CSS
.link-c {
  padding-left: 5px;
  padding-right: 5px;
  margin-left: -5px;
  margin-right: -5px;
  border-radius: 3px;
  z-index: 0;
}

.link-c:hover {
  color: #ffffff;
}

.link-c:hover::after {
  height: 100%;
  z-index: -1;
}

D:ホバーすると左から背景色が現れます

👇️ マウスオーバーしてみてください 👇️

D:ホバーすると左から背景色が現れます

HTML
<a href="#" class="animated-link link-d">D:ホバーすると左から背景色が現れます</a>
CSS
.link-d {
  padding-left: 5px;
  padding-right: 5px;
  margin-left: -5px;
  margin-right: -5px;
  border-radius: 3px;
  z-index: 0;
}

.link-d::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #3d4070;
  z-index: -1;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.3s ease-in-out;
}

.link-d:hover {
  color: #ffffff;
}

.link-d:hover::before {
  transform: scaleX(1);
}

.link-d:hover::after {
  opacity: 0;
}

ここは抑えておきたい!ポイント解説💡

まず、デフォルトの設定は消しておこう

.animated-link {
  position: relative;
  text-decoration: none;
⋮
}

text-decoration: none; でデフォルトの下線は消しましょう。アニメーションはこの後作った線に対してのみ動きます。また、position: relative; を設定することで、このクラスをつけた要素のみに下線を引くことができます。忘れると画面全体の左端に表示されるので要注意です。

CSSの『transition』は「じわっと動け!」の予約だと考えよう

CSSの『transition』はアニメーションっぽく徐々に変化させる…つまり、「じわっと動け!」の予約、と考えるとシンプルです。コピペしてきた動きをちょっと変化させたい場合は、この『transition』をコードから探し出してください。

上記のコードを直訳すると『色が0.3秒かけて最初は早く終わりはゆっくりとした動き(で)アニメーションっぽく徐々に変化する』という意味です。

この『ease-out』と書かれた部分はアニメの『緩急』を指定する「イージング関数」と呼ばれるもので、全部で5種類あります。

linearずーっと同じ速度
ease最初ゆっくり→加速→最後ゆっくり(デフォルト)
ease-inだんだん速くなる
ease-out最初が速くだんだん減速、一番よく使われる!
ease-in-outeaseより緩急がハッキリしてる

「イージング関数」は奥が深いのでここではおさらい程度の表面的な部分のみご紹介しますが、緩急の違いは下記を参考に体感してみてください。何回もやると目が痛くなるのでオススメしません(笑)

See the Pen イージング関数のサンプル by N Katsumata (@nk-codepen) on CodePen.

『transition: all』を使って重くなったら面倒だけど細かく書こう

このtransitionコード『.animated-link::after』内にやたら長いコードが記述されているのにお気づきでしょうか…

.animated-link::after {
⋮
  transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out, height 0.3s ease-in-out;
}

実はこれ、省略して transition: all 0.3s ease-in-out; と書くこともできるのですが、そうすると使わないプロパティも全てアニメーションされてしまうため、上記はページが重たくなるのを防ぐ書き方なのです。

書き方は、『変化が起きるものを記述する』です。例えば、下記のコードを見てください。

/* 悪い例 ❌ */

.my-button {
  display: inline-block;
  padding: 10px 20px;
  border-radius: 5px;
  background-color: #3498db; /* 青色 */
  color: white;
  transform: scale(1);

  transition: all 0.3s ease; /* ← ここを変えます */
}

.my-button:hover {
  background-color: #2980b9; /* 暗い青色に変化 */
  transform: scale(1.1);   /* 少し大きく変化 */
}

『.my-button』クラスのついたボタンをホバーした時にどうなるか後半に書かれていますね。

.my-button:hover {
  background-color: #2980b9; /* 暗い青色に変化 */
  transform: scale(1.1);   /* 少し大きく変化 */
}

この後半の動きを確認して『transition: all 0.3s ease;』を書き換えます。
変化する対象は『background-color』と『transform』なので下記にすればOK

transition: background-color 0.3s ease, transform 0.3s ease;

先程のコードの全体が下記になります。

/* 良い例 ✅ */

.my-button {
  display: inline-block;
  padding: 10px 20px;
  border-radius: 5px;
  background-color: #3498db;
  color: white;
  transform: scale(1);

  transition: background-color 0.3s ease, transform 0.3s ease; /* ← こうなります */
}

.my-button:hover {
  background-color: #2980b9;
  transform: scale(1.1);
}

確認が必要だったり、長くて面倒なのが難点ですが、覚えておいて損はないテクニックなので、ご紹介しました。もし、ページの軽量化業務に携わる際はお試しくださいね。

【CSSだけ!】画像のホバーエフェクト

See the Pen 【CSSだけ!】あるあるな画像のホバーエフェクト by N Katsumata (@nk-codepen) on CodePen.

画像幅の中で拡大する

👇️ マウスオーバーしてみてください 👇️



HTML
<a href="#" class="img-zoom-container">
  <img src="https://placehold.jp/3d4070/ffffff/300x200.png" alt="">
</a>
CSS
.img-zoom-container {
  overflow: hidden;
  display: inline-block;
}

.img-zoom-container img {
  display: block;
  transition: transform 0.4s ease;
}

.img-zoom-container:hover img {
  transform: scale(1.1);
}

横向きにクルッと回転

👇️ マウスオーバーしてみてください 👇️



HTML
<a href="#" class="flip-container">
  <img src="https://placehold.jp/3d4070/ffffff/300x200.png" alt="" class="animate-rotate-yoko">
</a>
回転用:共通スタイル
.flip-container {
  display: inline-block;
  perspective: 1000px;
}
CSS
.animate-rotate-yoko {
  display: inline-block;
  transform-style: preserve-3d;
  transition: transform 0.3s ease-in-out;
  transform-origin: center;
}

.animate-rotate-yoko:hover {
  transform: rotateY(180deg);
}

縦向きにクルッと回転

👇️ マウスオーバーしてみてください 👇️



HTML
<a href="#" class="flip-container">
  <img src="https://placehold.jp/3d4070/ffffff/300x200.png" alt="" class="animate-rotate-tate">
</a>
回転用:共通スタイル
.flip-container {
  display: inline-block;
  perspective: 1000px;
}
CSS
.animate-rotate-tate {
  display: block;
  transform-style: preserve-3d;
  transition: transform 0.3s ease-in-out;
  transform-origin: center;
}

.animate-rotate-tate:hover {
  transform: rotateX(180deg);
}

ここだけは抑えておきたい!ポイント解説💡

動きを滑らかにするためにCSSに記述すべきは…

画像の回転についてなのですが、動きを滑らかにするために『transform-style: preserve-3d;』、『perspective: 1000px;』、『transform-origin: center;』の3つを指定します。

  • transform-style: preserve-3d; は、3D空間のような立体感を保持するスタイル
  • perspective: 1000px; は、3D回転したときの奥行き(遠近感)を決める距離を指定するスタイル
  • transform-origin: center; は、回転や拡大縮小などの変形を行う際の中心点を指定するスタイル

この3つを組み合わせて使うことでCSSだけでもより滑らかな動きにできます。

複数の画像を設置する時

HTMLでは、このようなコードになります。

<div>

<a class="flip-container">
  <img src="https://placehold.jp/3d4070/ffffff/300x200.png" alt="" class="animate-rotate-tate">
</a>

<a class="flip-container">
  <img src="https://placehold.jp/3d4070/ffffff/300x200.png" alt="" class="animate-rotate-tate">
</a>

⋮

</div>

スクロールされてから再生!ふわっと表示とスライドイン表示

See the Pen ふわっと浮かび上がる要素 by N Katsumata (@nk-codepen) on CodePen.

A:シンプルなフェードイン要素

👇️ ここに表示されます 👇️

A:シンプルなフェードイン要素



HTML
<div class="fade-in">
  <p>A:シンプルなフェードイン要素</p>
</div>
CSS
.fade-in {
  opacity: 0;
  transition: opacity 1.2s ease-in-out;
}

.fade-in.is-visible {
  opacity: 1;
}
JavaScript(Vanilla JS)
const fadeInTargets = document.querySelectorAll(".fade-in");

const fadeInObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add("is-visible");
      observer.unobserve(entry.target);
    }
  });
});

fadeInTargets.forEach((target) => {
  fadeInObserver.observe(target);
});

B:ふわっと浮かび上がる要素

👇️ ここに表示されます 👇️

B:ふわっと浮かび上がる要素



HTML
<div class="fade-in-up">
  <p>B:ふわっと浮かび上がる要素</p>
</div>
CSS
.fade-in-up {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 0.8s ease-out, transform 0.8s ease-out;
}

.fade-in-up.is-visible {
  opacity: 1;
  transform: translateY(0);
}
JavaScript(Vanilla JS)
const fadeInUpTargets = document.querySelectorAll(".fade-in-up");

const fadeInUpObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add("is-visible");
      observer.unobserve(entry.target);
    }
  });
});

fadeInUpTargets.forEach((target) => {
  fadeInUpObserver.observe(target);
});

C:左からスライドイン

👇️ ここに表示されます 👇️

C:左からスライドイン



HTML
<div class="slide-in-left">
  <p>C:左からスライドイン</p>
</div>
CSS
.slide-in-left {
  opacity: 0;
  transition: opacity 0.8s ease-out, transform 0.8s ease-out;
}

.slide-in-left {
  transform: translateX(-50px);
}

.slide-in-left.is-visible {
  opacity: 1;
  transform: translateX(0);
}
JavaScript(Vanilla JS)
const slideInLeftTargets = document.querySelectorAll(".slide-in-left");

const slideInLeftObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add("is-visible");
      observer.unobserve(entry.target);
    }
  });
});

slideInLeftTargets.forEach((target) => {
  slideInLeftObserver.observe(target);
});

D:左からスライドイン

👇️ ここに表示されます 👇️

D:右からスライドイン



HTML
<div class="slide-in-right">
  <p>D:右からスライドイン</p>
</div>
CSS
.slide-in-right {
  opacity: 0;
  transition: opacity 0.8s ease-out, transform 0.8s ease-out;
}

.slide-in-right {
  transform: translateX(50px);
}

.slide-in-right.is-visible {
  opacity: 1;
  transform: translateX(0);
}
JavaScript(Vanilla JS)
const slideInRightTargets = document.querySelectorAll(".slide-in-right");

const slideInRightObserver = new IntersectionObserver((entries, observer) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add("is-visible");
      observer.unobserve(entry.target);
    }
  });
});

slideInRightTargets.forEach((target) => {
  slideInRightObserver.observe(target);
});

ここだけは抑えておきたい!ポイント解説💡

JavaScriptのコードは『DOMContentLoaded』の中に書こう

Vanilla JS(素のJavaScript)を扱う上で、

document.addEventListener('DOMContentLoaded', () => {
  // この中に処理を書く
});

という記述は、コードが予期せぬエラーで動かなくなるのを防ぐための非常に重要なおまじないだと考えてください。

では、なぜこれが必要なのでしょうか?
これには、ブラウザがHTMLファイルを上から一行ずつ読み込むことに理由があります。

もし、HTMLの要素(例:ボタン)が作られる前に、その要素を操作しようとするJavaScriptが実行されたらどうなるでしょうか?

おそらく「Cannot read properties of null (reading ‘addEventListener’)」などのエラーが出るでしょう。これは、「buttonがnull(=何も見つからない)なのに、addEventListenerを実行しようとしていますよ」という意味です。

ここで登場するのが『DOMContentLoaded』です。これは、「ページのHTML構造の準備がすべて完了したタイミングで、中の処理を実行してね」というブラウザへの命令になります。

このコードがあればHTMLの読み込みが終わるまでaddEventListenerの中の処理は待機してくれるのです。結果、ブラウザがボタンを認識し終わった完璧なタイミングで処理が実行されるため、エラーは発生しません。

そのため、今回ご紹介しているJavaScriptコードを使用する際は『DOMContentLoaded』の中に書いてくださいね。

document.addEventListener('DOMContentLoaded', () => {
  // この中に処理を書く、いくつ書いてもOK!HTMLを読み込んだ後に処理してくれます。
});

各ご紹介コードに書いていないのは『document.addEventListener(‘DOMContentLoaded’, () => {~});』ごと何度も同じJSファイルに書き込んでしまうのを防ぐためです。

フェードイン/アップ、左右のスライド全部使いたいって時

下記JSのみをコピペしてください。HTMLとCSSはそのままでOK◎

const animationTargets = document.querySelectorAll(
  '.fade-in, .fade-in-up, .slide-in-left, .slide-in-right'
);

const handleIntersect = (entries, observer) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      entry.target.classList.add('is-visible');
      observer.unobserve(entry.target);
    }
  });
};

const observer = new IntersectionObserver(handleIntersect);

animationTargets.forEach(target => {
  observer.observe(target);
});

虫めがねアイコンをクリックすると検索フォームが出てくるパーツ

See the Pen 虫めがねアイコンをクリックすると検索フォームが出てくるパーツ by N Katsumata (@nk-codepen) on CodePen.

A:【CSSだけ!】虫めがねアイコンをクリックすると検索フォームが出てくる


HTML
<form role="search" method="get" action="/search">
    <label class="search-form-label">
      <input class="search-form-input" type="search" name="q" placeholder="検索...">
      <svg class="search-form-icon" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
        <circle cx="11" cy="11" r="8"></circle>
        <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
      </svg>
    </label>
  </form>
CSS
.search-form-label {
  position: relative;
  display: inline-block;
}

.search-form-icon {
  position: absolute;
  top: 50%;
  left: 17px;
  transform: translateY(-50%);
  font-size: 22px;
  color: #888;
  transition: color 0.4s ease;
  pointer-events: none;
}

.search-form-input {
  -webkit-appearance: none;
  appearance: none;
  width: 60px;
  height: 60px;
  padding: 30px;
  border: none;
  border-radius: 30px;
  background-color: #eee;
  transition: width 0.4s ease, padding 0.4s ease;
  outline: none;
}

.search-form-input:focus {
  width: 250px;
  padding: 30px 30px 30px 60px;
}

B:虫めがねアイコンをクリックすると検索フォームが浮いて出てくる


HTML
<div class="search-widget">
  <button class="search-widget-toggle" aria-label="検索フォームを開閉">
    <svg class="search-widget-icon-open" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
      <circle cx="11" cy="11" r="8"></circle>
      <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
    </svg>
    <!-- 閉じるアイコン (バツ印) -->
    <svg class="search-widget-icon-close" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#333" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
      <line x1="18" y1="6" x2="6" y2="18"></line>
      <line x1="6" y1="6" x2="18" y2="18"></line>
    </svg>
  </button>
  <div class="search-widget-panel">
    <form class="search-widget-form" role="search" method="get" action="">
      <input class="search-widget-input" type="text" placeholder="search">
      <button class="search-widget-submit" type="submit" aria-label="検索">
        <svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <circle cx="11" cy="11" r="8"></circle>
          <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
        </svg>
      </button>
    </form>
  </div>
</div>
CSS
.search-widget-toggle {
  width: 50px;
  height: 50px;
  cursor: pointer;
  border: none;
  background: #eee;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
}

.search-widget-toggle i {
  font-size: 22px;
}

.search-widget-icon-close {
  display: none;
}

.search-widget-toggle.is-active .search-widget-icon-open {
  display: none;
}

.search-widget-toggle.is-active .search-widget-icon-close {
  display: inline;
}

.search-widget-panel {
  opacity: 0;
  width: 0;
  transition: opacity 0.4s, transform 0.4s;
  overflow: hidden;
}

.search-widget-panel.is-open {
  opacity: 1;
  z-index: 3;
  width: 280px;
  padding: 20px;
  background: #eee;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}

.search-widget-form {
  display: none;
  align-items: center;
  border-bottom: 2px solid #ccc;
  transition: background-color 0.3s;
}

.search-widget-panel.is-open .search-widget-form {
  display: flex;
}

.search-widget-panel input,
.search-widget-panel button {
  -webkit-appearance: none;
  appearance: none;
  outline: none;
  cursor: pointer;
  color: #666;
}

.search-widget-input {
  width: 100%;
  flex-grow: 1;
  border: none;
  height: 46px;
  background: transparent;
  padding: 0 10px;
}

.search-widget-form:focus-within {
  background: transparent;
}

.search-widget-submit {
  flex-shrink: 0;
  width: 46px;
  height: 46px;
  border: none;
  background: transparent;
  display: flex;
  align-items: center;
  justify-content: center;
}
JavaScript(Vanilla JS)
const toggleButton = document.querySelector(".search-widget-toggle");
const searchPanel = document.querySelector(".search-widget-panel");
const searchInput = document.querySelector(".search-widget-input");

if (toggleButton && searchPanel && searchInput) {
  toggleButton.addEventListener("click", () => {
    toggleButton.classList.toggle("is-active");
    searchPanel.classList.toggle("is-open");

    if (searchPanel.classList.contains("is-open")) {
      searchInput.focus();
    }
  });
}

ここだけは抑えておきたい!ポイント解説💡

『Cocoon』テーマなど既存のスタイルでデザインが崩れる時

Cocoonのテーマでは下記のように『input』自体にデフォルトのスタイル設定がされています。

input[type=text], input[type=password], input[type=date], input[type=datetime], input[type=email], input[type=number], input[type=search], input[type=tel], input[type=time], input[type=url], textarea, select, .search-edit {
    padding: 11px;
    border: 1px solid var(--cocoon-basic-border-color);
    border-radius: var(--cocoon-basic-border-radius);
    font-size: inherit;
    width: 100%;
}

そのため、ただコピペしてきただけでは下記のように表示が崩れることがあります…

そんな時はクラス名の前に『input』とつけてください。

input.search-form-input { /* inputをつけるだけ! */
  -webkit-appearance: none;
  appearance: none;
  width: 60px;
  height: 60px;
⋮
}

『input.search-form-input』と、することで優先してスタイルを読み込んでくれるため、正しいデザインで出力されるようになります。

ハンバーガーメニュー(スライドインと全画面メニュー付き)

See the Pen ハンバーガーメニュー by N Katsumata (@nk-codepen) on CodePen.

共通コピペ

下記2つは必ず使用します。色や位置などは適宜、ご調整くださいね!

ハンバーガーアイコン

.hamburger-button {
  position: relative;
  width: 50px;
  height: 50px;
  background: none;
  border: 1px solid #ccc;
  border-radius: 5px;
  cursor: pointer;
}

.hamburger-line {
  position: absolute;
  left: 10px;
  width: 30px;
  height: 2px;
  background-color: #333;
  transition: transform 0.3s, opacity 0.3s, top 0.3s;
}

.hamburger-line:nth-child(1) {
  top: 15px;
}

.hamburger-line:nth-child(2) {
  top: 24px;
}

.hamburger-line:nth-child(3) {
  top: 33px;
}

.hamburger-button.is-active .hamburger-line:nth-child(1) {
  top: 24px;
  transform: rotate(45deg);
}

.hamburger-button.is-active .hamburger-line:nth-child(2) {
  opacity: 0;
}

.hamburger-button.is-active .hamburger-line:nth-child(3) {
  top: 24px;
  transform: rotate(-45deg);
}

JavaScript

const hamburgerButtons = document.querySelectorAll(".hamburger-button");
hamburgerButtons.forEach((button) => {
  const targetPanelId = button.getAttribute("aria-controls");
  const targetPanel = document.getElementById(targetPanelId);

  button.addEventListener("click", () => {
    const isActive = button.classList.toggle("is-active");

    if (targetPanel) {
      targetPanel.classList.toggle("is-active");
    }

    document.body.classList.toggle("no-scroll", isActive);

    const isExpanded = isActive;
    button.setAttribute("aria-expanded", isExpanded);
    button.setAttribute(
      "aria-label",
      isExpanded ? "メニューを閉じる" : "メニューを開く"
    );
  });
});

A:ハンバーガーアイコンのみ



HTML
<button class="hamburger-button" type="button" aria-label="メニューを開く">
  <span class="hamburger-line"></span>
  <span class="hamburger-line"></span>
  <span class="hamburger-line"></span>
</button>

B:左からメニュー



HTML
<div class="type-left">
  <button class="hamburger-button" type="button" aria-label="メニューを開く" aria-controls="menu-panel-1" aria-expanded="false">
    <span class="hamburger-line"></span>
    <span class="hamburger-line"></span>
    <span class="hamburger-line"></span>
  </button>
</div>
<nav id="menu-panel-1" class="menu-panel slide-from-left">
  <ul>
    <li><a href="#">メニュー 1-1</a></li>
    <li><a href="#">メニュー 1-2</a></li>
    <li><a href="#">メニュー 1-3</a></li>
  </ul>
</nav>
CSS
.type-left {
  position: absolute;
  z-index: 100;
  top: 20px;
  left: 20px;
}
.menu-panel.slide-from-left {
  position: fixed;
  background-color: rgba(255, 255, 255, 0.95);
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
  top: 0;
  left: 0;
  width: 300px;
  height: 100%;
  visibility: hidden;
  transform: translateX(-100%);
  opacity: 0;
  transition: transform 0.4s, opacity 0.4s, visibility 0.4s;
}
.menu-panel.slide-from-left.is-active {
  visibility: visible;
  transform: translateX(0);
  opacity: 1;
}
.menu-panel.slide-from-left ul {
  list-style: none;
  padding: 160px 40px 40px 40px;
  margin: 0;
}
.menu-panel.slide-from-left li {
  margin-bottom: 20px;
}
.menu-panel.slide-from-left a {
  font-size: 1.2rem;
}

C:右からメニュー



HTML
<div class="type-right">
  <button class="hamburger-button" type="button" aria-label="メニューを開く" aria-controls="menu-panel-2" aria-expanded="false">
    <span class="hamburger-line"></span>
    <span class="hamburger-line"></span>
    <span class="hamburger-line"></span>
  </button>
</div>
<nav id="menu-panel-2" class="menu-panel slide-from-right">
  <ul>
    <li><a href="#">メニュー 2-1</a></li>
    <li><a href="#">メニュー 2-2</a></li>
    <li><a href="#">メニュー 2-3</a></li>
  </ul>
</nav>
CSS
.type-right {
  position: absolute;
  top: 20px;
  right: 20px;
  z-index: 100;
}

.menu-panel.slide-from-right {
  position: fixed;
  background-color: rgba(255, 255, 255, 0.95);
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
  top: 0;
  right: 0;
  width: 300px;
  height: 100%;
  visibility: hidden;
  transform: translateX(100%);
  opacity: 0;
  transition: transform 0.4s, opacity 0.4s, visibility 0.4s;
}

.menu-panel.slide-from-right.is-active {
  visibility: visible;
  transform: translateX(0);
  opacity: 1;
}

.menu-panel.slide-from-right ul {
  list-style: none;
  padding: 160px 40px 40px 40px;
  margin: 0;
}

.menu-panel.slide-from-right li {
  margin-bottom: 20px;
}

.menu-panel.slide-from-right a {
  font-size: 1.2rem;
}

D:全画面メニュー



HTML
<div class="type-fullscreen">
  <button class="hamburger-button" type="button" aria-label="メニューを開く" aria-controls="menu-panel-3" aria-expanded="false">
    <span class="hamburger-line"></span>
    <span class="hamburger-line"></span>
    <span class="hamburger-line"></span>
  </button>
</div>
<nav id="menu-panel-3" class="menu-panel fade-in-fullscreen">
  <ul>
    <li><a href="#">メニュー 3-1</a></li>
    <li><a href="#">メニュー 3-2</a></li>
    <li><a href="#">メニュー 3-3</a></li>
  </ul>
</nav>
CSS
.type-fullscreen {
  position: relative;
  z-index: 200;
}
.menu-panel.fade-in-fullscreen {
  position: fixed;
  visibility: hidden;
  transition: opacity 0.4s, visibility 0.4s;
  background-color: rgba(255, 255, 255, 0.95);
  box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  z-index: 150;
}
.menu-panel.fade-in-fullscreen.is-active {
  visibility: visible;
  opacity: 1;
}
.menu-panel.fade-in-fullscreen ul {
  list-style: none;
  padding: 80px 40px;
  margin: 0;
}
.menu-panel.fade-in-fullscreen li {
  margin-bottom: 20px;
}
.menu-panel.fade-in-fullscreen a {
  font-size: 1.2rem;
}

👆️👆️👆️ アニメーションは以上です! 👆️👆️👆️

次は、私が記事を書くきっかけにもなった、デザイン画を見てCSSで実装すべきか、それともJavaScriptか、悩んだ時の対処法をご紹介します!

CSSで実装すべき?それともJavaScript?と悩んだ時

👉 結論「できることは極力CSSで。どうしても足りないときだけJavaScriptを使う!」

実は私、よく迷ってました……こちら、どういうことか詳しく見てみましょう。

1. CSSは『自己完結型の動き』

まず実装する際に、アニメーションのきっかけが「ユーザーの単純な操作」「単独で動くこと」であれば、CSSを検討することから始めます。

  • 例えば:
    • マウスを乗せたらボタンの色が変わる (:hover)
    • フォーム入力中だけ枠線が光る (:focus)
    • 特定のクラスが付いたときにアニメーションを再生させる
    • ページを開いた直後にふわっとフェードイン

これらのきっかけで起こるアニメーションは「自己完結型」に分類されるので、CSSだけで実装すればOK♪ transition や @keyframes を使えば、ブラウザが最適化してくれるため、軽量かつ非常に滑らかな良い動きを実装できます。

👇️ @keyframesってこんなの

See the Pen @keyframesを使ったCSSアニメーションサンプル by N Katsumata (@nk-codepen) on CodePen.

そのため『できることは極力CSSで』と、言われる所以なのです。

2. JavaScriptは『コントロールする動き』

一方JavaScriptは、Web制作の上では『コントロールする動き』と考えるほうがわかりやすいです。

CSSは「自己完結」したものなので、すべてのアニメーションをCSSで表現してしまうと、ページを開いたときに画面に映っていない箇所もすべて再生されて、スクロールする頃には動きのないページになってしまいます。

そのCSSの「実行するタイミング」を決めたり、CSSでは動きがどうしてもカクつく、かえって動きが悪く利便性が損なわれるような「細かなアニメーション」を設定したい時は、JavaScriptの補佐が必須となります。

  • 例えば:
    • スクロールと連動させてアニメーションを再生したい
    • 時間の経過で一文字ずつテキストを表示したい
    • クリックしてからなどイベントが発生してからアニメーションを再生したい
    • スムーススクロール(リンクをクリックしてスル~っとページ内が動く動作でCSS『scroll-behavior: smooth;』だけで実装可能)などの動きが変…操作してて不快な感じがする

これらすべてCSSだけでは実現不可能な「制御」や「計算」を必要とする動きになります。なので、JavaScriptでCSSを補佐することで視覚や操作に快適性をもたらすため閲覧が楽になったり、操作しやすくなったりします。

しかし、そんなJavaScriptの唯一の欠点が『ページが重くなりやすいこと』

実は、JavaScriptはゲームが作れるほど多彩で機能も充実しているので、先程のCSSの動きもすべてJavaScriptだけでできてしまいます。

ページの処理を軽量化したい、すべてJavaScriptだけでページを制作したい、そういった方のために「jQuery」などのライブラリや「React」、「Vue.js」などのフレームワークを使用したサイトもありますが、またそれはそれで技術の習得が必要とされます。

今、対処したいのに、これでは話が違いますよね…
以上のことから、『どうしても足りないときだけJavaScriptを使う!』なのです。

昨今ではJavaScriptというと「jQuery」や「React」など、先にライブラリやフレームワークを考えられることが多いです。今回ご紹介する純粋なJavaScriptで作られたものは「Vanilla JS」と呼ばれます。何もトッピングされていない「バニラアイスクリーム」を語源とする、半ばジョークから生まれました。バニラのように真っ白。真っ白=純粋という意味合いで使われているのです。なので、どこかにライブラリやフレームワークを使用していないものを示す時は「Vanilla JS」と、どこかに記載すると認識のズレを防止できます。

まだまだ、現場で使われることの多い「Vanilla JS」。バージョンアップ作業の懸念点などから、フレームワークから「Vanilla JS」へ移行することもございます。すべてのJavaScript系ライブラリやフレームワークの基盤でもあるので、ぜひ、この機会に苦手意識を薄めていってくださいね◎

おまけ:アニメーションで酔う!?そんなユーザー様のための対処法!

ちょっと踏み込んだお話になりますが、ユーザー様の中には、画面の動きによって乗り物酔いに似た症状を呈する人がいます。そうした方がOS側で「動きを減らす」設定をしている場合も…その対応としてCSSでは、OSでの設定を検知してアニメーションを無効化する…というのが最も丁寧な実装です。

/* CSSです */

@media (prefers-reduced-motion: reduce) {
  .クラス名 {
    transition-duration: 0.001ms !important;
  }
}

※『transition-duration: 0.001ms !important;』にしたのは『transition: none;』よりも安全でユーザーの動き削減設定を尊重できるため、こちらが採用されることが多いようです。

ただしこれは必須ではないので今は考えなくてもいいのですが、ユーザビリティが厳しく重視される公的なサイトやサービスの制作に携わる可能性もあるので取り上げさせていただきました。

FrankuLのコピペで簡単シリーズはこちら✨️

まとめ

このアニメーション実装。私自身がとても悩んだので、この記事を書くきっかけになりました。
なぜなら現場ではこんな事態に遭遇しやすいからです……

  • デザイン画で一切アニメーションの指示がされていない…
  • いい感じにまとめて!自由にアニメーションつけていいよ!と言われる
  • 後から追加したい!でも、時間がない!
  • AIでコードを生成させても結局コードチェックや精査で時間がかかる…

ですが、これを実装する時は、これとこれを組み合わせるという、ちょっとしたコツがわかってしまえば怖くありません。読むだけでも頭のどこかに残るので、あとは実装を繰り返したり、実際のサイトを眺めているうちに落ち着いて対処できるようになるので大丈夫◎

ちょっと動きがあるだけでWebサイトが、リッチになったり、操作性が上がったり、コンテンツが伝わりやすくなったり…とメリットづくしなので、ぜひ、この機会にお試しくださいね☺️!

今回の記事でご紹介したデモは筆者のCodePenアカウントでも公開しています 👇️

CodePen
『N Katsumata』のCodePen一覧はこちら!
この記事を書いた人
プロフィール
Ka.tsu.ma.ta

👉️ FrankuLメンバー。(https://frankul.net/crew/katsumata/)
👉️ Webクリエイター。
👉️ コーディングを中心に、Webデザイン・マーケティング・WordPress運用など、幅広くWeb業務に携わっています。ブランドの"想い"をカタチにするのが好きです✨

フリーランスの基礎知識
よかったらシェアしてね
タイトルとURLをコピーしました