Cara menganimasikan elemen "detail" menggunakan WAAPI





Selamat siang teman!



Di artikel ini, saya akan menunjukkan kepada Anda bagaimana Anda dapat menganimasikan elemen detail asli menggunakan Web Animations API .



Mari kita mulai dengan markup.



Elemen "detail" harus berisi elemen "ringkasan". ringkasan adalah bagian konten yang terlihat saat akordeon ditutup.



Elemen lainnya adalah bagian dari konten bagian dalam akordeon. Untuk mempermudah tugas kita, kita akan membungkus konten ini dalam div dengan kelas "konten".



<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>


Kelas akordeon



Kami membutuhkan kelas Accordion untuk dapat menggunakan kembali kode kami. Dengan kelas seperti itu, kita dapat membuat contoh detail berapa pun di halaman.



class Accordion {
  constructor() {}

  // ,     summary
  onClick() {}

  // ,     
  shrink() {}

  // ,      
  open() {}

  // ,     
  expand() {}

  // ,    shrink  expand
  onAnimationFinish() {}
}


konstruktor ()



Konstruktor digunakan untuk menyimpan data yang diperlukan untuk akordeon.



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 ()



Dalam fungsi "onClick", kami memeriksa apakah elemen sedang dalam proses animasi (menutup atau memperluas). Kita perlu melakukan ini untuk kasus ketika pengguna mengklik akordeon sebelum animasi berakhir. Kami tidak ingin akordeon melompat dari terbuka penuh ke tertutup sepenuhnya.



Elemen "details" memiliki atribut "open" yang ditambahkan oleh browser saat elemen dibuka. Kita bisa mendapatkan nilai atribut ini melalui 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()
  }
}


menyusut ()



Fungsi menyusut menggunakan fungsi WAAPI "animate". Anda dapat membaca tentang fitur ini di sini . WAAPI sangat mirip dengan pernyataan CSS "keyframes" di mana kita perlu mendefinisikan keyframes untuk animasinya. Dalam hal ini, kita hanya membutuhkan dua frame seperti itu: yang pertama adalah ketinggian elemen detail saat ini (terbuka), yang kedua adalah tinggi detail tertutup (ringkasan tinggi).



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
}


Buka ()



Fungsi "open" dipanggil saat kita ingin membuka akordeon. Fungsi ini tidak mengontrol animasi akordeon. Pertama, kami menghitung tinggi elemen "details" dan menambahkan gaya sebaris yang sesuai padanya. Setelah ini selesai, kita dapat menambahkan atribut "open" ke dalamnya untuk membuat konten terlihat, tetapi pada saat yang sama disembunyikan berkat overflow: hidden dan tinggi elemen yang tetap. Selanjutnya, kami menunggu bingkai berikutnya memanggil fungsi perluasan dan menganimasikan elemen.



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


memperluas ()



Fungsi perluasan mirip dengan fungsi menyusut, tetapi alih-alih menganimasikan dari ketinggian elemen saat ini ke tinggi tertutupnya, kami menganimasikan dari tinggi elemen ke tinggi penuhnya. Tinggi total adalah tinggi ringkasan ditambah tinggi konten bagian dalam.



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 ()



Fungsi ini dipanggil di akhir detail animasi pembuka dan penutup. Dibutuhkan satu parameter, nilai boolean untuk atribut "open", yang tidak lagi diproses oleh browser (jika Anda ingat, kami membatalkan perilaku browser default dalam fungsi "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 = ''
}


Memulai akordeon



Fuh! Kami hampir selesai.



Yang perlu dilakukan hanyalah membuat instance kelas Accordion untuk setiap elemen detail di halaman.



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


Catatan



Untuk menghitung dengan benar ketinggian elemen dalam status terbuka dan tertutup, ringkasan dan konten harus memiliki ketinggian yang sama di seluruh animasi.



Jangan tambahkan bagian kosong internal untuk ringkasan terbuka, karena ini dapat menyebabkan lompatan tiba-tiba. Hal yang sama berlaku untuk konten internal - itu harus memiliki ketinggian tetap, dan Anda harus menghindari mengubah tingginya saat membuka detailnya.



Selain itu, jangan tambahkan bagian kosong eksternal antara ringkasan dan konten, karena tidak akan diperhitungkan saat menghitung tinggi di bingkai utama. Sebagai gantinya, gunakan padding pada konten Anda untuk menambahkan beberapa ruang.



Kesimpulan



Begitulah, dengan mudah dan sederhana, kami berhasil membuat akordeon dalam JavaScript murni.







Saya harap Anda menemukan sesuatu yang menarik untuk diri Anda sendiri. Terima kasih atas perhatian Anda.



All Articles