vue3:通过【自定义指令】实现自定义的不同样式的tooltip

一、效果展示

vue3自定义不同样式的tooltip

二、实现思路

1.ts文件

在ts文件中创建一个全局容器

import一个容器组件,用于存放自定义的各式组件

创建一个指令并获取到指令传递的数据,并为容器组件传值

2.容器组件

用于存放自定义Tooltip样式的组件,同时在这里会设置容器位置。

3.各种子组件

写自己需要的样式

三、代码

ts文件

import { createApp, ref } from "vue";
import myToolTip from "./myToolTipIndex.vue";

//tooltip是否显示
export const tooltipDisplay = ref("none");

//给myToolTip组件传递text
export const text = ref<string>("");

//给myToolTip组件传递位置
export const finalLeft = ref<number>(0);
export const finalTop = ref<number>(0);

//选择组件,默认为1号组件
export const mode = ref<number>(1);

// 创建一个全局div容器用于显示文本
const tooltipDiv = document.createElement("div");
// 将全局div元素添加到body中
document.body.appendChild(tooltipDiv);

// 创建一个全局变量用于存储myToolTip实例
let tooltipApp: any;

// 实现指令 v-my-tooltip
export const vTooltip = {
  //el:指令绑定到的元素。这可以用于直接操作 DOM。binding:一个对象,包含value, oldValue, arg, modifiers等属性。
  mounted(el: any, binding: any) {
    // 显示tooltip
    const showTooltip = (event: any) => {
      //获取元素的绑定的文本内容
      text.value = binding.value;

      // 获取元素的文本内容
      // text.value=el.innerText;

      // 显示
      tooltipDisplay.value = "block";

      // 判断是否有修饰符
      if (binding.modifiers.mode1) {
        mode.value = 1;
      } else if (binding.modifiers.mode2) {
        mode.value = 2;
      }

      // 如果已有实例则更新数据,否则创建实例
      if (tooltipApp) {
        // 已有实例则直接更新text
        text.value = binding.value;
      } else {
        // 没有实例则创建并挂载
        tooltipApp = createApp(myToolTip);
        tooltipApp.mount(tooltipDiv);
      }
      // 设置位置不遮挡
      finalLeft.value = event.clientX + 10;
      finalTop.value = event.clientY + 15;
    };

    // 鼠标移出时隐藏tooltip
    const hideTooltip = () => {
      tooltipDisplay.value = "none";
    };

    // 添加事件监听器
    el.addEventListener("mouseenter", showTooltip);
    el.addEventListener("mousemove", showTooltip);
    el.addEventListener("mouseleave", hideTooltip);
  },

  unmounted(el: any) {
    // 移除事件监听器
    el.removeEventListener("mouseenter", el._showTooltip);
    el.removeEventListener("mousemove", el._showTooltip);
    el.removeEventListener("mouseleave", el._hideTooltip);
    // 卸载实例
    tooltipApp.unmount();
    // 移除全局div
    tooltipDiv.remove();
  },
};

2.挂载在全局div的子组件容器

<template>
  <!--移动组件让组件可以不被遮挡-->
  <Teleport to="body">
    <!--容器,用于设置位置-->
    <div
      class="tooltipBox"
      ref="tooltipRef"
      :style="{
        display: tooltipDisplay,
        left: `${posleft}px`,
        top: `${posTop}px`,
      }"
    >
      <!--动态组件,根据mode的值来判断显示哪个组件-->
      <component :is="tooltipComponent" :text="text" />
    </div>
  </Teleport>
</template>
<script setup lang="ts">
import { text, finalLeft, finalTop, tooltipDisplay, mode } from "./toolTip";
import { ref, computed, onMounted, watch, nextTick } from "vue";

// 引入组件,新增的组件在这里引入
import myToolTipOne from "./myToolTipOne.vue";
import myToolTipTwo from "./myToolTipTwo.vue";

