从0到1开发一个Vue3的新手引导组件(附带遇到的问题以及解决方式)
1. 前言:
新手引导组件,顾名思义,就是强制性的要求第一次使用的用户跟随引导使用应用,可以让一些第一次使用系统的新手快速上手,正好我最近也遇到了这个需求,于是就想着开发一个通用组件拿出来使用(写完之后才发现element就有,后悔了哈哈哈😭😭)
示例图:
-
第一步

-
第二步

-
第三步

2. 使用的技术栈以及组件:
Vue3+Vite+Element+uuid,使用了el-popover组件以及el-button组件
3. 遇到的问题:
开发这个组件不是一个简单的事情,遇到了不少的问题,尤其是样式上的问题,下面我一一说明以及对应的解决方式
-
需要将用户可以点击的部分凸出出来:
在原先我是打算给组件传入一个
class类名的,然后通过指定的class类名获取到dom,之后再通过dom.getBoundingclientRect()方法获取指定dom的尺寸以及位置信息,然后再组件中创建一个空白区域,但是我真正尝试的时候才发现一个致命问题,getBoundingclientRect方法获取的位置信息都是对的,但是尺寸却是固定的0,后面才发现,如果新手引导的内容是一个图片的话,就必须要等到图片加载完成后再在页面上进行引导,我尝试了onload之后再进行绘制,虽然实现了想要的效果,但是我觉得这样操作太麻烦了,就抛弃了这种方法 -
改为插槽:
上面的方法行不通,操作也麻烦,然后我就创建了两个插槽,一个是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开发一个能简单使用的新手引导组件的全过程以及源码了,可能使用起来不是很方便,哈哈,这里不得不佩服那些开发通用组件的大佬,确实很不容易👍🏻👍🏻
相关文章:
从0到1开发一个Vue3的新手引导组件(附带遇到的问题以及解决方式)
1. 前言: 新手引导组件,顾名思义,就是强制性的要求第一次使用的用户跟随引导使用应用,可以让一些第一次使用系统的新手快速上手,正好我最近也遇到了这个需求,于是就想着开发一个通用组件拿出来使用(写完之后才发现element就有,后悔了哈哈哈😭😭) 示例图…...
概率统计(二)
二维离散型 联合分布律 样本总数为16是因为,两封信分别可以放在4个信箱 边缘分布律 条件分布律 独立性 选填才能用秒杀 联合概率乘积不等于边缘概率的乘积则不独立 二维连续型 区间用一重积分面积用二重积分 离散型随机变量 常见6个分布的期望和方差 离散型随机变…...
文件类:如何将excel文件转为csv文件(且保留时间格式)?
最近有个场景,在ftp服务器上,读取csv文件并入库,但是客户提供的一部分文件却是xls文件,就得搞个将excel转为csv文件的方法,话不多说直接开干。 方法 public static void convertExcelToCSV(String excelFilePath, Str…...
FiddlerScript Rules修改-更改发包中的cookie
直接在fiddler script editor中增加如下处理代码即可 推荐文档oSession -- 参数说明 测试笔记 看云...
直升机停机坪的H代表什么
可为什么直升机的停机坪为什么要用“H”来表示呢? Helicopter 直升机停机坪的“H”来自直升机的英文Helicopter的首字母,也是停机坪的识别标志,表示可用于直升机的垂直起降,方便于直升机飞行员在空中能快速识别降落位置。 另外…...
hyperworks软件许可优化解决方案
Hyperworks软件介绍 Altair 仿真驱动设计改变了产品开发,使工程师能够减少设计迭代和原型测试。提升科学计算能力扩大了应用分析的机会,使大型设计研究能够在限定的项目时间完成。现在,人工智能在工程领域的应用再次改变了产品开发。基于物理…...
四川赤橙宏海商务信息咨询有限公司抖音电商服务靠谱吗?
在数字化浪潮席卷全球的今天,电商行业蓬勃发展,各种新兴电商平台层出不穷。其中,抖音电商以其独特的社交属性和庞大的用户基础,迅速崛起为行业新星。四川赤橙宏海商务信息咨询有限公司,作为专注于抖音电商服务的佼佼者…...
鸿蒙开发:Universal Keystore Kit(密钥管理服务)【密钥派生(C/C++)】
密钥派生(C/C) 以HKDF256密钥为例,完成密钥派生。具体的场景介绍及支持的算法规格,请参考[密钥生成支持的算法]。 在CMake脚本中链接相关动态库 target_link_libraries(entry PUBLIC libhuks_ndk.z.so)开发步骤 生成密钥 指定密钥别名。 初始化密钥属…...
【ARMv8/v9 GIC 系列 6 -- 中断优先级详细介绍】
请阅读【ARM GICv3/v4 实战学习 】 文章目录 Interrupt prioritizationInterrupt Priority ValueSGI And PPI Priority SetSecure And Non-secure Priority AccessInterrupt prioritization 在ARM GICv3和GICv4架构中,中断的优先级化(prioritization)是通过以下几种方式来描…...
【CORS 报错】跨域请求问题:CORS 多种环境下的解决方案
🔥 个人主页:空白诗 文章目录 一、CORS错误的常见原因二、解决方案1. Vue3 Vite项目下的解决方案创建Vue3 Vite项目配置Vite的代理发送请求 2. jQuery项目下的解决方案使用CORS请求头使用JSONP 3. 其他环境下的解决方案使用服务器端代理设置CORS头使用…...
【Scrapy】深入了解 Scrapy 中间件中的 process_spider_output 方法
准我快乐地重饰演某段美丽故事主人 饰演你旧年共寻梦的恋人 再去做没流着情泪的伊人 假装再有从前演过的戏份 重饰演某段美丽故事主人 饰演你旧年共寻梦的恋人 你纵是未明白仍夜深一人 穿起你那无言毛衣当跟你接近 🎵 陈慧娴《傻女》 Scrapy 是…...
GigE Vision GVCP/GVSP
GIGE协议,全称Gigabit Ethernet Vision协议,是一种基于千兆以太网(Gigabit Ethernet)技术开发的相机接口标准,主要用于高速图像采集和处理。该协议通过以太网技术实现图像数据和控制信号的传输,具有低成本、…...
结合C++智能指针聊聊观察者模式
0. 问题 问题是这样,三个类A,B,C。AC都有指针指向同一个B类对象,C类可以回收了刚刚生成的B类对象的内存,A类应该对这个指针进行如何操作,才能确保使用该指针时不会产生野指针问题发生未定义结果? 这是前两天面试的时候…...
【React】监听浏览器返回事件
文章目录 popstate事件:点击浏览器前进,后退会触发popstate事件即,在同一文档的两个历史记录条目之间导航会触发该事件 useEffect(() > {const handlePageBack () > {// 此处写你想要触发的事件console.log(浏览器返回按钮被点击了&a…...
python用selenium网页模拟时无法定位元素解决方法1
进行网页模拟时,有时我们明明可以复制出元素的xpath,但是用selenium的xpath click无法点击到元素。这种情况有几种原因,本文写其中一种——iframe 比如下图网址,第二行出现iframe,则往下的行内元素都会定位不到&#…...
css中文字书写方向
writing-mode 是 CSS 中的一个属性,用于设置文本、内联元素、表格单元格和表格列的书写方向、文本排列以及块流方向。以下是对 writing-mode 属性的详细介绍: 1. 语法和值 语法:writing-mode: horizontal-tb | vertical-rl | vertical-lr |…...
医学王者刊!影响因子自创刊只增不减,3区跃升1区,国人发文占比6成!
【SciencePub学术】今天给大家推荐的是一本医学领域的SCI,是1本颇富潜力的国产期刊。影响因子自创刊以来就逐年上涨,凭借自己的努力从中科院3区跃迁至中科院1区,据说很多人已经靠信息差吃上了这本期刊的红利,接下来给大家解析一下…...
数据建设实践之大数据平台(五)
安装hive 上传安装包到/opt/software目录并解压 [bigdata@node101 software]$ tar -zxvf hive-3.1.3-with-spark-3.3.1.tar.gz -C /opt/services [bigdata@node101 services]$ mv apache-hive-3.1.3-bin apache-hive-3.1.3 配置环境变量 export JAVA_HOME=/opt/services…...
js原型和类---prototype,__proto__,new,class
原型和原型链 在js中,所有的变量都有原型,原型也可以有原型,原型最终都指向Object 什么是原型 在js中,一个变量被创建出来,它就会被绑定一个原型;比如说,任何一个变量都可以使用console.log打…...
bevfomer self-att to transformer to tensorrt
self-attentation https://blog.csdn.net/weixin_42110638/article/details/134016569 query input* Wq key input* Wk value input* Wv output 求和 query . key * value detr multiScaleDeformableAttn Deformable Attention Module,在图像特征上&#…...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15
缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下: struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
Cesium1.95中高性能加载1500个点
一、基本方式: 图标使用.png比.svg性能要好 <template><div id"cesiumContainer"></div><div class"toolbar"><button id"resetButton">重新生成点</button><span id"countDisplay&qu…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
高频面试之3Zookeeper
高频面试之3Zookeeper 文章目录 高频面试之3Zookeeper3.1 常用命令3.2 选举机制3.3 Zookeeper符合法则中哪两个?3.4 Zookeeper脑裂3.5 Zookeeper用来干嘛了 3.1 常用命令 ls、get、create、delete、deleteall3.2 选举机制 半数机制(过半机制࿰…...
Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
Robots.txt 文件
什么是robots.txt? robots.txt 是一个位于网站根目录下的文本文件(如:https://example.com/robots.txt),它用于指导网络爬虫(如搜索引擎的蜘蛛程序)如何抓取该网站的内容。这个文件遵循 Robots…...
