usePopover.ts 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. import { getCurrentInstance, ref } from 'vue'
  2. import { getRect, isObj } from '../common/util'
  3. export function usePopover(visibleArrow = true) {
  4. const { proxy } = getCurrentInstance() as any
  5. const popStyle = ref<string>('')
  6. const arrowStyle = ref<string>('')
  7. const showStyle = ref<string>('')
  8. const arrowClass = ref<string>('')
  9. const popWidth = ref<number>(0)
  10. const popHeight = ref<number>(0)
  11. const left = ref<number>(0)
  12. const bottom = ref<number>(0)
  13. const width = ref<number>(0)
  14. const height = ref<number>(0)
  15. const top = ref<number>(0)
  16. function noop() {}
  17. function init(
  18. placement:
  19. | 'top'
  20. | 'top-start'
  21. | 'top-end'
  22. | 'bottom'
  23. | 'bottom-start'
  24. | 'bottom-end'
  25. | 'left'
  26. | 'left-start'
  27. | 'left-end'
  28. | 'right'
  29. | 'right-start'
  30. | 'right-end',
  31. visibleArrow: boolean,
  32. selector: string
  33. ) {
  34. // 初始化 class
  35. if (visibleArrow) {
  36. const arrowClassArr = [
  37. `wd-${selector}__arrow`,
  38. placement === 'bottom' || placement === 'bottom-start' || placement === 'bottom-end' ? `wd-${selector}__arrow-up` : '',
  39. placement === 'left' || placement === 'left-start' || placement === 'left-end' ? `wd-${selector}__arrow-right` : '',
  40. placement === 'right' || placement === 'right-start' || placement === 'right-end' ? `wd-${selector}__arrow-left` : '',
  41. placement === 'top' || placement === 'top-start' || placement === 'top-end' ? `wd-${selector}__arrow-down` : ''
  42. ]
  43. arrowClass.value = arrowClassArr.join(' ')
  44. }
  45. // 初始化数据获取
  46. getRect('#target', false, proxy).then((rect) => {
  47. if (!rect) return
  48. left.value = rect.left as number
  49. bottom.value = rect.bottom as number
  50. width.value = rect.width as number
  51. height.value = rect.height as number
  52. top.value = rect.top as number
  53. })
  54. // 用透明度可在初始化时获取到pop尺寸
  55. getRect('#pos', false, proxy).then((rect) => {
  56. if (!rect) return
  57. popWidth.value = rect.width as number
  58. popHeight.value = rect.height as number
  59. })
  60. }
  61. function control(
  62. placement:
  63. | 'top'
  64. | 'top-start'
  65. | 'top-end'
  66. | 'bottom'
  67. | 'bottom-start'
  68. | 'bottom-end'
  69. | 'left'
  70. | 'left-start'
  71. | 'left-end'
  72. | 'right'
  73. | 'right-start'
  74. | 'right-end',
  75. offset: number | number[] | Record<'x' | 'y', number>
  76. ) {
  77. // arrow size
  78. const arrowSize = visibleArrow ? 9 : 0
  79. // 上下位(纵轴)对应的距离左边的距离
  80. const verticalX = width.value / 2
  81. // 上下位(纵轴)对应的距离底部的距离
  82. const verticalY = arrowSize + height.value + 5
  83. // 左右位(横轴)对应的距离左边的距离
  84. const horizontalX = width.value + arrowSize + 5
  85. // 左右位(横轴)对应的距离底部的距离
  86. const horizontalY = height.value / 2
  87. let offsetX = 0
  88. let offsetY = 0
  89. if (Array.isArray(offset)) {
  90. offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset[0]
  91. offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + (offset[1] ? offset[1] : offset[0])
  92. } else if (isObj(offset)) {
  93. offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset.x
  94. offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + offset.y
  95. } else {
  96. offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset
  97. offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + offset
  98. }
  99. // const offsetX = (verticalX - 17 > 0 ? 0 : verticalX - 25) + offset
  100. // const offsetY = (horizontalY - 17 > 0 ? 0 : horizontalY - 25) + offset
  101. const placements = new Map([
  102. // 上
  103. ['top', [`left: ${verticalX}px; bottom: ${verticalY}px; transform: translateX(-50%);`, 'left: 50%;']],
  104. [
  105. 'top-start',
  106. [
  107. `left: ${offsetX}px; bottom: ${verticalY}px;`,
  108. `left: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px;`
  109. ]
  110. ],
  111. [
  112. 'top-end',
  113. [
  114. `right: ${offsetX}px; bottom: ${verticalY}px;`,
  115. `right: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px; transform: translateX(50%);`
  116. ]
  117. ],
  118. // 下
  119. ['bottom', [`left: ${verticalX}px; top: ${verticalY}px; transform: translateX(-50%);`, 'left: 50%;']],
  120. [
  121. 'bottom-start',
  122. [`left: ${offsetX}px; top: ${verticalY}px;`, `left: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px;`]
  123. ],
  124. [
  125. 'bottom-end',
  126. [
  127. `right: ${offsetX}px; top: ${verticalY}px;`,
  128. `right: ${(popWidth.value >= width.value ? width.value / 2 : popWidth.value - 25) - offsetX}px; transform: translateX(50%);`
  129. ]
  130. ],
  131. // 左
  132. ['left', [`right: ${horizontalX}px; top: ${horizontalY}px; transform: translateY(-50%);`, 'top: 50%']],
  133. [
  134. 'left-start',
  135. [
  136. `right: ${horizontalX}px; top: ${offsetY}px;`,
  137. `top: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px;`
  138. ]
  139. ],
  140. [
  141. 'left-end',
  142. [
  143. `right: ${horizontalX}px; bottom: ${offsetY}px;`,
  144. `bottom: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px; transform: translateY(50%);`
  145. ]
  146. ],
  147. // 右
  148. ['right', [`left: ${horizontalX}px; top: ${horizontalY}px; transform: translateY(-50%);`, 'top: 50%']],
  149. [
  150. 'right-start',
  151. [
  152. `left: ${horizontalX}px; top: ${offsetY}px;`,
  153. `top: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px;`
  154. ]
  155. ],
  156. [
  157. 'right-end',
  158. [
  159. `left: ${horizontalX}px; bottom: ${offsetY}px;`,
  160. `bottom: ${(popHeight.value >= height.value ? height.value / 2 : popHeight.value - 20) - offsetY}px; transform: translateY(50%);`
  161. ]
  162. ]
  163. ])
  164. popStyle.value = placements.get(placement)![0]
  165. arrowStyle.value = placements.get(placement)![1]
  166. }
  167. return { popStyle, arrowStyle, showStyle, arrowClass, init, control, noop }
  168. }