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

news2024/11/27 13:46:05

一、效果展示

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-

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1558423.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最新2024年增强现实(AR)营销指南(完整版)

AR营销是新的最好的东西&#xff0c;就像元宇宙和VR营销一样。利用AR技术开展营销活动可以带来广泛的利润优势。更不用说&#xff0c;客户也喜欢AR营销&#xff01; 如果企业使用AR&#xff0c;71%的买家会更多地购物。40%的购物者准备在他们可以在AR定制的产品上花更多的钱。…

详解Java线程的状态

一、观察线程的所有状态 线程的状态是⼀个枚举类型 Thread.State public class ThreadState {public static void main(String[] args) {for (Thread.State state : Thread.State.values()) {System.out.println(state);}} } NEW: 安排了⼯作, 还未开始⾏动 RUNNABLE: 可⼯…

JavaSE day16笔记 - string

第十六天课堂笔记 学习任务 Comparable接口★★★★ 接口 : 功能的封装 > 一组操作规范 一个抽象方法 -> 某一个功能的封装多个抽象方法 -> 一组操作规范 接口与抽象类的区别 1本质不同 接口是功能的封装 , 具有什么功能 > 对象能干什么抽象类是事物本质的抽象 &…

MYSQL——索引概念索引结构

索引 索引是帮助数据库高效获取数据的排好序的数据结构。 有无索引时&#xff0c;查询的区别 主要区别在于查询速度和系统资源的消耗。 查询速度&#xff1a; 在没有索引的情况下&#xff0c;数据库需要对表中的所有记录进行扫描&#xff0c;以找到符合查询条件的记录&#…

《深入理解计算机系统》学习(9):链接和执行

目录 一、链接1.1 编译器驱动程序1.2 链接任务 二、目标文件2.1 目标文件三种形式2.2 可重定位目标文件 三、符号3.1 符号表3.2 符号解析3.3 链接器解析多重定义的全局符号 四、重定位4.1 重定位条目4.2 重定位符号引用 五、可执行目标文件5.1 可执行文件结构5.2 加载可执行目标…

设置asp.net core WebApi函数请求参数可空的两种方式

以下面定义的asp.net core WebApi函数为例&#xff0c;客户端发送申请时&#xff0c;默认三个参数均为必填项&#xff0c;不填会报错&#xff0c;如下图所示&#xff1a; [HttpGet] public string GetSpecifyValue(string param1,string param2,string param3) {return $"…

C++格式化输入和输出

格式化输入与输出 除了条件状态外&#xff0c;每个iostream对象还维护一个格式状态来控制IO如何格式化的细节。 格式状态控制格式化的某些方面&#xff0c;如整型值是几进制、浮点值的精度、一个输出元素的宽度等。 标准库定义了一组操纵符来修改流的格式状态。 一个操纵符…

【进程IO】详细讲解文件描述符fd

文章目录 前言什么叫文件描述符FILE与fd的关系 再次理解文件为什么要有文件的方法列表呢&#xff1f; 进程和struct file的关系再次理解open操作 前言 C语言的关于文件操作的各种函数实际上是对系统调用的封装。那么从进程的角度看&#xff0c;一个文件到底是如何被描述的呢&a…

Postwoman 安装

Postwoman作为Postman的女朋友&#xff0c;具有免费开源、轻量级、快速且美观等特性&#xff0c;是一款非常好用的API调试工具。能帮助程序员节省时间&#xff0c;提升工作效率。 Github地址&#xff1a;GitHub - hoppscotch/hoppscotch: &#x1f47d; Open source API devel…

Qt/QML编程之路:画线及倒车影响(48)

前言: 倒车影像中有一个属性比较实用,那就是倒车线,这条线很明显会在视频图像上叠加显示,或者说在视频上面一个图层画的线。这里有一个画线的Qt示例,用于在一个scene上画一个对角线: #include "mainwindow.h" #include <QApplication> #include <QtW…

ES6 学习(一)-- 基础知识

