自定义事件封装
作者:Yuan Tang
更新于:5 个月前
字数统计:818 字
阅读时长:2 分钟
元素平滑上升
Vue 项目中通过封装自定义事件,实现盒子滚动到视口内时,盒子平滑上升的效果。
前值知识
- :可以创建一个新的 Animation 的便捷方法,将它应用于元素,然后运行动画。返回一个新建的 Animation 对象实例。
`Element.animate()` https://developer.mozilla.org/zh-CN/docs/Web/API/Element/animate接收两个参数:
- 第一个参数是一个描述动画的 Keyframes 数组对象,其中包含了一个或多个 CSS 样式和时间。
- 第二个参数是一个描述动画的选项对象,其中包含了一些动画的属性。如 持续时间
duration
、动画的延迟时间delay
、动画效果ease
等。
返回的动画对象实例可以用于控制动画的暂停、播放、取消等操作。调用对应的
pause()
、play()
、cancel()
方法即可。 [
IntersectionObserver
] :是一个异步观察目标元素与其祖先元素或顶级文档视口(viewport)交叉状态的方法,它被设计为异步的,以避免阻塞主线程。当一个 IntersectionObserver 对象被创建时,其被配置为监听根中一段给定比例的可见区域。一旦 IntersectionObserver 被创建,则无法更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值;然而,你可以在同一个观察者对象中配置监听多个目标元素。
思路分析
- 创建一个自定义事件,在
mounted
组件挂载的生命周期钩子上,使用 Web Animation API 来应用动画。写法为el.animate(关键帧, 配置)。此时全部的组件挂载完毕后直接触发了动画。 - 先让动画暂停,使用 IntersectionObserver 监听元素是否出现在视口内,如果出现则播放动画。在
unmounted
组件卸载的生命周期钩子上,取消对元素的监听。此时组件无论一开始有没有出现在视口,都会触发一次动画。 - 在
mounted
生命周期钩子函数添加一个判断,如果此时元素已经出现在视口内,则return
不播放动画。 - 声明一个变量
map
,用来存储未播放过动画的元素,播放过后从map
内删除,避免重复播放动画。
代码实现
js
const DISTANCE = 100 // 初始y轴偏移位置
const DURATION = 500 // 动画持续时间
const map = new WeakMap()
// IntersectionObserver API
const ob = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
// 出现在视口中
console.dir(entry)
const animation = map.get(entry.target)
animation && animation.play()
ob.unobserve(entry.target) // 播放过后就不再播放动画
}
}
})
function isBelowViewPort(el) {
const rect = el.getBoundingClientRect()
return rect.top - window.innerHeight > 0
}
export default {
mounted(el, binding) {
// 判断当前元素是否在视口之上或者视口内,在的话不需要播放动画
if (!isBelowViewPort(el))
return
// 元素挂载后使用 Web Animation API 来应用动画。写法为el.animate(关键帧, 配置)
const animation = el.animate([{
transform: `translateY(${DISTANCE}px)`,
opacity: 0.5
}, {
transform: 'translateY(0)',
opacity: 1
}], {
duration: DURATION,
ease: 'ease-out',
fill: 'forwards'
})
animation.pause() // 先暂停动画,等待指令的触发
map.set(el, animation)
ob.observe(el) // 观察元素是否进入视口
},
unmounted(el) {
ob.unobserve(el) // 元素卸载后断开观察
}
}