从0到1开发一个Vue3的新手引导组件(附带遇到的问题以及解决方式)

1. 前言:

新手引导组件,顾名思义,就是强制性的要求第一次使用的用户跟随引导使用应用,可以让一些第一次使用系统的新手快速上手,正好我最近也遇到了这个需求,于是就想着开发一个通用组件拿出来使用(写完之后才发现element就有,后悔了哈哈哈😭😭)

示例图:

  1. 第一步
    第一步

  2. 第二步
    第二步

  3. 第三步
    第三步

2. 使用的技术栈以及组件:

Vue3+Vite+Element+uuid,使用了el-popover组件以及el-button组件

3. 遇到的问题:

开发这个组件不是一个简单的事情,遇到了不少的问题,尤其是样式上的问题,下面我一一说明以及对应的解决方式

  1. 需要将用户可以点击的部分凸出出来:

    在原先我是打算给组件传入一个class类名的,然后通过指定的class类名获取到dom,之后再通过dom.getBoundingclientRect()方法获取指定dom的尺寸以及位置信息,然后再组件中创建一个空白区域,但是我真正尝试的时候才发现一个致命问题,getBoundingclientRect方法获取的位置信息都是对的,但是尺寸却是固定的0,后面才发现,如果新手引导的内容是一个图片的话,就必须要等到图片加载完成后再在页面上进行引导,我尝试了onload之后再进行绘制,虽然实现了想要的效果,但是我觉得这样操作太麻烦了,就抛弃了这种方法

  2. 改为插槽:

    上面的方法行不通,操作也麻烦,然后我就创建了两个插槽,一个是content插槽,此插槽没有任何作用,只是将内容显示出来,一个是target插槽,这里的插槽将会作为新手引导的内容,这里解释下为什么需要两个插槽,因为新手引导组件可能应用于循环中,针对于循环的其他内容,可以使用content插槽填充,需要新手引导的内容再使用target插槽即可

4. 源码讲解(请查看详细注释):

<!--
 * @Author: wangZhiyu <w3209605851@163.com>
 * @Date: 2024-07-05 09:28:39
 * @LastEditTime: 2024-07-11 14:05:36
 * @LastEditors: wangZhiyu <w3209605851@163.com>
 * @Descripttion: /新手引导组件.vue
-->
<template>
  <!-- 新手引导插槽 -->
  <div class="targetSlot" v-if="isTarget">
    <!-- 提示文字区域 -->
    <el-popover :popper-style="`width:auto;color:#000;font-size:20px;z-index:${forceShowPopover ? 9999 : 2024}`" :visible="visible" placement="top" :offset="25">
      <!-- popover内的元素 -->
      <div style="white-space: nowrap; display: flex; align-items: center; justify-content: center">
        <el-button v-if="isShowAudioCourse" type="info" circle size="small" style="font-size: 18px">?</el-button>
        <span style="margin: 0 5px">{{ tipMessage }}</span>
        <el-button v-if="readBtn" type="primary" @click="onRead">{{ readMessage }}</el-button>
      </div>

      <!-- 触发popover的元素 -->
      <template #reference>
        <!-- 元素背景高亮区域 -->
        <div :class="`highLightArea highLightArea_${uuid}`" v-if="isFoucsArea"></div>
      </template>
    </el-popover>
    <slot name="target"></slot>
  </div>
  <!-- <slot name="target"></slot> -->

  <!-- 普通内容插槽(根据传入的参数进行动态改变) -->
  <slot :name="isTarget ? 'content' : 'target'"></slot>

  <!-- 新手引导遮罩层(这里给遮罩层添加了一个点击事件,并且组织事件冒泡,是为了避免点击遮罩层所产生事件冒泡罩层影响) -->
  <div class="maskArea" @click.stop="() => {}" v-if="isFoucsArea && value"></div>
</template>

<script setup>
// 导入UUID库
import { v4 as uuidv4 } from 'uuid';

import { ref, nextTick, onMounted, useSlots, watch } from 'vue';

// 设置是否显示提示信息
const visible = ref(false);

// 生成一个uuid
const uuid = uuidv4();

// 抛出事件
const emits = defineEmits(['onRead']);

