vue2封装tooltip文本提示框

为什么要这样封装使用?原因是业务数据庞大,table表格的行数列数太多,同时table表格内套用过多 elemet ui 框架的【el-input】【el-tooltip】组件,导致渲染很慢,滚动会导致页面卡顿。Ant Design of Vue的ui项目也存在套用过多【Tooltip 文字提示】组件,导致渲染很慢。为了解决这个问题,就需要减少ui的组件套用(用html标签+css替换ui组件),可以提高渲染速度,同时还需要用虚拟table表格渲染动态渲染的数据。

【index.js】与【JTooltip.vue】文件的目录


image.png

在【main.js】引入

//文本提示工具
import JTooltip from '@/components/aocsas/JTooltip/index';
Vue.use(JTooltip);
image.png

【index.js】代码如下:

/**
在main.js中引入
import JTooltip from '@/components/aocsas/JTooltip/index';
Vue.use(JTooltip); //注册指令
 * */

import JTooltip from "./JTooltip.vue";
const TOOLTIP = {
  install(Vue) {
    let MessageConstructor = Vue.extend(JTooltip);
    //instance相当于Message.vue的this
    let instance = new MessageConstructor();
    instance.$mount();
    document.body.appendChild(instance.$el);
    Vue.nextTick(() => {});

    //注册一个全局自定义指令 v-tooltip="'文本提示内容'"
    Vue.directive("tooltip", {
      bind(el, binding, vnode, oldVnode) {
        el.$message = binding.value;
        el.addEventListener("mouseover", function (e) {
          e.stopPropagation(); //阻止冒泡
          instance.show(el); //鼠标进入显示:传递dom标签元素
        });
        el.addEventListener("mouseout", function (e) {
          instance.hide(); //鼠标离开隐藏
        });
      },
      inserted() {},
      update(el, binding, vnode, oldVnode) {
        el.$message = binding.value; //将更新的数据绑定到dom标签元素上
        if (binding.value !== binding.oldValue) {
          instance.updateDate(); //更新数据
        }
      },
      componentUpdated(el, binding, vnode, oldVnode) {},
      unbind() {},
    });
  },
};
export default TOOLTIP;

【JTooltip.vue】代码如下:

<!-- 
  为什么要这样封装使用?原因是ui框架的tooltip组件,套用过多的话,会导致页面卡顿
  用法:
  <div class="ellipsis1" v-tooltip="'文本提示内容'" width="250" placement="top">这是一个内容文本!</div>
  <div class="ellipsis1" v-tooltip="'文本提示内容'" width="300" placement="bottom">这是一个内容文本!</div>
 -->
<template>
  <div
    class="j-tooltip-layer"
    :class="{ active: isActive }"
    :style="{ ...getStyleObj, width: 'auto' }"
    @mouseover="handleMouseover"
    @mouseout="handleMouseout"
  >
    <div class="j-tooltip-arrow" :placement="placement" :style="{ left: arrowLeft }"></div>
    <div class="j-tooltip-content" :style="{ width: getStyleObj.width }">{{ content }}</div>
  </div>
</template>

