【Vue3】如何手写一个Popover气泡卡片组件

基本思路

将气泡卡片分为两部分,分别为箭头和内容部分,通过方法getBoundingClientRect获取按钮相对于视口的位置,具体细节可以复习:点击复习getBoundingClientRect

然后再将箭头和内容定位到合适的位置,关于trigger的方法实现通过click、mouseover、mouseleave、mouseup事件很容易实现,在此不再赘述。
因为后面还要做tooltip组件,所以把获取箭头和内容部分的位置封装为单独的函数。

//getArrowStyle.js
const getArrowStyle = function (placement, refDOMRect, color) {
  let res = {}
  const width = 12
  switch (placement) {
    case 'bottom':
      res = {
        borderBottomColor: color,
        left: refDOMRect.right - refDOMRect.width / 2 - width / 2 + 'px',
        top: refDOMRect.bottom + 'px',
        filter: 'drop-shadow(2px -2px 2px rgba(0, 0, 0, .1))'
      }
      break;
    case 'top':
      res = {
        borderTopColor: color,
        left: refDOMRect.right - refDOMRect.width / 2 - width / 2 + 'px',
        top: refDOMRect.top - width + 'px',
        filter: 'drop-shadow(2px 2px 2px rgba(0, 0, 0, .1))'
      }
      break;
    case 'left':
      res = {
        borderLeftColor: color,
        left: refDOMRect.left - width + 'px',
        top: refDOMRect.bottom - refDOMRect.height / 2 - width / 2 + 'px',
        filter: 'drop-shadow(2px 2px 2px rgba(0, 0, 0, .1))'
      }
      break;
    case 'right':
      res = {
        borderRightColor: color,
        left: refDOMRect.right + 'px',
        top: refDOMRect.bottom - refDOMRect.height / 2 - width / 2 + 'px',
        filter: 'drop-shadow(-2px 2px 2px rgba(0, 0, 0, .1))'
      }
      break;
    default:
      res = { borderBottomColor: color }
      break;
  }
  return res
}
export {
  getArrowStyle
}
//getPlainStyle.js
const getPlainStyle = function (Contentwidth, placement, refDOMRect) {
  const width = Number(Contentwidth) + 40
  const arrowWidth = 12
  let res = {}
  switch (placement) {
    case 'bottom':
      res = {
        left: refDOMRect.right - refDOMRect.width / 2 - width / 2 + 'px',
        top: refDOMRect.bottom + arrowWidth + 'px'
      }
      break;
    case 'top':
      res = {
        left: refDOMRect.right - refDOMRect.width / 2 - width / 2 + 'px',
        bottom: document.body.clientHeight - (refDOMRect.top - arrowWidth) + 'px'
      }
      break;
    case 'left':
      res = {
        transformOrigin: 'right',
        left: refDOMRect.left - arrowWidth - width + 'px',
        top: refDOMRect.bottom - refDOMRect.height / 2 + 'px',
        transform: 'translate(0,-50%)',
      }
      break;
    case 'right':
      res = {
        transformOrigin: 'left',
        transform: 'translate(0,-50%)',
        left: refDOMRect.right + arrowWidth + 'px',
        top: refDOMRect.bottom - refDOMRect.height / 2 + 'px',
      }
      break;
    default:
      break;
  }
  return res
}

export {
  getPlainStyle
}

如何定位获取DOM位置呢?

通过ref定位按钮组件

  <div class="popover-reference" ref="reference" >
    <slot name="reference">
    </slot>
  </div>

在setup的onMounted中获取到实际的元素位置

setup(){
      let reference = ref(null)
      let refDOMRect = reactive({})
            onMounted(() => {
        const DOMRect = reference.value.getBoundingClientRect()
        for (let key in DOMRect) {
          if (typeof DOMRect[key] !== 'function') {
            refDOMRect[key] = DOMRect[key]
          }
        }
       return {
       //别忘记return出去,不然没法获得动态数据
        reference,
        refDOMRect,
      }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值