// 父组件传入的参数
const props = defineProps({
  // 是否继承父组件样式
  isInheritStyle: {
    type: Boolean,
    default: () => false,
  },
  // 高亮区域盒子的占比大小-->宽
  highLightAreaWidth: {
    type: [String, Number],
  },
  // 高亮区域盒子的占比大小-->高
  highLightAreaHeight: {
    type: [String, Number],
  },
  // 高亮区域盒子的占比位置-->垂直距离调整
  highLightAreaTop: {
    type: [String, Number],
  },
  // 高亮区域盒子的占比位置-->水平距离调整
  highLightAreaLeft: {
    type: [String, Number],
  },
  // 是否强制显示提示(避免提示被遮住的情况)
  forceShowPopover: {
    type: Boolean,
    default: () => false,
  },
  // 是否显示已阅读按钮
  readBtn: {
    type: Boolean,
    default: () => false,
  },
  // 内部按钮文字
  readMessage: {
    type: String,
    default: () => '下一步',
  },
  // 提示文字
  tipMessage: {
    type: String,
    default: () => '',
  },
  // 是否显示提示按钮,点击后可显示对应的视频教程
  isShowAudioCourse: {
    type: Boolean,
    default: () => false,
  },
  // 是否为当前应该显示的教程步骤(用于一个页面多个步骤的流程引导)
  isTarget: {
    type: Boolean,
    default: () => true,
  },
  // 是否为当前应该显示的教程步骤(用于一个页面多个步骤的流程引导)
  value: {
    type: Boolean,
    default: () => true,
  },
});

// 当前组件中被使用的插槽
const slot = useSlots();

// 已完成引导
const onRead = () => {
  visible.value = false;
  emits('onRead');
};

// 是否聚焦按钮,高亮新手引导
let isFoucsArea = ref(false);

// 如果 slot.target 成立,则表示使用了新手引导插槽
isFoucsArea.value = slot.target ? true : false;

// 页面初始化函数
const init = () => {
  nextTick(() => {
    // 判断是否指定了新手引导插槽的高层级盒子继承父元素的全部css属性(最小程度的影响添加一个div布局的影响)
    if (props.isInheritStyle) {
      // 获取新手引导插槽的高层级盒子
      const targetSlot = document.querySelector('.targetSlot');
      targetSlot.className = 'inheritFather';
    }

    // 获取高亮盒子
    const highLightArea = document.querySelector(`.highLightArea_${uuid}`);
    // 判断高亮盒子是否存在
    if (highLightArea) {
      // 根据指定参数配置盒子的高亮区域尺寸
      highLightArea.style.width = `${props.highLightAreaWidth || 100}%`;
      highLightArea.style.height = `${props.highLightAreaHeight || 100}%`;
      highLightArea.style.top = `${props.highLightAreaTop || 50}%`;
      highLightArea.style.left = `${props.highLightAreaLeft || 50}%`;
    }
    // TODO:将显示提示放在setTimeout中,存入宏任务队列,避免顺序执行错误,应该等到highLightArea盒子全部处理完成后再显示提示区域,否则有可能显示的位置不对,这里设置是否显示提示信息表示为isFoucsArea.value,表示只给设置了新手引导插槽中添加提示,其余盒子不添加提示
    setTimeout(() => (visible.value = isFoucsArea.value), 100);
  });
};

watch(
  () => props.isTarget,
  () => {
    setTimeout(() => {
      visible.value = true;
    }, 1);
  }
);

onMounted(init);
</script>

<style>
.maskArea {
  position: fixed;
  top: 0px;
  left: 0px;
  width: 100vw;
  height: 100vh;
  background: rgba(0, 0, 0, 0.4);
  z-index: 1;
  cursor: default;
}
.handGif {
  position: absolute;
  left: 100%;
  width: 60px;
  height: 70px;
  transform: translateX(50%);
}

.targetSlot {
  position: relative;
  z-index: 10;
  display: flex;
  justify-content: center;
  align-items: center;
}

.inheritFather {
  all: inherit;
  z-index: 10;
}
.highLightArea {
  position: absolute;
  width: 100%;
  height: 100%;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border: 3px solid #fff;
  border-radius: 5px;
  z-index: 9;
  font-size: 16px;
  pointer-events: none;
  padding: 10px;
}
</style>

5. 使用新手引导组件示例:

<!--
 * @Author: wangZhiyu <w3209605851@163.com>
 * @Date: 2024-07-09 13:44:13
 * @LastEditTime: 2024-07-09 15:49:38
 * @LastEditors: wangZhiyu <w3209605851@163.com>
 * @FilePath: \Vue3新手引导组件\Vue3-Tour\src\App.vue
 * @Descripttion: 
