一、安装
由于本工程是采用lerna实现的单工程多bundle的形式,所以在安装时候按照lerna的安装标准执行命令如下:
lerna add driver.js --scope=vr-config // 将 driver.js 安装到 vr-config工程
复制代码
在其他工程中的安装:
yarn add driver.js
或
npm install driver.js
复制代码
二、在需要加引导的页面引入并使用
1.基本使用
<script lang="ts">
import { onMount } from 'svelte';
import Driver from "driver.js";
onMount(() => {
// 新手引导
showTips();
})
// 新手指引
const showTips = () => {
const driver = new Driver();
driver.highlight({
element: "#driver-demo",
stageBackground: "#ffa0a0",
popover: {
title: "温馨提示",
description: "这是本站的首页",
position: "bottom",
className: "first-step"
}
});
}
</script>
<div class="w-full relative flex justify-center items-center bg-color" >
<h1>测试driver.js</h1>
<button id="driver-demo">新手指引测试</button>
</div>
<style>
.container-height {
height: calc(100% - 38px);
}
</style>
复制代码
2.提供步骤,以指导用户有关功能的信息 (刷新或者再次打开该网页仍有引导)
<script lang="ts">
import { onMount } from 'svelte';
import Driver from "driver.js";
onMount(() => {
// 新手引导
showTips();
})
// 新手指引
const showTips = () => {
const driver = new Driver({
prevBtnText: "上一步",
nextBtnText: "下一步",
doneBtnText: "我知道了",
closeBtnText: "关闭"
});
const steps = [
{
element: "#first-des",
popover: {
title: "这是本站首页",
description: "本站首页是一些展示性的信息",
position: "bottom"
}
},
{
element: "#second-des",
popover: {
title: "这是素材上传",
description: "素材上传",
position: "top"
}
},
{
element: "#third-des",
popover: {
title: "这是编辑指导",
description: "视频指导",
position: "right"
}
}
];
driver.defineSteps(steps);
driver.start();
}
</script>
<div class="w-full relative flex justify-center items-center bg-color" >
<h1>测试driver.js</h1>
<button id="first-des">首页</button>
<button id="second-des">素材上传</button>
<button id="third-des">编辑指导</button>
</div>
<style>
.container-height {
height: calc(100% - 38px);
}
</style>
复制代码
3.新手引导,当用户第一次访问该网站的时候,才有新手引导,刷新或者再次打开该网页 ,无引导
入口初始化时候 用localStorage存取访问的状态firstVisitEditor
let flag = localStorage.getItem("firstVisitEditor");
用户关闭了新手引导,此时需设置firstVisitEditor,表明该用户已经访问过该网页(不是第一次访问),下次在同一台电脑上再打开该网页的时候,将不再显示新手引导(用户手动清除缓存的情况除外)
onReset: (ele) => {
//Called when overlay is about to be cleared; 用户关闭了新手引导,此时需设置firstVisitEditor,表明该用户已经访问过该网页(不是第一次访问),下次在同一台电脑上再打开该网页的时候,将不再显示新手引导(用户手动清除缓存的情况除外)
localStorage.setItem(
"firstVisitEditor",
JSON.stringify({ firstVisitEditor: false })
);
},
<script lang="ts">
import { onMount } from 'svelte';
// 引入driver.js
import Driver from "driver.js";
// 入口初始化时候 用localStorage存取访问的状态firstVisitEditor
onMount(() => {
// 新手引导
let flag = localStorage.getItem("firstVisitEditor");
if (!flag) {
// flag不存在,即当用户第一次访问该网页的时候,调用showTips,显示新手引导
showTips();
}
})
// 新手指引
// 每个步骤都有唯一的id 对应到页面的html中的id
const showTips = () => {
const driver = new Driver({
prevBtnText: "上一步",
nextBtnText: "下一步",
doneBtnText: "我知道了",
closeBtnText: "关闭",
onReset: (ele) => {
//Called when overlay is about to be cleared; 用户关闭了新手引导,此时需设置firstVisitEditor,表明该用户已经访问过该网页(不是第一次访问),下次在同一台电脑上再打开该网页的时候,将不再显示新手引导(用户手动清除缓存的情况除外)
localStorage.setItem(
"firstVisitEditor",
JSON.stringify({ firstVisitEditor: false })
);
},
});
const steps = [
{
element: "#first-des",
popover: {
title: "这里是页面列表区域",
description: "在这里你可以直观地看到你的页面列表",
position: "right",
},
},
{
element: "#second-des",
popover: {
title: "添加组件前,你需要添加一个页面来承载组件",
description: "VR 场景可以添加多个页面,分别可以添加2D和3D页面,每个组件添加前需要创建一个页面",
position: "bottom",
},
},
{
element: "#third-des",
popover: {
title: "这是组件列表开关",
description: "开关打开后,点击组件列表区域,你可以快速选择组件来进行拖动编辑",
position: "top",
},
},
{
element: "#fourth-des",
popover: {
title: "这是模型素材开关",
description: "如果你还没有想好要做成什么样子,打开模型素材开关来看看我们的成品区域吧",
position: "top",
},
},
{
element: "#mainIframe",
popover: {
title: "介绍中间部分",
description: "中间是可视化拖动区域,对于一些组件你都可以拖动来改变他的位置/大小",
position: "right",
},
},
{
element: "#sixth-des",
popover: {
title: "介绍属性栏",
description: "这里是属性编辑区域,您可以对组件的属性进行修改",
position: "left",
},
},
];
driver.defineSteps(steps);
driver.start();
}
</script>
<div class="container-height w-full relative flex justify-center items-center bg-color" >
<div class="h-full absolute flex flex-row top-0 overflow-hidden" style="left: 205px;">
<ComponentsMenu></ComponentsMenu>
</div>
<Container></Container>
<!-- <div id="WebGL-output" /> -->
<!-- <Helper /> -->
<div class="h-full absolute flex flex-row left-0 top-0">
<Layout></Layout>
<TemplateLayout></TemplateLayout>
</div>
// id和steps中id相对应 其他id在子组件中
<div id="sixth-des" class="h-full absolute flex flex-row right-0 top-0">
<MaterielForm></MaterielForm>
</div>
<EventModal/>
</div>
<style>
.container-height {
height: calc(100% - 38px);
}
</style>
复制代码
4.跨组件跨路由时候新手指引如何设置呢?
每个路由子组件入口组件中分别在localStorage中设置不同的参数,来标识当前的路由下的组件或者页面,是否是第一次访问的
如我的项目分为三个路由,也就是页面链接一共是三个,我也不知道用户什么时候跳转到下一个路由页面,所以每个路由子页面都设置不用的状态来管理
PhoneEdge.svelte中
onMount(
() => {
// 新手引导
let flag = localStorage.getItem("firstVisitEdge");
if (!flag) {
// flag不存在,即当用户第一次访问该网页的时候,调用showTips,显示新手引导
showTips();
}
)
// 新手指引
const showTips = () => {
const driver = new Driver({
prevBtnText: "上一步",
nextBtnText: "下一步",
doneBtnText: "我知道了",
closeBtnText: "关闭",
stageBackground: '#808080',
onReset: (ele) => {
//Called when overlay is about to be cleared; 用户关闭了新手引导,此时需设置firstVisitEdge,表明该用户已经访问过该网页(不是第一次访问),下次在同一台电脑上再打开该网页的时候,将不再显示新手引导(用户手动清除缓存的情况除外)
localStorage.setItem(
"firstVisitEdge",
JSON.stringify({ firstVisitEdge: false })
);
},
});
const vidoHtml=`<video controls width="450">
<source src="https://www.w3schools.com/media/cc0-videos/flower.webm"
type="video/webm">
<source src="https://www.w3schools.com/html/mov_bbb.mp4"
type="video/mp4">
Sorry, your browser doesn't support embedded videos.
</video>`;
const steps = [
{
element: "#first-model",
popover: {
title: "这里是体验示例",
description: vidoHtml,
position: "right",
},
},
{
element: "#second-model",
popover: {
title: "这里是一键矫正",
description: "在测试图片的时候可以使用一键矫正的功能,如果你只是想测试我们的功能,那就可以直接点击这个按钮哦",
position: "right",
},
}
];
driver.defineSteps(steps);
driver.start();
}
复制代码
PhotoUpload.svelte
onMount(() => {
// 新手引导
let flag = localStorage.getItem("firstVisitUpload");
if (!flag) {
// flag不存在,即当用户第一次访问该网页的时候,调用showTips,显示新手引导
showTips();
}
});
// 新手指引
const showTips = () => {
const driver = new Driver({
prevBtnText: "上一步",
nextBtnText: "下一步",
doneBtnText: "我知道了",
closeBtnText: "关闭",
// stageBackground: '#808080',
onReset: (ele) => {
//Called when overlay is about to be cleared; 用户关闭了新手引导,此时需设置firstVisitUpload,表明该用户已经访问过该网页(不是第一次访问),下次在同一台电脑上再打开该网页的时候,将不再显示新手引导(用户手动清除缓存的情况除外)
localStorage.setItem(
"firstVisitUpload",
JSON.stringify({ firstVisitUpload: false })
);
},
});
const steps = [
{
element: "#first-img",
popover: {
title: "这里是图片上传",
description: "点击/拖拽全景图(检测图片是否为 2: 1 的比例)至指定区域(上传规定的全景图,即可开启VR世界)",
position: "right",
},
},
{
element: "#second-img",
popover: {
title: "这里是体验示例",
description: "如果您没有全景图,可以【使用体验示例】区域使用我们的示例图片;如果还没有拍摄好全景图,也欢迎使用我们的示例全景图",
position: "right",
},
}
];
driver.defineSteps(steps);
driver.start();
}
复制代码
Editor.svelte
onMount(() => {
// 新手引导
let flag = localStorage.getItem("firstVisitEditor");
if (!flag) {
// flag不存在,即当用户第一次访问该网页的时候,调用showTips,显示新手引导
showTips();
}
})
// 新手指引
const showTips = () => {
const driver = new Driver({
prevBtnText: "上一步",
nextBtnText: "下一步",
doneBtnText: "我知道了",
closeBtnText: "关闭",
onReset: (ele) => {
//Called when overlay is about to be cleared; 用户关闭了新手引导,此时需设置firstVisitEditor,表明该用户已经访问过该网页(不是第一次访问),下次在同一台电脑上再打开该网页的时候,将不再显示新手引导(用户手动清除缓存的情况除外)
localStorage.setItem(
"firstVisitEditor",
JSON.stringify({ firstVisitEditor: false })
);
},
});
const steps = [
{
element: "#first-des",
popover: {
title: "这里是页面列表区域",
description: "在这里你可以直观地看到你的页面列表",
position: "right",
},
},
{
element: "#second-des",
popover: {
title: "添加组件前,你需要添加一个页面来承载组件",
description: "VR 场景可以添加多个页面,分别可以添加2D和3D页面,每个组件添加前需要创建一个页面",
position: "bottom",
},
},
{
element: "#third-des",
popover: {
title: "这是组件列表开关",
description: "开关打开后,点击组件列表区域,你可以快速选择组件来进行拖动编辑",
position: "top",
},
},
{
element: "#fourth-des",
popover: {
title: "这是模型素材开关",
description: "如果你还没有想好要做成什么样子,打开模型素材开关来看看我们的成品区域吧",
position: "top",
},
},
{
element: "#mainIframe",
popover: {
title: "介绍中间部分",
description: "中间是可视化拖动区域,对于一些组件你都可以拖动来改变他的位置/大小",
position: "right",
},
},
{
element: "#sixth-des",
popover: {
title: "介绍属性栏",
description: "这里是属性编辑区域,您可以对组件的属性进行修改",
position: "left",
},
},
];
driver.defineSteps(steps);
driver.start();
}
复制代码
三、从新手指引看实现原理
看秋风的笔记中有文章介绍:从王者荣耀里我学会的前端新手指引:juejin.cn/post/689105…
直接贴代码:
// 蒙层实现
<style>
.guide-mask {
z-index: 999999;
background-color: #000;
top: 0;
bottom: 0;
left: 0;
right: 0;
position: fixed;
opacity: 0.8;
}
</style>
<div class="guide-mask"></div>
复制代码
// 气泡实现
<style>
.tooltip-box:before {
content: "";
position: absolute;
right: 100%;
top: -10px;
left: 20%;
width: 0;
height: 0;
border-left: 10px solid transparent;
border-right: 10px solid transparent;
border-bottom: 13px solid white;
}
</style>
<div class='tooltip-box'>
气泡实现
</div>
复制代码
上面没什么可解释的比较容易理解下面来说下
定位
我们通过 getBoundingClientRect 属性来获取目标元素的大小及其相对于视口的位置。然后通过绝对定位来进行布局。
<style>
...
.guide-helper-layer {
position: absolute;
z-index: 9999998;
background-color: #FFF;
background-color: rgba(255, 255, 255, .9);
border-radius: 4px;
}
.guide-content {
position: absolute;
z-index: 10000000;
background-color: transparent;
}
.guide-mark-relative {
position: relative;
z-index: 9999999 !important;
}
...
</style>
</head>
<body>
<h2>新手指引demo</h2>
<div class="skill guide-mark-relative">
...
</div>
<div class="guide-mask"></div>
<div class="guide-helper-layer" style="width: 472px; height:58px; top:55px;left: 36px;">
<div class='tooltip-box'>
气泡实现
</div>
</div>
<script>
const guideTarget = document.querySelector('.skill')
const tooltip = document.querySelector('.tooltip-box')
var rect = guideTarget.getBoundingClientRect()
const helperLayer = document.querySelector('.guide-helper-layer')
helperLayer.style.left = rect.left - 3 + 'px'
helperLayer.style.top = rect.top - 3 + 'px'
helperLayer.style.width = rect.width + 3 * 2 + 'px'
helperLayer.style.height = rect.height + 3 * 2 + 'px'
tooltip.style.top = rect.height + 3 * 2 + 10 + 5 + 'px'
</script>
复制代码
效果预览&&源码
感谢阅读
❤️关注+点赞+收藏+评论+转发❤️,原创不易,鼓励笔者创作更好的文章
关注公众号小圆脸儿
,一个专注于web前端基础、工程化、面试的前端公众号