index.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. /*
  2. 文档来源 https://blog.csdn.net/weixin_41277748/article/details/138855857
  3. 代码来源 https://github.com/DearWQ/wq-admin-system/blob/main/src/directive/toolTip/MyToolTip.vue
  4. */
  5. // 引入组件
  6. import {createApp, nextTick} from "vue";
  7. import MyToolTip from './index.vue'
  8. function getViewportSize() {
  9. return {
  10. windowWidth: window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth,
  11. windowHeight: window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
  12. };
  13. }
  14. let windowWidth = getViewportSize().windowWidth
  15. let windowHeight = getViewportSize().windowHeight
  16. // 位置定位
  17. function calculationLocation(el, target, placements) {
  18. if (!el || !target) return;
  19. el.tooltipPosition.y = 0;
  20. el.tooltipPosition.x = 0;
  21. // @ts-ignore
  22. let el_dom = target.w_tooltip.firstElementChild.getBoundingClientRect()
  23. let target_dom = target.getBoundingClientRect()
  24. if (placements === "left") {
  25. el.tooltipPosition.x = 0
  26. el.tooltipPosition.y = target_dom.y - target_dom.height / 2 + 14
  27. el.tooltipPosition.right = windowWidth - target_dom.left + target_dom.height / 2 - 22
  28. } else if (placements === "left-right") {
  29. el.tooltipPosition.x = 0
  30. el.tooltipPosition.y = target_dom.y - (el_dom?.height || 0) / 2 + target_dom.height / 2
  31. el.tooltipPosition.left = target_dom.left + target_dom.width + 10
  32. } else if (placements === "bottom") {
  33. el.tooltipPosition.x = target_dom.x + target_dom.width / 2 - (el_dom?.width || 0) / 2
  34. el.tooltipPosition.y = target_dom.y + target_dom.height + 10
  35. if (el.tooltipPosition.x > (windowWidth-el_dom.width) && target_dom.left + el_dom.width > windowWidth) {
  36. el.tooltipPosition.x = windowWidth - el_dom.width
  37. el.arrowPosition.left = (el_dom.width / 10) * 8
  38. }
  39. if (el.tooltipPosition.x < 0) {
  40. el.arrowPosition.left = (el_dom.width / 10) * 2
  41. }
  42. } else if (placements === "right") {
  43. el.tooltipPosition.x = 0
  44. el.tooltipPosition.y = target_dom.y - (el_dom?.height || 0) / 2 + target_dom.height / 2
  45. el.tooltipPosition.left = target_dom.left + target_dom.width + 10
  46. } else if (placements === "right-left") {
  47. //提示文字的位置
  48. el.tooltipPosition.x = 0;
  49. el.tooltipPosition.y = target_dom.y - target_dom.height / 2;
  50. el.tooltipPosition.right = windowWidth - target_dom.left + target_dom.height / 2
  51. //三角形的位置
  52. el.arrowPosition.top = target_dom.height
  53. } else if (placements === "top") {
  54. el.tooltipPosition.x = target_dom.x + target_dom.width / 2 - (el_dom?.width || 0) / 2
  55. el.tooltipPosition.y = target_dom.y - target_dom.height - 11
  56. if (el.tooltipPosition.x > (windowWidth-el_dom.width) && target_dom.left + el_dom.width > windowWidth) {
  57. el.tooltipPosition.x = windowWidth - el_dom.width
  58. el.arrowPosition.left = (el_dom.width / 10) * 9
  59. }
  60. if (el.tooltipPosition.x < 0) {
  61. el.arrowPosition.left = (el_dom.width / 10) * 2
  62. }
  63. }
  64. el.tooltipPosition.x = el.tooltipPosition.x < 0 ? 0 : el.tooltipPosition.x
  65. el.tooltipPosition.y = el.tooltipPosition.y < 0 ? 0 : el.tooltipPosition.y
  66. }
  67. /**
  68. * 处理边界情况
  69. * @param {HTMLElement} el
  70. * @param direction
  71. * return {string} props
  72. */
  73. function dealDomBoundary(el, direction) {
  74. let target_dom = el.getBoundingClientRect()
  75. //处理上下左右边界情况
  76. if (target_dom.left < 150 && direction === 'left') {
  77. direction = 'left-right'
  78. }
  79. if (target_dom.right > (windowWidth - 150) && direction === 'right') {
  80. direction = 'right-left'
  81. }
  82. if (target_dom.top > windowHeight / 100 * 90 && direction === 'bottom') {
  83. direction = 'top'
  84. }
  85. if (target_dom.top < windowHeight / 100 * 10 && direction === 'top') {
  86. direction = 'bottom'
  87. }
  88. return direction
  89. }
  90. // 方向
  91. const allPlacements = ['left', 'bottom', 'right', 'top']
  92. function getElStyleAttr(element, attr) {
  93. const styles = window.getComputedStyle(element)
  94. // @ts-ignore
  95. return styles[attr]
  96. }
  97. const isOverflow = (target) => {
  98. const scrollWidth = target.scrollWidth
  99. const offsetWidth = target.offsetWidth
  100. const range = document.createRange()
  101. range.setStart(target, 0)
  102. range.setEnd(target, target.childNodes.length)
  103. const rangeWidth = range.getBoundingClientRect().width
  104. const padding = (parseInt(getElStyleAttr(target, 'paddingLeft'), 10) || 0) + (parseInt(getElStyleAttr(target, 'paddingRight'), 10) || 0)
  105. return (rangeWidth + padding > target.offsetWidth) || scrollWidth > offsetWidth
  106. }
  107. export const ellipsisTooltip = localStorage.getItem('useEllipseTooltip') ? {
  108. mounted(el, binding) {
  109. //获取指令的参数
  110. const {
  111. value: {
  112. placement, content, destroyOnLeave
  113. } = {}
  114. } = binding;
  115. // 加上超出...样式
  116. el.style.overflow = "hidden";
  117. el.style.textOverflow = "ellipsis";
  118. el.style.whiteSpace = "nowrap";
  119. windowWidth = getViewportSize().windowWidth
  120. windowHeight = getViewportSize().windowHeight
  121. //鼠标移开时 清除元素
  122. const onMouseLeave = () => {
  123. if (el.w_tipInstance) {
  124. el.w_tipInstance.hiddenTip()
  125. el.w_tooltip.remove()
  126. el.w_tipInstance = null
  127. el.w_tooltip = null
  128. }
  129. };
  130. const onMouseEnter = () => {
  131. // 判断内容长度 需要展示
  132. if (isOverflow(el)) {
  133. // @ts-ignore
  134. const directiveList = allPlacements.filter(placement => binding.modifiers[placement])
  135. const placements = directiveList.length ? directiveList : allPlacements
  136. if (!el.w_tooltip) {
  137. // 创建tooltip实例
  138. const vm = createApp(MyToolTip)
  139. // 创建根元素
  140. el.w_tooltip = document.createElement('div')
  141. // 挂载到页面
  142. document.body.appendChild(el.w_tooltip)
  143. el.w_tooltip.id = `tooltip_${Math.floor(Math.random() * 10000)}`
  144. el.w_tipInstance = vm.mount(el.w_tooltip)
  145. }
  146. // 设置 tooltip 显示方向 处理边界情况
  147. let direction = dealDomBoundary(el, placement || placements[0] || 'top')
  148. el.w_tipInstance.placements = direction;
  149. // 设置显示内容
  150. el.w_tipInstance.setContent(content || el.innerText)
  151. // 使 tooltip 显示
  152. el.w_tipInstance.showTip()
  153. nextTick(() => {
  154. // 计算 tooltip 在页面中的位置
  155. calculationLocation(el.w_tipInstance, el, direction)
  156. })
  157. el._scrollHandler = () => {
  158. // 重新定位位置
  159. if (el.w_tipInstance) calculationLocation(el.w_tipInstance, el, placements[0])
  160. }
  161. window.addEventListener('scroll', el._scrollHandler)
  162. const _destroyOnLeave = destroyOnLeave || true
  163. if (_destroyOnLeave) el.addEventListener("mouseleave", onMouseLeave);
  164. }
  165. };
  166. el.addEventListener("mouseenter", onMouseEnter);
  167. },
  168. unmounted(el) {
  169. if (el.w_tooltip) {
  170. document.body.removeChild(el.w_tooltip)
  171. }
  172. window.removeEventListener('scroll', el._scrollHandler)
  173. }
  174. } : {}