经常在网站上会看到首次浏览网页/发布新版本后会出现引导功能,指引操作功能,大概率是通过元素定位实现的,都有哪些成熟的库,如何自定义相关组件,需要注意哪几点?
一、vue-tour
1.1、安装
yarn add vue-tour
1.2、引入
- 入口文件
main.js
import VueTour from 'vue-tour';
require('vue-tour/dist/vue-tour.css');
Vue.use(VueTour);
1.3、使用
<v-tour name="myVueTour" :steps="steps"></v-tour>
1.4、API
- start() 分步指南开始执行
1.5、分步数组
steps: [
{
target: '#v-step-0', // 目标元素选择器
content: '', // 主体内容
before: type => new Promise((resolve, reject) => {
// 耗时/异步操作
resolve('foo')
})
}
]
1.6、属性
options: {
useKeyboardNavigation: false,
labels: {
buttonSkip: '跳过',
buttonPrevious: '上一步',
buttonNext: '下一步',
buttonStop: '完成'
}
}
1.7、回调函数
callbacks: {
onStart: () => void, // 开始
onPreviousStep: (currentStep) => void, // 上一步
onNextStep: (currentStep) => void, // 下一步
onSkip: () => void, // 跳过
onFinish: () => void, // 完成
onStop: () => void, // 关闭
}
1.8、插槽
- header
- content
- actions
<v-tour name="myTour" :steps="steps">
<template slot-scope="tour">
<transition name="fade">
<v-step
v-if="tour.steps[tour.currentStep]"
:key="tour.currentStep"
:step="tour.steps[tour.currentStep]"
:previous-step="tour.previousStep"
:next-step="tour.nextStep"
:stop="tour.stop"
:skip="tour.skip"
:is-first="tour.isFirst"
:is-last="tour.isLast"
:labels="tour.labels"
>
<template v-if="tour.currentStep === 2">
<div slot="actions">
<button @click="tour.previousStep" class="btn btn-primary">Previous step</button>
<button @click="tour.nextStep" class="btn btn-primary">Next step</button>
</div>
</template>
</v-step>
</transition>
</template>
</v-tour>
二、intro.js
虽然是开源插件,但若用在商业应用、网站或插件中需要获得商业许可证。具体使用详见 introjs.com/docs/exampl…
2.1、安装
yarn add intro.js
2.2、引入并使用
import introJs from 'intro.js'
import 'intro.js/introjs.css'
const intro = introJs()
// 更多配置参数请到官方文档查看
intro.setOptions({
nextLabel: '下一个', // 下一个按钮文字
prevLabel: '上一个', // 上一个按钮文字
skipLabel: '跳过', // 跳过按钮文字
doneLabel: '立即体验', // 完成按钮文字
hidePrev: true, // 在第一步中是否隐藏上一个按钮
hideNext: true, // 在最后一步中是否隐藏下一个按钮
exitOnOverlayClick: false, // 点击叠加层时是否退出介绍
showStepNumbers: false, // 是否显示红色圆圈的步骤编号
disableInteraction: true, // 是否禁用与突出显示的框内的元素的交互,就是禁止点击
showBullets: false, // 是否显示面板指示点
steps: [
{
element: '', // 元素
intro: '', // 引导框具体内容
position: '', // 引导框相对目标出现的位置 top right bottom left
}
], // 对应的数组,顺序出现每一步的引导提示
})
export default intro;
2.3、API
- 开始
Intro.start() - 退出引导回调
Intro.onexit(() => {}) - 设置属性
Intro.setOptions
2.4、属性
Intro().setOptions({
nextLabel: '下一步', // 下一个按钮文字
prevLabel: '上一步', // 上一个按钮文字
skipLabel: '跳过', // 跳过按钮文字
doneLabel: '完成', // 完成按钮文字
hidePrev: true, // 在第一步中是否隐藏上一个按钮
hideNext: true, // 在最后一步中是否隐藏下一个按钮
exitOnOverlayClick: false, // 点击叠加层时是否退出介绍
showStepNumbers: false, // 是否显示红色圆圈的步骤编号
disableInteraction: true, // 是否禁用与突出显示的框内的元素的交互,就是禁止点击
showBullets: false, // 是否显示面板指示点
steps: [
{
element: '#step_one', // 元素
intro: '', // 引导框具体内容
position: 'top', // 引导框相对目标出现的位置 top right bottom left
}
], // 对应的数组,顺序出现每一步的引导提示
}).start();
三、driver.js
3.1、安装
yarn add driver.js
3.2、引入
import Driver from 'driver.js';
import 'driver.js/dist/driver.min.css';
3.3、使用
const driver = new Driver({
doneBtnText: '完成', // 结束按钮的文字
animate: true, // 动画
stageBackground: '#ffffff', // 突出显示元素的背景颜色
nextBtnText: '下一步', // 下一步按钮的文字
prevBtnText: '上一步', // 上一步按钮的文字
closeBtnText: '关闭' // 关闭按钮的文字
});
driver.defineSteps(steps);
driver.highlight({
element: "#some-element",
popover: {
title: "Title",
description: "Description"
}
});
driver.start();
3.2、API
driver.isActivated; // 检查驱动程序是否处于活动状态
driver.defineSteps([]); // 步骤列表
driver.start(stepNumber = 0); // 开始执行定义的步骤,stepNumber步骤索引
driver.moveNext(); // 跳转至下一步
driver.movePrevious(); // 跳转至上一步
driver.hasNextStep(); // 检查是否有下一步
driver.hasPreviousStep(); // 检查是否有上一步
// 阻止当前移动:若执行某些异步任务并手动移到下一步,则在“onNext”或“onPrevious”中很有用
driver.preventMove();
driver.highlight(string|stepDefinition); // 使用查询选择器或步骤定义突出显示元素
driver.refresh(); // 重新定位弹出窗口和突出显示的元素
driver.reset(); // 重置覆盖并清除屏幕
//当你想在一个驱动程序运行时运行另一个驱动程序实例时,可以传递一个布尔参数来立即清除,而不执行动画等操作
driver.reset(clearImmediately = false);
driver.hasHighlightedElement(); // 检查是否有突出显示的元素
const activeElement = driver.getHighlightedElement(); // 获取屏幕上当前突出显示的元素
const lastActiveElement = driver.getLastHighlightedElement(); // 获取最后一个突出显示的元素
activeElement.getCalculatedPosition(); // 获取活动元素的屏幕坐标
activeElement.hidePopover(); // 隐藏弹窗
activeElement.showPopover(); // 显示弹窗
activeElement.getNode(); // 获取此元素后面的DOM元素
3.5、属性
new Driver({
className: 'scoped-class', // 包装类名
animate: true, // 动画
opacity: 0.75, // 遮罩层不透明度(0表示仅弹出且不覆盖)
padding: 10, // 边距
stageBackground: '#FFFFFF', // 突出显示元素的背景颜色
allowClose: true, // 点击遮罩层是否关闭
overlayClickNext: false, // 移动到下一步的覆盖物点击
doneBtnText: 'Done', // 最后一个按钮上的文本
closeBtnText: 'Close', // “关闭”按钮上的文本
nextBtnText: 'Next', // “下一步”按钮上的文本
prevBtnText: 'Previous', // “上一步”按钮上的文本
showButtons: ['next', 'previous', 'close'], // 需要展示的按钮
keyboardControl: true, // 允许通过键盘进行控制(esc以关闭,箭头键移动)
scrollIntoViewOptions: {}, // scrollIntoView 属性
onHighlightStarted: (Element) {}, // 在元素即将突出显示时调用
onHighlighted: (Element) {}, // 当元素完全突出显示时调用
onDeselected: (Element) {}, // 取消选择元素时调用
onReset: (Element) {}, // 覆盖即将清除时调用
onNext: (Element) => {}, // 在任何步骤转到下一步时调用
onPrevious: (Element) => {}, // 在任何步骤转到上一步时调用
})
3.6、分步数组
steps: [
{
element: '#step_one', // 元素
popover: {
className: 'guide-navbar', // 类名
title: '', // 标题
description: '', // 描述信息
position: 'bottom', // 位置 top right bottom left
},
}
]
四、byte-guide
4.1、安装
yarn add byte-guide
4.2、引入
import Guide from 'byte-guide';
4.3、使用
<Guide
steps={[]}
localKey="uni-key"
/* customize styles */
hotspot
modalClassName="my-guide-modal"
maskClassName="my-guide-arrow"
/* customize callbacks */
onClose={() => {}}
beforeStepChange={(nextIndex, nextStep) => {}}
afterStepChange={(nextIndex, nextStep) => {}}
/* customize footers */
stepText={(stepIndex, stepCount) => `Step ${stepIndex} of ${stepCount}`}
nextText="Next"
prevText="Previous"
showPreviousBtn
okText="Finish"
/>
4.4、API
steps分步数组localKey唯一键,它将存储在 localStorage 中,表示指南是否已经结束expireDate过期日期closable支持在最后一步之前关闭closeEle自定义跳过指南元素modalClassName弹框类名maskClassName遮罩层类名mask是否展示遮罩层arrow是否展示箭头hotspot是否展示热点stepText步骤信息自定义文本nextText下一步按钮文本prevText上一步按钮文本showPreviousBtn是否展示上一步按钮okText最后一步按钮文本visible是否展示指南lang语言,支持 zh|en|jastep当前步骤数字afterStepChange切换步骤后的回调函数beforeStepChange切换步骤前的回调函数onClose关闭指南
4.5、步骤数组
steps: [
{
selector: '', // 当前步骤元素选择器
targetPos: {
top: 0,
left: 0,
width: 0,
height: 0,
}, // 若不希望弹框相对于锚元素显示,可传入 targetPos 对象来指示弹框相对于文档主体的位置
title: '', // 标题
content: '', // 主体内容
placement: '', // 位置 'top' | 'bottom' | 'left' | 'right' | 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' | 'left-top' | 'left-bottom' | 'right-top' | 'right-bottom'
offset: {
x: 0,
y: 0,
}, // 偏移量
parent: 'body',
visible: true, // 当前步骤是否展示
skip: false, // 是否应该跳过该步骤
beforeStepChange: (curStep: IStep, curStepIndex: number, steps: IStep[]) => void; // 修改前回调
afterStepChange: (curStep: IStep, curStepIndex: number, steps: IStep[]) => void; // 修改后回调
},
]
五、custom-guide
以上都是别人实现的插件,要想修改提示窗口跟交互就必须依赖对方提供的 API,那么自己如何实现呢,实际上自己实现也是非常简单的,只需要注意以下几个要点:
- 引导配置:弹窗定位/样式/内容/操作、遮罩层等
- 引导步骤:目标元素、提示信息、操作等
- 节点渲染:子节点渲染至除父组件以外 DOM 节点,比如 body
5.1、引导配置
根据项目/自身需求考虑好可提供的配置:
| 属性 | 含义 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| steps | 引导步骤 | array | 是 | -- |
| localKey | 本地缓存key | string | 否 | 'custom-guide-key' |
| visible | 引导页显示/隐藏 | bool | 否 | true |
| step | 初始步骤 | Number | 否 | 0 |
| closable | 是否支持跳过引导 | bool | 否 | true |
| arrow | 弹窗是否展示箭头 | bool | 否 | true |
| mask | 是否展示蒙层 | bool | 否 | true |
| modalClassName | 弹窗类名 | string | 否 | -- |
| maskClassName | 蒙层类名 | string | 否 | -- |
| prevText | 上一步按钮文案 | string | 否 | '上一步' |
| nextText | 下一步按钮文案 | string | 否 | '下一步' |
| okText | 确认按钮文案 | string | 否 | 我知道了 |
| prevStepChange | 点击上一步回调 | (index, step) => {} | 否 | -- |
| nextStepChange | 点击下一步回调 | (index, step) => {} | 否 | -- |
| onClose | 引导结束回调 | () => {} | 否 | -- |
- 弹窗定位:根据
placement位置不同,定位不同,默认bottom
{
top: top + height + offset.x,
left: left + width/2 - modalWidth/2 + offset.y
}
- 弹窗传送到 body/指定元素下,传送门
createPortal(modal, node);
- 遮罩层位置
border-width高亮目标元素
border-width: top, right, bottom, left;
5.2、引导步骤
| 属性 | 含义 | 类型 | 是否必填 | 默认值 |
|---|---|---|---|---|
| selector | 目标元素 | string | 是 | -- |
| title | 标题 | string | 是 | -- |
| content | 主体内容 | string, reactNode | 是 | -- |
| placement | 位置,如 bottom / left-top 等 | string | 否 | 'bottom' |
| offset | 偏移量 | object,{x, y} | 否 | -- |
| targetPosition | 弹窗目标定位主体,默认针对目标元素 | object, {top, left, width, height} | 否 | -- |
| skip | 是否跳过该步骤 | bool | 否 | false |
| prevBeforeChange | 点击上一步之前回调 | (curStep, curIndex, steps) => void; | 否 | -- |
5.3、节点渲染
在 react 中若希望 将子节点渲染至除父组件以外 DOM 节点,比如 body 下,可借助于 createPortal
createPortal(<div></div>, document.body)