公司要求在自己的平台上实现新手引导,还是跨页面,跨路由的。我找了下网上的新手引导插件,最后选了Intro.js来使用,可能因为它稍微好看那么一丢丢吧。
我写的大多数都是思路,如果能看懂的话应该能轻易实现这些效果,直接粘贴的话估计悬哈。
建议先去Intro.js官网看一下demo,了解一下再来看demo基础上的操作。
我只是个小小的基层程序员,如果有什么让大家看了发笑的想法和逻辑就不要嘲笑我了,请温柔地告诉我谢谢。
以下都是自己真实遇到的问题并且想方设法解决的,有更好的解决办法可以贴出来一起讨论。
最大的问题就是,Intro.js不能跨路由,因为它是在当前页面,根据dom唯一的Id值,来获取并显示新手引导,所以没有渲染的页面它是获取不到的。
所以我想的是,在每个页面都从头加载一次Intro.js,上一个页面引导完毕以后,就直接router.push到下一个路由,然后自动加载,进行引导,引导完了再push到下一个路由,好的,完美套娃。
我们先在utils.js文件里封装一个工具类Intro.js,方便每个页面直接引用。这里我直接贴最终版的代码啦,我觉得还是比较好看懂的。
import introJs from 'intro.js'
import 'intro.js/introjs.css';
import router from '@/router'
import store from '@/store'
let count = 0 // 初始化为0,后面会根据每一步传入的dom的id选择器 判断当前在第几步
let state = 0 // skip: 0 ,done: 1;
export function guide (introSteps) {
// console.log(sessionStorage.getItem('isIntro'))
if (!parseInt(sessionStorage.getItem('isIntro'))) {
return
}
introJs().setOptions({
steps: introSteps,
/* 当位置选择自动的时候,位置排列的优先级 */
positionPrecedence: ["top", "bottom", "right", "left"],
prevLabel: "上一步",
nextLabel: "下一步",
skipLabel: "跳过",
// 这里因为每个页面的最后一步其实就会显示doneLabel了,而实际上我们的新手引导操作并没有完成,需要跳转路由继续,所以显示的是下一步而非完成。
doneLabel: "下一步",
hidePrev: true,
exitOnOverlayClick: false,
/* 是否显示说明的数据步骤*/
showStepNumbers: false,
/* 是否使用点点点显示进度 */
showBullets: false,
/* 是否使用键盘Esc退出 */
exitOnEsc: false,
/* 默认提示位置 */
hintPosition: 'top-left',
/* 说明高亮区域的样式 */
highlightClass: 'customHighlight',
/* 引导说明文本框的样式 */
tooltipClass: 'customTooltip'
}).onchange(function (targetElement) {
// 每次change时触发,我的id样式是“guide-step<id>”
// id代表新手引导的第几步,例如“guide-step1”,通过id判断当前是第几步
count = parseInt(targetElement.id.slice(-1))
}).onafterchange(function (targetElement) {
// 公司设计的新手引导只有七步,如果count == 7了,说明现在是最后一步
// 跳转完成后用js的dom操作将doneLabel由“下一步”改为“完成”
if (count === 7) {
let i = document.getElementsByClassName('introjs-donebutton')[0].innerHTML = '知道了'
}
}).oncomplete(function (targetElement) {
// 点击完成以及跳过按钮后执行的事件,不区分完成和跳过。
state = 1
setTimeout(() => {
if (state) {
judgeCount()
}
}, 200)
}).onexit(function (targetElement) {
// 点击完成按钮后, 执行的事件
}).onskip(function (targetElement) {
// 点击跳过按钮后, 执行的事件
// onskip这个我当时在官网api里查不到,是我自己翻源码翻出来的,折腾半天,无语。
// 因为点击跳过以后是默认完成了新手引导,此时就需要将状态传给后端,保证下一次进入有Intro.js的页面时时不加载新手引导了。
// 因为vue是跨路由写的,每个页面的完成其实都不是用户真正完成了新手引导
// 所以如果不区分完成和跳过各自触发的事件的话,我们就不会知道用户当前是跳过( ==完成了新手引导)还是只是当前页面的完成,需要继续跳转下一个路由。
state = 0
// 存储进缓存,下次进入新手引导路由页时将不在触发Intro.js
store.state.introStepInfo.isIntro = state
sessionStorage.setItem('isIntro', state)
}).start();
}
// 这个是每一步新手引导对应的点击下一步时对应应该进行的操作
// 我这边,1,2步在一个页面,3,4步在一个页面,5,6步在一个页面,第7步在一个页面
// 所以1,3,5的时候我不需要进行特定操作,按照Intro.js走就行了
function judgeCount () {
// 这是事先预存的跳转路由需要的一些数据
let _introStepInfo = JSON.parse(sessionStorage.getItem('introStepInfo'))
switch (count) {
case 1:
break
case 2:
router.push({
path: '/courseDetail',
query: {
id: _introStepInfo.courseId
}
})
break
case 3:
break
case 4:
let _introChapterName = sessionStorage.getItem('chapterName')
router.push({
path: '/coursePreview',
query: {
id: _introStepInfo.courseId,
chapterId: _introStepInfo.chapterId
}
})
break
case 5:
break
case 6:
router.push({
path: '/myCourses'
})
break
case 7:
sessionStorage.setItem('isIntro', 0)
break
}
}
这里要注意一个问题,就是有些dom是要在从后端拿到数据以后渲染了才有的,不渲染的话Intro.js获取不到唯一的id值。
所以我们在axios封装里面,要在全局设定一个值为false的Boolean变量isAxios,在请求回来以后将它变为true。
这只是最基础的,除此以外还要考虑该页面是不是有两个以上需要拿到数据才能渲染的dom,所以这时需要再增加一个全局变量count,请求request的时候+1,response回来的时候-1,count为0的时候再把isAxios设为true。然后我们再在需要渲染Intro.js的页面生命周期 mounted() 里设定一个500毫秒/次的定时器,判断isAxios,为true了,我们就可以进行新手引导了,并且在完成后清除。
这些都是不成熟的,如果有更好的方法大家可以提出。
// 引入自己封装的新手引导函数
import { guide } from '@/utils/intro'
在mounted()生命周期里设定计时器
let i = setInterval(() => {
if (!this.isAxios) {
guide([
{
title: '开放课程',
intro: '查看您已采购的全部课程。(1/7)',
element: document.querySelector('#guide-step1')
},
{
title: '课程资源',
intro: '点击任意一门课程封面进入该课程。(2/7)',
element: document.querySelector('#guide-step2')
}
])
clearInterval(i)
}
}, 500)
其实在封装axios里关于这个还有很多需要注意的小细节,但与intro.js其实关系不大了,本次就不说了,大家多多注意一下就可以了。