-->
<template>
  <div class="container">
    <w-tour @onRead="onRead" readBtn tipMessage="这是新手教程的第一步" :isTarget="step === 1" :value="open">
      <template #target>
        <el-button type="primary">按钮1</el-button>
      </template>
    </w-tour>

    <w-tour @onRead="onRead" readBtn tipMessage="这是新手教程的第二步" :isTarget="step === 2" :value="open">
      <template #target>
        <el-button type="primary">按钮2</el-button>
      </template>
    </w-tour>

    <w-tour @onRead="onRead" readBtn tipMessage="这是新手教程的第三步" :isTarget="step === 3" :value="open">
      <template #target>
        <el-button type="primary">按钮3</el-button>
      </template>
    </w-tour>
  </div>
</template>
<script setup>
import { ref } from 'vue';
const step = ref(1);
const open = ref(true);

const onRead = () => {
  step.value++;

  if (step.value === 4) {
    // step.value = 0;
    open.value = false;
  }
  console.log(step.value);
};
</script>

<style scoped>
.container {
  display: flex;
  justify-content: space-around;
  align-items: center;
  width: 500px;
  height: 100px;
  background-color: #7c7777;
  margin: 100px auto;
  padding: 10px;
}
</style>

6. 总结:

以上就是从0到1开发一个能简单使用的新手引导组件的全过程以及源码了,可能使用起来不是很方便,哈哈,这里不得不佩服那些开发通用组件的大佬,确实很不容易👍🏻👍🏻

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

回车不搜索直接页面刷新问题解决

使用技术栈&#xff1a;vue3、elementUiPlus 问题&#xff1a;回车触发方法&#xff0c;会刷新整个页面&#xff0c;不执行搜索 解决方法&#xff1a;在搜索的表单中增加submit.native.prevent submit.native.prevent

LLM 合成数据生成完整指南

大型语言模型是强大的工具&#xff0c;不仅可以生成类似人类的文本&#xff0c;还可以创建高质量的合成数据。这种能力正在改变我们进行 AI 开发的方式&#xff0c;特别是在现实世界数据稀缺、昂贵或隐私敏感的情况下。在本综合指南中&#xff0c;我们将探索 LLM 驱动的合成数据…

学习测试8-数据库mysql操作

下载配置mysql 网络博客 使用 在Linux里 1 service mysql start 启动服务 2 在Navicatt 中连接Linux服务器 3 第一步 将所有文件上传到/opt目录下 第二步 chmod 777 deploy-mysql.sh 第三步 ./deploy-mysql.sh4 service mysql status 查看状态是否安装成功 5 重启mys…

WPS打开PDF文件的目录

WPS打开PDF文件的目录 其实WPS中PDF文件并没有像Word那样标准的目录&#xff0c;但是倒是有书签&#xff0c;和目录一个效果 点击左上角书签选项&#xff0c;或者使用Alt Shift 1快捷键即可

『Django』自带的后台

theme: smartblue 本文简介 点赞 关注 收藏 学会了 上一篇讲了 Django 操作 MySQL 的方法&#xff0c;讲了如何创建模型&#xff0c;如何对数据库做增删改查的操作。但每次修改数据都要写代码&#xff0c;多少有点麻烦。 有没有简单一点的方法呢&#xff1f; 有的有的&#…

centos 安装ffmpeg

这个错误表明在你的 CentOS 系统的默认仓库中没有 ffmpeg 包。CentOS 的默认仓库通常不包含 ffmpeg&#xff0c;因为它涉及一些许可证问题。但是&#xff0c;你可以通过添加第三方仓库来安装 ffmpeg。 使用 EPEL 和 RPM Fusion 仓库 # 安装 EPEL 仓库 sudo yum install epel-…

苹果入局,AI手机或将实现“真智能”?

【潮汐商业评论/原创】 “AI应用智能手机不就是现在的AI手机。” 当被问到现阶段对AI手机的看法时&#xff0c;John如是说。“术业有专攻&#xff0c;那么多APP在做AI功能&#xff0c;下载用就是了&#xff0c;也用不着现在换个AI手机啊。” 对于AI手机&#xff0c;或许大多…

Windows中安装部署MinIo文件系统,在Spring Boot中引入MinIo依赖实现上传文件到MinIo文件系统中

minio安装部署可以看这篇教程&#xff1a;https://blog.csdn.net/qq_43108153/article/details/134016896 创建桶 将私有设置成公开 导入依赖 <!-- minio --> <dependency><groupId>io.minio</groupId><artifactId>minio</artifactId>…

【正点原子K210连载】第二十一章 machine.UART类实验摘自【正点原子】DNK210使用指南-CanMV版指南

1&#xff09;实验平台&#xff1a;正点原子ATK-DNK210开发板 2&#xff09;平台购买地址https://detail.tmall.com/item.htm?id731866264428 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/docs/boards/xiaoxitongban 第二十一章 machine.…

