van-popover组件文档中提供placement这个属性,用来控制气泡弹出位置,但是只能写死,无法传入函数或者传入‘auto’,搜了一下全网好像也没有提供这个问题的处理方案。
就会出现一个问题:当触发元素在视口底部,placement设置了'bottom',气泡就会溢出屏幕。
vant的githup仓库上也有这么一条issue提到这个问题:https://github.com/youzan/vant/issues/11600
官方的回复是,加入自动翻转的功能会令组件体积增大,需要评估是否加入,但是作者查看最新的V4版本也没加入,大概率是评估后觉得无需加入,所以还是维持现状。
既然官方不打算支持,就自己在项目中给van-popover组件打个补丁,优化一下用户体验吧。
// src/patch/popover.js
import { Popover } from 'vant'
Popover.methods.onToggle = function (value) {
this.$emit('input', value)
setTimeout(_ => resetPlacement(this), 20)
}
/**
* @description 根据气泡位置,重新设置placement的值,避免气泡显示位置溢出屏幕的问题
* @param {*} that
*/
function resetPlacement(that) {
const { wrapper, popover } = that.$refs
// #reference插槽元素距离视口顶部的剩余距离
const restTop = wrapper.getBoundingClientRect().top
// #reference插槽元素距离视口底部的剩余距离
const restBottom = window.innerHeight - wrapper.getBoundingClientRect().bottom
// 气泡弹窗的总高度
const popoverHeight = popover.$el.scrollHeight
if (/bottom/.test(that.placement) && restBottom < popoverHeight) {
that.placement = that.placement.replace('bottom', 'top') // 直接重置props的值虽然能生效,但vue会有报错,有待解决
return
}
if (/top/.test(that.placement) && restTop < popoverHeight) {
that.placement = that.placement.replace('top', 'bottom')
return
}
}
export default {
install(Vue) {
Vue.component(Popover.name, Popover)
}
}
// src/main.js
import Popover from './patch/popover';
import Vue from 'vue'
Vue.use(Popover)
代码不多,思路也很简单:当点击refence插槽的元素触发气泡弹窗弹出后,判断一下如果H1小于H2,就说明气泡弹窗溢出了视口,就把方向重置一下,bottom改为top,同理,上下左右四个方向都是这样判断。
但是这方案有个问题,就是直接设置了this.placement的值,这个属性是通过props传入的,也就是说,这样做会打破vue的单向数据流,vue会有报错:
但是实际使用的时候通常都是使用静态的字面量,在此组件的绝大部分使用场景中都不会有问题,哪位大神如果看到这,有更好的解决方案或者能去掉以上的报错提示,期待您在评论留言讨论。