const tooltipComponent = computed(() => {
  if (mode.value === 1) {
    return myToolTipOne;
  } else {
    return myToolTipTwo;
  }
});
//获取dom
const tooltipRef = ref<HTMLElement>();

// 存储容器的宽度和高度
const divWidth = ref<number>();
const divHeight = ref<number>();

// 计算视窗的宽度和高度
const windowWidth = window.innerWidth;
const windowHeight = window.innerHeight;

// 计算位置
const posleft = computed(() => {
  if (divWidth && finalLeft && divWidth.value && finalLeft.value) {
    if (finalLeft.value + divWidth.value > windowWidth) {
      return windowWidth - divWidth.value - 8; // 调整left值,使其不超出右边界
    } else {
      return finalLeft.value;
    }
  }
});
const posTop = computed(() => {
  if (divHeight && finalTop && divHeight.value && finalTop.value) {
    if (finalTop.value + divHeight.value > windowHeight) {
      return windowHeight - divHeight.value - 8; // 调整top值,使其不超出下边界
    } else {
      return finalTop.value;
    }
  }
});

onMounted(() => {
  // 获取容器的宽度和高度
  if (tooltipRef.value) {
    divHeight.value = tooltipRef.value.offsetHeight;
    divWidth.value = tooltipRef.value.offsetWidth;
  }
});

watch(text, () => {
  // 监听text的变化,当text变化时,重新获取容器的宽度和高度
  nextTick(() => {
    if (tooltipRef.value) {
      divHeight.value = tooltipRef.value.offsetHeight;
      divWidth.value = tooltipRef.value.offsetWidth;
    }
  });
});
</script>
<style scoped>
.tooltipBox {
  position: absolute;
  width: auto;
  height: auto;
}
</style>
```

3.负责样式的子组件

<template>
  <div class="tooltip">
    <text>{{ text }}</text>
  </div>
</template>
<script setup lang="ts">
import { computed } from "vue";

const props = defineProps({
  text: {
    type: String,
    default: "tooltip",
  },
});

const text = computed(() => props.text);
</script>
<style scoped>
.tooltip {
  width: 200px;
  height: 100px;
  display: flex;
  justify-content: center;
  align-items: center;
  background-color: #1b1010ba;
  color: #ff0000;
  border: 1px solid #ccc;
  padding: 5px;
  border-radius: 5px;
  box-shadow: 0 0 5px rgba(0, 0, 0, 0.3);
  /* transition: all 0.3s; */
}
</style>

四、使用方式

1.传递一个字符串

v-Tooltip.mode1="'15615'"

2.传递一个变量

v-Tooltip.mode2="item.name"

<template>
  <div class="page">
    <div class="box">
      <div v-for="item in fileList" v-Tooltip.mode1="item.name" class="item">
        {{ item.name }}
      </div>
      <div v-for="item in fileList2" v-Tooltip.mode2="'165'" class="item">
        {{ item.name }}
      </div>
    </div>
  </div>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { vTooltip } from "./components/toolTip/toolTip";

const fileList = ref([
  { id: 1, name: "一天两夜只吃了三顿饭" },
  { id: 2, name: "求下联" },
  { id: 3, name: "七荤八素还喝了九瓶酒" },
  { id: 4, name: "求横批" },
  { id: 5, name: "十分难受" },
]);
const fileList2 = ref([
  { id: 1, name: "2727teg" },
  { id: 2, name: "78657twrw" },
  { id: 3, name: "3278wssssssssssssssssssssssssssssrfrf" },
  { id: 4, name: "fsreg74" },
  { id: 5, name: "fewferg54" },
]);
</script>
<style scoped>
.page {
  width: 100%;
  height: 100vh;
  display: flex;
  justify-content: space-around;
  align-items: center;
  background-color: #0b0c0e;
}
.item {
  background-color: #ecaeae;
}
</style>

五、项目位置

GitHub - yigedayouzi/vue3-tooltip-

  • 7
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值