Java中HashMap详解:hash原理、扩容机制、线程不安全及源码分析

前言 HashMap 是 Java 中常用的数据结构之一&#xff0c;用于存储键值对。在 HashMap 中&#xff0c;每个键都映射到一个唯一的值&#xff0c;可以通过键来快速访问对应的值&#xff0c;算法时间复杂度可以达到 O(1)。 HashMap 的实现原理是基于哈希表的&#xff0c;它的底层是…

禁用华为小米?微软中国免费送iPhone15

微软中国将禁用华为和小米手机&#xff0c;要求员工必须使用iPhone。如果还没有iPhone&#xff0c;公司直接免费送你全新的iPhone 15&#xff01; 、 这几天在微软热度最高的话题就是这个免费发iPhone&#xff0c;很多员工&#xff0c;收到公司的通知。因为&#xff0c;登录公司…

ITSS服务经理职责与认可情况分析

随着信息技术的高速发展和广泛应用&#xff0c;企业对信息技术管理和应用的需求日益凸显。 为满足这一迫切需求&#xff0c;对具备专业知识和技能的人才的需求也日益增长&#xff0c;他们负责管理和实施相关项目。 因此&#xff0c;ITSS服务项目经理证书应运而生&#xff0c;这…

提升Selenium在Chrome上的HTML5视频捕获效果的五个方法

在使用Selenium进行网页自动化测试时&#xff0c;捕获HTML5视频是一个常见的需求。然而&#xff0c;许多开发者发现&#xff0c;在使用Chrome浏览器时&#xff0c;视频捕获效果并不理想&#xff0c;经常出现视频背景为空白的问题。本文将概述五种方法&#xff0c;帮助提升Selen…

基于语义的法律问答系统

第一步&#xff0c;准备数据集 第二步&#xff0c;构建索引数据集&#xff0c;问答对数据集&#xff0c;训练数据集&#xff0c;召回评估数据集 第三步&#xff0c;构建dataloader,选择优化器训练模型&#xff0c;之后召回评估 第四步&#xff0c;模型动转静&#xff0c;之后…

eplan软件许可优化解决方案

Eplan软件介绍 Eplan是一款专业的电气设计软件&#xff0c;用于自动化工程和电气系统的设计与文档化。它由德国的Eplan Software & Service GmbH开发&#xff0c;并在全球范围内广泛应用于工程设计和电气工程领域。 Eplan软件提供了全面的工具和功能&#xff0c;以简化和优…

Vue3 引入腾讯地图 包含标注简易操作

1. 引入腾讯地图API JavaScript API | 腾讯位置服务 (qq.com) 首先在官网注册账号 并正确获取并配置key后 找到合适的引入方式 本文不涉及版本操作和附加库 据体引入参数参考如下图 具体以链接中官方参数为准标题 在项目根目录 index.html 中 写入如下代码 <!-- 引入腾…

微软的人工智能语音生成器在测试中达到与人类同等水平

微软公司开发了一种新的神经编解码语言模型 Vall-E&#xff0c;在自然度、语音鲁棒性和说话者相似性方面都超越了以前的成果。它是同类产品中第一个在两个流行基准测试中达到人类同等水平的产品&#xff0c;而且显然非常逼真&#xff0c;以至于微软不打算向公众开放。 VALL-E …

车载测试资料学习和CANoe工具实操车载项目(每日直播)

每日直播时间&#xff1a;&#xff08;直播方式&#xff1a;腾讯会议&#xff09; 周一到周五&#xff1a;20&#xff1a;00-23&#xff1a;00 周六与周日&#xff1a;9&#xff1a;00-17&#xff1a;00 向进腾讯会议学习的&#xff0c;可以关注我并后台留言 直播内容&#xff…

GPMC并口多通道AD采集案例,基于TI AM62x四核处理器平台!

GPMC并口简介 GPMC(General Purpose Memory Controller)是TI处理器特有的通用存储器控制器接口&#xff0c;是AM62x、AM64x、AM437x、AM335x、AM57x等处理器专用于与外部存储器设备的接口&#xff0c;如&#xff1a; (1)FPGA器件 (2)ADC器件 (3)SRAM内存 (4)NOR/NAND闪存 …

electron实现右键菜单保存图片功能

1.创建窗口&#xff0c;加载页面&#xff0c;代码如下&#xff1a; //打开窗口const {ipcMain, BrowserWindow} require("electron") const saveImage require("../ipcMain/saveImage") let win null; ipcMain.handle(on-open-event, (event, args) &g…