一、效果展示
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>