WAAPIを使用して「details」要素をアニメーション化する方法





良い一日、友達!



この記事ではWeb AnimationsAPIを使用してネイティブの詳細要素アニメーション化する方法を紹介します



マークアップから始めましょう。



「details」要素には「summary」要素が含まれている必要があります。要約は、アコーディオンが閉じられたときにコンテンツの表示部分です。



その他の要素は、アコーディオンの内部コンテンツの一部です。タスクを簡単にするために、このコンテンツをクラス「content」のdivでラップします。



<details>
  <summary>Summary of the accordion</summary>
  <div class="content">
    <p>
      Lorem, ipsum dolor sit amet consectetur adipisicing elit.
      Modi unde, ex rem voluptates autem aliquid veniam quis temporibus repudiandae illo, nostrum, pariatur quae!
      At animi modi dignissimos corrupti placeat voluptatum!
    </p>
  </div>
</details>


アコーディオンクラス



コードを再利用するには、Accordionクラスが必要です。このようなクラスを使用すると、ページ上の詳細をいくつでもインスタンス化できます。



class Accordion {
  constructor() {}

  // ,     summary
  onClick() {}

  // ,     
  shrink() {}

  // ,      
  open() {}

  // ,     
  expand() {}

  // ,    shrink  expand
  onAnimationFinish() {}
}


コンストラクター()



コンストラクターは、アコーディオンに必要なデータを格納するために使用されます。



constructor(el) {
  //  details
  this.el = el
  //  summary
  this.summary = el.querySelector('summary')
  //  div   "content"
  this.content = el.querySelector('.content')

  //    (    )
  this.animation = null
  //      ?
  this.isClosing = false
  //      ?
  this.isExpanding = false
  //    summary
  this.summary.addEventListener('click', (e) => this.onClick(e))
}


onClick()



「onClick」関数では、要素がアニメーション化(クローズまたは展開)されているかどうかを確認します。アニメーションが終了する前にユーザーがアコーディオンをクリックした場合にこれを行う必要があります。アコーディオンが完全に開いた状態から完全に閉じた状態にジャンプすることは望ましくありません。



「details」要素には、要素が開かれたときにブラウザーによって追加される「open」属性があります。this.el.openを介して、この属性の値を取得できます。



onClick(e) {
  //    
  e.preventDefault()
  //   details  "overflow"   "hidden"    
  this.el.style.overflow = 'hidden'
  // ,         
  if (this.isClosing || !this.el.open) {
    this.open()
    // ,         
  } else if (this.isExpanding || this.el.open) {
    this.shrink()
  }
}


シュリンク ()



縮小機能は、WAAPIの「アニメーション」機能を使用します。この機能については、こちらをご覧くださいWAAPIは、アニメーションのキーフレームを定義する必要があるという点で、CSSの「keyframes」ステートメントと非常によく似ています。この場合、このようなフレームは2つだけ必要です。1つ目は詳細要素の現在の高さ(開いている)、2つ目は閉じている詳細の高さ(要約の高さ)です。



shrink() {
  //    
  this.isClosing = true

  //    
  const startHeight = `${this.el.offsetHeight}px`
  //   summary
  const endHeight = `${this.summary.offsetHeight}px`

  //    
  if (this.animation) {
    //  
    this.animation.cancel()
  }

  //  WAAPI 
  this.animation = this.el.animate({
    //   
    height: [startHeight, endHeight]
  }, {
    //         ,        (duration - )
    duration: 400,
    //        (easing (animation-timing-function) -  )
    easing: 'ease-out'
  })

  //     onAnimationFinish()
  this.animation.onfinish = () => this.onAnimationFinish(false)
  //   ,   "isClosing"  "false"
  this.animation.oncancel = () => this.isClosing = false
}


開いた ()



アコーデオンを開きたいときに「開く」関数が呼び出されます。この関数は、アコーディオンのアニメーションを制御しません。まず、「details」要素の高さを計算し、適切なインラインスタイルを追加します。これが完了したら、コンテンツを表示するために「open」属性を追加できますが、同時にオーバーフローのおかげで非表示になります:非表示と要素の固定高さ。次に、次のフレームが展開関数を呼び出して要素をアニメーション化するのを待ちます。



open() {
  //    
  this.el.style.height = `${this.el.offsetHeight}px`
  //  details  "open"
  this.el.open = true
  //       "expand"
  requestAnimationFrame(() => this.expand())
}


展開()



展開関数は縮小関数に似ていますが、要素の現在の高さから閉じた高さまでアニメーション化する代わりに、要素の高さから完全な高さまでアニメーション化します。全体の高さは、要約の高さに内部コンテンツの高さを加えたものです。



expand() {
  //    
  this.isExpanding = true
  //    
  const startHeight = `${this.el.offsetHeight}px`
  //     ( summary +  )
  const endHeight = `${this.summary.offsetHeight + this.content.offsetHeight}px`

  //    
  if (this.animation) {
    //  
    this.animation.cancel()
  }

  //  WAAPI 
  this.animation = this.el.animate({
    height: [startHeight, endHeight]
  }, {
    duration: 400,
    easing: 'ease-out'
  })

  this.animation.onfinish = () => this.onAnimationFinish(true)
  this.animation.oncancel = () => this.isClosing = false
}


onAnimationFinish()



この関数は、アニメーションの開始と終了の詳細の最後に呼び出されます。1つのパラメーター、「open」属性のブール値を取りますが、これはブラウザーによって処理されなくなります(覚えている場合は、「onClick」関数でのデフォルトのブラウザー動作をキャンセルしました)。



onAnimationFinish(open) {
  //    "open"
  this.el.open = open
  //  ,  
  this.animation = null
  //  
  this.isClosing = false
  this.isExpanding = false
  //  overflow   
  this.el.style.height = this.el.style.overflow = ''
}


アコーディオンの初期化



ふー!ほぼ完了です。



あとは、ページの詳細要素ごとにAccordionクラスのインスタンスを作成するだけです。



document.querySelectorAll('details').forEach(el => {
  new Accordion(el)
})


備考



開いた状態と閉じた状態の要素の高さを正しく計算するには、要約とコンテンツの高さがアニメーション全体で同じである必要があります。



突然のジャンプにつながる可能性があるため、開いている要約に内部ブランクを追加しないでください。同じことが内部コンテンツにも当てはまります。高さは固定されている必要があり、詳細を開くときに高さを変更しないようにする必要があります。



また、キーフレームの高さを計算するときに考慮されないため、要約とコンテンツの間に外部ブランクを追加しないでください。代わりに、コンテンツにパディングを使用してスペースを追加してください。



結論



このようにして、簡単かつ簡単に、純粋なJavaScriptでアコーディオンを作成することができました。







何か面白いものを見つけていただければ幸いです。清聴ありがとうございました。



All Articles