朋友们,美好的一天!
在本文中,我将向您展示如何使用Web Animations API来对本机细节元素进行动画处理。
让我们从标记开始。
“详细信息”元素必须包含“摘要”元素。摘要是关闭手风琴时内容的可见部分。
任何其他元素都是手风琴内部内容的一部分。为了使我们的任务更轻松,我们将把此内容包装在带有“内容”类的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“关键帧”语句非常相似,因为我们需要为动画定义关键帧。在这种情况下,我们只需要两个这样的框架:第一个是“详细信息”元素的当前高度(打开),第二个是闭合明细的高度(摘要高度)。
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”属性以使内容可见,但同时由于溢出而隐藏:隐藏和元素的固定高度。接下来,我们等待下一帧调用expand函数并对元素进行动画处理。
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 = ''
}
初始化手风琴
!我们快完成了。
剩下要做的就是为页面上的每个details元素创建Accordion类的实例。
document.querySelectorAll('details').forEach(el => {
new Accordion(el)
})
备注
为了正确地计算元素在打开和关闭状态下的高度,摘要和内容在整个动画中必须具有相同的高度。
不要在打开的摘要中添加内部空格,因为这可能导致突然跳转。内部内容也是如此-它必须具有固定的高度,并且在打开细节时应避免更改其高度。
另外,请勿在摘要和内容之间添加外部空格,因为在计算关键帧的高度时不会考虑它们。而是在内容上使用填充以增加一些空间。
结论
这样,我们便可以轻松,简单地用纯JavaScript创建手风琴。
希望您发现自己感兴趣的东西。感谢您的关注。