ハートボタンのインタラクション

クリックJavaScriptボタン

いいねボタンなどのハートボタンをクリックしたときのインタラクションです。

実装のポイント

まずハートのアイコンはアクティブと非アクティブの画像が縦に並んだものを使用しています。これは非アクティブとアクティブ時の画像を別々で用意してしまうと、アクティブ化したときに読み込みが発生し、表示に遅延が発生してしまうためです。

下に縮みながら上にぴょんと跳ねるハートのアニメーション

1つの画像で表示位置を変更することで画像の切り替えを実現しています。非アクティブ時はbackground-position: 0 0とし、アクティブ時はbackground-position: 0 -100%としています。

アニメーション自体はCSSではなくJavaScriptのWeb Animation APIを使用しています。非アクティブ時とアクティブ時でbackground-position以外に違いがなくトランジションは使えません。また、このアニメーションが再生されるのはクリック時のみであるためクラスなどで制御するのが難しいです。

Web Animation APIの詳しい説明は割愛しますが、CSSアニメーションのようにキーフレームを配列に格納して使用でき、再生、停止、キャンセルなども容易に行えます。

アクティブ化したときはアニメーションが実行されますが、非アクティブにするときはアニメーションをなしにしています。非アクティブ時は誤って押したときやいいねを取り下げるといった消極的な理由なので、仰々しいアニメーションはせず、即座に反映するようにしています。

ちなみにアニメーションさせている要素は<button>要素ではなく、中のアイコンを表示している<span>要素です。<button>要素をアニメーションさせるとインタラクションのエリアが変わってしまいユーザービリティを下げてしまうことがあります。インタラクション要素ではなく内部の要素をアニメーションさせるほうが無難です。

また連打対策として非アクティブにしたときにアニメーションを強制的にキャンセルしています。

そのほか、.srOnlyというスクリーンリーダー用の不可視スタイルも使用しています。<button>要素のアクセシビリティを担保するためタグ内部に「Like」というテキストを入れています。この要素は不可視のスタイルなので画面上では見えませんがタグとしては存在しているため、スクリーンリーダーでは読み上げられ、ボタンの役割を伝えられます。

コード例

HTML

<button class="likeButton">
  <span class="srOnly">Like</span>
  <span class="iconImage"></span>
</button>

CSS

.likeButton {
  display: block;
  width: 128px;
  height: 128px;
  .iconImage {
    display: block;
    width: 100%;
    height: 100%;
    background-image: url("./icon_heart.svg");
    background-size: 100%;
    background-position: 0 0;
  }
  &.isActive {
    .iconImage {
      background-position: 0 -100%;
    }
  }
}

/* スクリーンリーダー用の不可視スタイル */
.srOnly {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border-width: 0;
}

JavaScript

// ボタンの要素を取得
const buttonElement = document.querySelector(".likeButton");
const iconElement = document.querySelector(".iconImage");

// イージング関数
const easeOutQuart = "cubic-bezier(0.25, 1, 0.5, 1)";
const easeInQuart = "cubic-bezier(0.5, 0, 0.75, 0)";

// ハートのキーフレームアニメーション
const keyframes = [
  { scale: 1 },
  { scale: 0.6, offset: 0.3, easing: easeInQuart },
  {
    scale: 1.4,
    offset: 0.5,
    easing: easeOutQuart,
  },
  { scale: 1, easing: easeInQuart },
];

// アニメーションを作成
const buttonAnimation = iconElement.animate(keyframes, {
  duration: 500,
});
// そのままだと即実行されるので止めていおく
buttonAnimation.pause();

// ボタンがクリックされた時の処理
buttonElement.addEventListener("click", () => {
  const isActive = buttonElement.classList.contains("isActive");
  if (isActive) {
    // 現状がアクティブならアクティブを解除
    buttonElement.classList.remove("isActive");
    // アニメーションをキャンセル(連打対策)
    buttonAnimation.cancel();
  } else {
    // 現状が非アクティブならアクティブにする
    buttonElement.classList.add("isActive");
    // アニメーションを実行
    buttonAnimation.play();
  }
});

上記の例は基本形の動きですが、つぎのように下に縮みながら上にぴょんと跳ねるバリエーションを持たせても面白いかもしれません。

下に縮みながら上にぴょんと跳ねるハートのアニメーション

CSS

.likeButton {
  display: block;
  width: 128px;
  height: 128px;
  
  .iconImage {
    display: block;
    width: 100%;
    height: 100%;
    background-image: url("./icon_heart.svg");
    background-size: 100%;
    background-position: 0 0;
    transform-origin: 50% 100%;
  }
  &.isActive {
    .iconImage {
      background-position: 0 -100%;
    }
  }
}

JavaScript

// ボタンの要素を取得
const buttonElement = document.querySelector(".likeButton");
const iconElement = document.querySelector(".iconImage");

// イージング関数
const easeOutQuart = "cubic-bezier(0.25, 1, 0.5, 1)";
const easeInQuart = "cubic-bezier(0.5, 0, 0.75, 0)";

// ハートのキーフレームアニメーション
const keyframes = [
  { scale: 1 },
  { scale: "0.9 0.6", translate: 0, offset: 0.2, easing: easeInQuart },
  {
    scale: 1.2,
    translate: "0 -50%",
    offset: 0.4,
    easing: easeOutQuart,
  },
  { scale: 1, translate: 0, easing: easeInQuart },
];

// アニメーションを作成
const buttonAnimation = iconElement.animate(keyframes, {
  duration: 800,
});
// そのままだと即実行されるので止めていおく
buttonAnimation.pause();

// ボタンがクリックされた時の処理
buttonElement.addEventListener("click", () => {
  const isActive = buttonElement.classList.contains("isActive");
  if (isActive) {
    // 現状がアクティブならアクティブを解除
    buttonElement.classList.remove("isActive");
    // アニメーションをキャンセル(連打対策)
    buttonAnimation.cancel();
  } else {
    // 現状が非アクティブならアクティブにする
    buttonElement.classList.add("isActive");
    // アニメーションを実行
    buttonAnimation.play();
  }
});

変更点はCSSのtransform-origin: 50% 100%;で変形の原点を縦中心の下部に変更し、キーフレームアニメーションを縦に大きく縮むようにするのと上への移動を追加しています。こうすることで元気よくジャンプするハートのアニメーションになります。

いろんなアニメーションのバリエーションに挑戦してみてください。

Copyright Web Motion Catalog all rights reserved.