文章目录 1. 初识 ES62. let 声明变量3. const 声明常量4. 解构赋值 1. 初识 ES6 ECMAScript6.0(以下简称ES6)是JavaScript语言的下一代标准&#xff0c;已经在2015年6月正式发布了。它的目标&#xff0c;是使得」JavaScript语言可以用来编写复杂的大型应用程序&#xff0c;成为…

关系网络c++

题目&#xff1a; 代码&#xff1a; #include<bits/stdc.h>using namespace std;int n,x,y;struct node{int num;//编号 int t;//步数 node(){}node(int sum,int tt){numsum;ttt;} }; int mp[101][101];//图 bool flag[101];//标记 queue<node> q; void bfs() {q…

FLASH的读取与写入

FLASH的写入 结合HAL库所给参数&#xff1a; 查阅具体使用芯片的参考手册。 就不在详细解释&#xff0c;英文自行翻译。具体代码如下&#xff1a; /*FLASH写入程序*/ void WriteFlashTest(uint32_t L, uint32_t addr, uint32_t *Data,int Page) {int i0;/* 1/4解锁FLASH*/HAL…

【Anaconda】Linux下Anaconda安装和虚拟环境配置

Linux下Anaconda安装和虚拟环境配置 一、安装anaconda二、conda虚拟环境管理三、jupyter相关启动部署四、遇到问题 下面介绍整体流程&#xff0c;遇到问题优先看“遇到问题章节”&#xff01; 一、安装anaconda 1.下载anaconda安装包 &#xff08;1&#xff09;可以选择在官网…

文件名目录名或卷标语法不正确:数据恢复策略与预防措施

一、文件名目录名或卷标语法不正确的现象 在日常使用电脑或移动设备时&#xff0c;我们经常会遇到“文件名目录名或卷标语法不正确”的错误提示。这种错误通常发生在尝试访问、修改或删除文件、目录或卷标时&#xff0c;系统会提示无法完成操作&#xff0c;因为文件名、目录名…

JavaScript高级 —— 学习(二)

目录 一、深入对象 &#xff08;一&#xff09;创建对象三种方式 1.利用对象字面量创建 2.利用 new Object() 创建 3.利用构造函数创建 &#xff08;二&#xff09;利用构造函数创建对象 1.构造函数介绍 2.约定 3.实例化执行过程 &#xff08;三&#xff09;实例成员…

动态规划之子序列(一)

300.最长递增子序列 给你一个整数数组 nums &#xff0c;找到其中最长严格递增子序列的长度。 子序列是由数组派生而来的序列&#xff0c;删除&#xff08;或不删除&#xff09;数组中的元素而不改变其余元素的顺序。例如&#xff0c;[3,6,2,7] 是数组 [0,3,1,6,2,2,7] 的子序…

Oracle数据库——子查询五

14.1子查询语法 子查询 (内查询) 在主查询之前一次执行完成。子查询的结果被主查询(外查询)使用 。范例一:谁的工资比 Abel 高? 第一:查询Abel的工资是多少。第二:比较大于这个工资的人数。 注意事项: 子查询要包含在括号内。将子查询放在比较条件的右侧。</

docker部署-RabbitMq

1. 参考 RabbitMq官网 docker官网 2. 拉取镜像 这里改为自己需要的版本即可&#xff0c;下面容器也需要同理修改 docker pull rabbitmq:3.12-management3. 运行容器 docker run \ --namemy-rabbitmq-01 \ -p 5672:5672 \ -p 15672:15672 \ -d \ --restart always \ -…

基于opencv的SVM算法的车牌识别系统设计与实现

基于opencv的SVM算法的车牌识别系统设计与实现 车牌识别技术是智能交通系统中的一项关键技术&#xff0c;它能够自动识别车辆的车牌号码。本文将详细介绍如何使用Python编程语言结合OpenCV库和SVM算法来实现车牌识别系统。 系统架构 车牌识别系统主要包括以下几个模块&…