vue3 根据点击位置,实现一个用户头像弹框定位
需求背景
最近在做直播后台,涉及到对用户的一些操作,比如关注/取关/禁言/踢出直播间。多个地方都要用,需要封装一个弹框组件
效果图
实现过程分析
- 根据点击元素,获取元素的位置
- 动态设置元素top,left位置
top 和 left 如何获取
根据 点击事件event
参数,来获取位置,根据需要来实际计算位置
- 元素距离左边的距离 + 元素本身的宽度
获取元素top/left,其实直接用getBoundingClientRect 里的bottom / right 即可
代码实现
export const getCurrentDialogXY = (event: Event) => {
const target = event.target as HTMLElement
const { bottom, right } = target.getBoundingClientRect()
return { bottom, right }
}
调用
```js
const avatarHandler = (event: Event) => {
const { right, bottom } = getCurrentDialogXY(event)
currentX.value = right
currentY.value = bottom
}
传入子组件,在子组件内接收到left/top 重新定位即可
<user-info-dialog
:left="currentX"
:top="currentY"
/>
<div ref="userInfoRef" class="dialog-wrap" :style="{ top: top + 'px', left: left + 'px' }"></div>
细节处理
- 通过设置,你会发现弹框始终定位在元素右下角,可以根据自身需求,设置元素位置
通过监听弹框的ref(弹框有显示隐藏逻辑,所以监听弹框的ref即可,然后动态设置位置),
watchEffect(() => {
const userInfoRefValue = userInfoRef.value
if (userInfoRefValue) {
const offsetHeight = userInfoRefValue.offsetHeight // 获取弹框的高度
const offsetWidth = userInfoRefValue.offsetWidth // 获取元素的宽度
// 不管弹框显示是左侧还是右侧,根据需求 都需要 减去 弹框的高度 - 用户头像高度的一半 进行定位
propTop.value = props.top - offsetHeight - userAvatarWidth.value / 2
// 如果是弹框显示在左侧,需要减去弹框的宽度 - 头像的宽度
if (props.roleEnumType === 1) {
propLeft.value = props.left - offsetWidth - userAvatarWidth.value
}
}
})
- 弹框打开之后,要点击所有位置,都可以关闭
- 那我们就需要实现一个朦层(透明色),点击朦层,触发关闭即可
你会发现,其实我们的整个元素,是直接在根节点上,根常规里的,每个弹框,必须要绑定在循环里,才能做一一对应的关系,怎么不太一样呢?
这里是用到的
vue3中的Teleport:将其插槽内容渲染到 DOM 中的另一个位置
这对于我们层级比较多,做全局定位,脱离当前元素,非常好用!!!
当我们触发关闭事件,抛出去让父组件处理即可