<script>
export default {
  name: "JTooltip",
  components: {},
  props: {},
  watch: {},
  computed: {
    getStyleObj() {
      this.rect = JSON.parse(JSON.stringify(this.rect));
      if (Object.keys(this.rect).length === 0) return {};
      let rect = this.rect;
      let left = rect.left + rect.width / 2;
      let top = rect.top - 12;
      let { width, height } = this.getTextWidthHeight(this.content);

      this.arrowLeft = "50%";
      if (rect.left + rect.width / 2 + width / 2 > document.body.clientWidth) {
        //在右边超出了
        left = document.body.clientWidth - width / 2;
        this.arrowLeft = `${width - (document.body.clientWidth - (rect.left + rect.width / 2)) - 2}px`;
      } else if (rect.left + rect.width / 2 < width / 2) {
        //在左边超出了
        left = width / 2;
        this.arrowLeft = `${rect.left + rect.width / 2}px`;
      }

      if (height > rect.top) {
        //在上面超出了(被遮挡了)
        top = rect.top + rect.height + height + 12;
        this.placement = "bottom";
      }
      if (this.placement == "bottom") {
        if (rect.top + rect.height + height > document.body.clientHeight) {
          this.placement = "top";
        } else {
          top = rect.top + rect.height + height + 12;
        }
      }

      return {
        top: top + "px",
        left: left + "px",
        width: width + "px",
      };
    },
  },
  data() {
    return {
      isActive: false,
      content: "",
      maxWidth: 250,
      rect: {},
      domEl: {},
      placement: "top",
      arrowLeft: "",
    };
  },
  mounted() {},
  beforeDestroy() {},
  methods: {
    //鼠标移进来(显示)
    show(domEl) {
      this.domEl = domEl;
      this.isActive = true;
      this.content = this.domEl.$message;
      this.rect = this.domEl.getBoundingClientRect(); //获取元素位置和尺寸
      this.maxWidth = this.domEl.getAttribute("width") || 250;
      this.placement = this.domEl.getAttribute("placement") || "top";
      clearTimeout(this.timer);
    },
    //鼠标移出去(隐藏)
    hide() {
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        this.isActive = false;
        clearTimeout(this.timer);
      }, 100);
    },
    //被指令周期函数调用
    updateDate() {
      this.content = this.domEl.$message;
      this.rect = this.domEl.getBoundingClientRect();
    },
    //获取文字的宽度/高度
    getTextWidthHeight(text = "") {
      const dom = document.createElement("div");
      dom.innerHTML = text;
      dom.classList.add("j-tooltip-content");
      dom.style.position = "absolute";
      dom.style.top = "-999999px";
      dom.style.whiteSpace = "nowrap"; //不换行计算出来的宽度是正确的
      document.body.appendChild(dom);
      let width = dom.offsetWidth + 2;
      width = Math.min(this.maxWidth, width);
      dom.style.whiteSpace = "normal"; //恢复默认值,高度才能展开
      dom.style.width = width + "px";
      dom.style.maxWidth = width + "px";
      let height = dom.offsetHeight;
      document.body.removeChild(dom);
      return {
        width: width, //增加18px是padding左右边距
        height: height, //增加12px是padding上下边距,4px是箭头高度
      };
    },
    handleMouseover() {
      clearTimeout(this.timer);
    },
    handleMouseout() {
      clearTimeout(this.timer);
      this.timer = setTimeout(() => {
        this.isActive = false;
        clearTimeout(this.timer);
      }, 100);
    },
  },
};
</script>

<style>
.j-tooltip-layer {
  position: fixed;
  top: 0px;
  left: 0px;
  z-index: 2147483647;
  visibility: hidden;
  opacity: 0;
  transform: translate(-50%, -100%);
  transition: opacity 0.3s;
}
.j-tooltip-layer.active {
  opacity: 1;
  visibility: visible;
}
.j-tooltip-content {
  min-height: 32px;
  padding: 6px 8px;
  color: #fff;
  text-align: left;
  text-decoration: none;
  display: flex;
  justify-content: center;
  background-color: rgba(0, 0, 0, 0.75);
  border-radius: 4px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  box-sizing: border-box !important;
  font-size: 14px;
  word-break: break-all;
  word-wrap: break-word;
  white-space: normal;
  line-height: normal;
}
.j-tooltip-arrow {
  box-sizing: content-box !important;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  display: block;
  border-width: 4px;
  border-style: solid;
  border-color: transparent;
}
.j-tooltip-arrow[placement="top"] {
  top: 100%;
  border-top-color: rgba(0, 0, 0, 0.75);
}
.j-tooltip-arrow[placement="bottom"] {
  bottom: 100%;
  border-bottom-color: rgba(0, 0, 0, 0.75);
}
</style>


image.png

image.png

注意:如果是套用UI组件过多,导致渲染过慢,建议用纯div+css这种方式替换UI组件,渲染效率会有明显的提升!虚拟表格+原生的div+css解决了项目渲染过慢的问题!
虚拟表格用的是这个框架:
http://www.umyui.com/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容