14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Web Animations APIでシマウマを回したくなったときに見るやつ

Last updated at Posted at 2023-06-06

Web Animations APIの出番が少なすぎて使い方を忘れそうなので覚えてるうちに。シマウマを回したくなったときの参考にしてください。

See the Pen Web Animations API Zebra Rotation by nishinoshake (@nishinoshake) on CodePen.

Web Animations API

  • CSSアニメーションのJS版
  • CSSで良くない?
  • GSAPで良くない?

仕様を見ていて便利だなぁと思ったのが、再生速度をコントロールできるところ。現時点での印象は、JSでキーフレームを使いたい、かつアニメーションライブラリが使えない状況で役に立つAPIという感じ。ニッチだな!

Web Animations APIのcompositeが凄過ぎてすごいからみんな見てくれを見てみたら、composite プロパティはたしかに使えそう。あの罪深い <div> を減らせるならそれだけでも価値がある。たまさんかわいい。

些細なところですが、デフォルトではアニメーション時にインラインスタイルを書き換えないので、なんでこの要素動いてるの!っていう驚きを同業者に与えられる。結局ニッチだな!

commitStyles() でインラインスタイルにぶち込めます

シマウマを回す

/* CSSアニメーション */
@keyframes rotation {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

.zebra {
  /* animation-timing-function の初期値は ease*/
  animation: rotation 2s linear infinite;
}
/* Web Animations API */
document.querySelector('.zebra').animate(
  [
    { transform: 'rotate(0deg)' },
    { transform: 'rotate(360deg)' }
  ], {
    duration: 2000,
    iterations: Infinity,
    easing: 'linear' // こっちは linear が初期値
  }
)

CSSアニメーションとの比較。パッと見は似ていますが、プロパティがちょこちょこ違います。 無限ループが "infinite" ではなく Infinity だったり、イージングの初期値も地味に違います。他にも違いがありそうなので、使う前に仕様のご確認を。

シマウマと遊ぶ

See the Pen Web Animations API Zebra Rotation by nishinoshake (@nishinoshake) on CodePen.

回すだけだとCSSで良くない?ってなるので、再生速度を変更できるようにしました。CSSで animation-duration を変えるとアニメーションがバキっとなりますが、こっちだとそれがないのが優秀。

あと、finished プロパティが Promise を返してくれるので、終わったら要素を消したい、みたいなときに便利。animationend イベントよりも扱いやすいので、単発で消すアニメーションには良いかも。(このサンプルだとボタンを押したときの Faster! の文字とか)

再生速度の playbackRate は 0 で動きが止まり、負の値は逆再生になります。

playbackRate が負の状態で currentTime が 0 まで巻き戻ると アニメーションが止まってしまうので、currentTime を duration * 10000 からスタートさせる心温まる実装をしています。狂気の1万イテレーション!

1万イテレーションで静止するシマウマ

何かを回したい人のために関数を置いておくので、よかったら使ってください。

TypeScript
type RotationOption = {
  el: HTMLElement
  duration?: number
  step?: number
}

const createRotation = (option: RotationOption) => {
  const OFFSET_ITERATIONS = 10000
  const duration = option.duration === undefined ? 1000 : option.duration
  const step = option.step === undefined ? 0.5 : option.step
  const animation = option.el.animate(
    [
      { transform: 'rotate(0deg)' },
      { transform: 'rotate(360deg)' }
    ], {
      duration,
      iterations: Infinity
    }
  )

  animation.currentTime = duration * OFFSET_ITERATIONS

  return {
    faster: () => {
      animation.playbackRate += step
    },
    slower: () => {
      animation.playbackRate -= step
    }
  }
}

const target = document.querySelector<HTMLElement>('.target')

if (target) {
  const rotation = createRotation({
    el: target,
    duration: 1000,
    step: 0.5
  })
  
  // Faster!
  document.querySelector<HTMLElement>('.faster')?.addEventListener('click', () => rotation.faster())
  
  // Slower!
  document.querySelector<HTMLElement>('.slower')?.addEventListener('click', () => rotation.slower())
}
JavaScript
const createRotation = (option) => {
  const OFFSET_ITERATIONS = 10000
  const duration = option.duration === undefined ? 1000 : option.duration
  const step = option.step === undefined ? 0.5 : option.step
  const animation = option.el.animate(
    [
      { transform: 'rotate(0deg)' },
      { transform: 'rotate(360deg)' }
    ], {
      duration,
      iterations: Infinity
    }
  )

  animation.currentTime = duration * OFFSET_ITERATIONS

  return {
    faster: () => {
      animation.playbackRate += step
    },
    slower: () => {
      animation.playbackRate -= step
    }
  }
}

const target = document.querySelector('.target')

if (target) {
  const rotation = createRotation({
    el: target,
    duration: 1000,
    step: 0.5
  })
  
  // Faster!
  document.querySelector('.faster')?.addEventListener('click', () => rotation.faster())
  
  // Slower!
  document.querySelector('.slower')?.addEventListener('click', () => rotation.slower())
}

参考

14
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?