页面中的新人引导工具

410 阅读7分钟

经常在网站上会看到首次浏览网页/发布新版本后会出现引导功能,指引操作功能,大概率是通过元素定位实现的,都有哪些成熟的库,如何自定义相关组件,需要注意哪几点?

一、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>

vueTour.png

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;

introjs.png

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();

driverjs.png

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"
/>

byteGuide.png

4.4、API
  • steps 分步数组
  • localKey 唯一键,它将存储在 localStorage 中,表示指南是否已经结束
  • expireDate 过期日期
  • closable 支持在最后一步之前关闭
  • closeEle 自定义跳过指南元素
  • modalClassName 弹框类名
  • maskClassName 遮罩层类名
  • mask 是否展示遮罩层
  • arrow 是否展示箭头
  • hotspot 是否展示热点
  • stepText 步骤信息自定义文本
  • nextText 下一步按钮文本
  • prevText 上一步按钮文本
  • showPreviousBtn 是否展示上一步按钮
  • okText 最后一步按钮文本
  • visible 是否展示指南
  • lang 语言,支持 zh|en|ja
  • step 当前步骤数字
  • 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本地缓存keystring'custom-guide-key'
visible引导页显示/隐藏booltrue
step初始步骤Number0
closable是否支持跳过引导booltrue
arrow弹窗是否展示箭头booltrue
mask是否展示蒙层booltrue
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;

image.png

5.2、引导步骤
属性含义类型是否必填默认值
selector目标元素string--
title标题string--
content主体内容string, reactNode--
placement位置,如 bottom / left-topstring'bottom'
offset偏移量object,{x, y}--
targetPosition弹窗目标定位主体,默认针对目标元素object, {top, left, width, height}--
skip是否跳过该步骤boolfalse
prevBeforeChange点击上一步之前回调(curStep, curIndex, steps) => void;--
5.3、节点渲染

在 react 中若希望 将子节点渲染至除父组件以外 DOM 节点,比如 body 下,可借助于 createPortal

createPortal(<div></div>, document.body)