كيفية تحريك عنصر "التفاصيل" باستخدام WAAPI





يوم جيد ، أيها الأصدقاء!



في هذه المقالة ، سأوضح لك كيف يمكنك تحريك عنصر التفاصيل الأصلي باستخدام Web Animations API .



لنبدأ بالترميز.



يجب أن يحتوي عنصر "التفاصيل" على عنصر "ملخص". الملخص هو الجزء المرئي من المحتوى عند إغلاق الأكورديون.



أي عناصر أخرى هي جزء من المحتوى الداخلي للأكورديون. لتسهيل مهمتنا ، سنلف هذا المحتوى في div بـ "content" للفصل.



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


فئة الأكورديون



نحتاج إلى فئة الأكورديون حتى نتمكن من إعادة استخدام الكود الخاص بنا. باستخدام هذه الفئة ، يمكننا إنشاء مثيل لأي عدد من التفاصيل على الصفحة.



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" ، نتحقق مما إذا كان العنصر قيد التحريك (إغلاق أو توسيع). نحتاج إلى القيام بذلك للحالة عندما ينقر المستخدم على الأكورديون قبل انتهاء الحركة. لا نريد أن يقفز الأكورديون من الفتح الكامل إلى الإغلاق الكامل.



عنصر "التفاصيل" له خاصية "فتح" التي أضافها المستعرض عند فتح العنصر. يمكننا الحصول على قيمة هذه السمة عبر 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 "animate". يمكنك أن تقرأ عن هذه الميزة هنا . تتشابه WAAPI مع عبارة "keyframes" في CSS من حيث أننا نحتاج إلى تحديد إطارات مفتاحية للرسوم المتحركة. في هذه الحالة ، نحتاج فقط إلى إطارين من هذا القبيل: الأول هو الارتفاع الحالي لعنصر التفاصيل (مفتوح) ، والثاني هو ارتفاع التفاصيل المغلقة (ارتفاع الملخص).



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
}


افتح ()



يتم استدعاء الوظيفة "المفتوحة" عندما نريد فتح الأكورديون. لا تتحكم هذه الوظيفة في حركة الأكورديون. أولاً ، نحسب ارتفاع عنصر "التفاصيل" ونضيف الأنماط المضمنة المناسبة إليه. بعد الانتهاء من ذلك ، يمكننا إضافة سمة "open" إليها لجعل المحتوى مرئيًا ، ولكن في الوقت نفسه مخفي بفضل overflow: hidden والارتفاع الثابت للعنصر. بعد ذلك ، ننتظر حتى يستدعي الإطار التالي وظيفة التوسيع وتحريك العنصر.



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



يتم استدعاء هذه الوظيفة في نهاية تفاصيل الرسوم المتحركة الافتتاحية والختامية. يتطلب الأمر معلمة واحدة ، وهي قيمة منطقية للسمة "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 = ''
}


تهيئة الأكورديون



فوه! نحن على وشك الانتهاء.



كل ما تبقى القيام به هو إنشاء مثيل لفئة الأكورديون لكل عنصر تفاصيل على الصفحة.



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


ملاحظات



من أجل حساب ارتفاع عنصر بشكل صحيح في حالتي الفتح والمغلق ، يجب أن يكون للملخص والمحتوى نفس الارتفاع خلال الرسم المتحرك.



لا تضف فراغات داخلية للملخص المفتوح ، لأن هذا قد يؤدي إلى قفزات مفاجئة. وينطبق الشيء نفسه على المحتوى الداخلي - يجب أن يكون له ارتفاع ثابت ، ويجب تجنب تغيير ارتفاعه أثناء فتح التفاصيل.



أيضًا ، لا تقم بإضافة فراغات خارجية بين الملخص والمحتوى ، حيث لن يتم أخذها في الاعتبار عند حساب الارتفاع في الإطارات الرئيسية. بدلاً من ذلك ، استخدم المساحة المتروكة على المحتوى الخاص بك لإضافة بعض المساحة.



خاتمة



هكذا ، بسهولة وبساطة ، تمكنا من إنشاء أكورديون بجافا سكريبت خالص.







أتمنى أن تجد شيئًا ممتعًا لنفسك. شكرآ لك على أهتمامك.



All Articles