React 前端导航

自己实现【前端引导页】功能的思路,当前好用第三方库有哪些

不使用第三方库实现【前端引导页】功能

引导页的本质就是化繁为简,将核心功简洁、明了的展示给用户,教会用户如何使用我们的产品,现在产品功能需求普遍更新迭代很快,难免需要引导用户快速了解产品的功能特性。

下面我们从两个方面来介绍【前端引导页】的实现:

  • 自己实现引导页功能,简单介绍实现思路
  • 介绍目前比较便捷的第三方库,快速实现

自己实现引导页功能

引导页核心功能其实就两点,后续代码以 Vue 作实现:

  • 高亮展示
  • 引导操作

这两点实现并不难,就是引导部分跟着高亮部分移动,添加一些简单的动画或过渡效果即可,也分为蒙层引导和无蒙层引导,这里介绍相对比较复杂的蒙层引导,这里简单的介绍两种实现思路。

使用 cloneNode + position + transition 实现

核心思路:

  • 高亮部分 通过 el.cloneNode(true) 复制对应目标元素节点,并将克隆节点添加到蒙层上
  • 通过 margin(或 tranlate、position 等)实现克隆节点的位置与目标节点重合
    引导部分 通过 position: fixed 实现定位效果,并通过动态修改 left、top 属性实现引导弹窗跟随目标移动
  • 过渡动画 通过 transition 实现位置的平滑移动
  • 页面位置/内容 发生变化时(如:resize、scroll 事件),需要重新计算位置信息

缺点:

  • 目标节点需要被深度复制
  • 不能实现边引导边操作

核心代码:

// 核心配置参数
const selectors = [
  {
    selector: "#btn1",
    message: "点此【新增】数据!",
  },
  {
    selector: "#btn2",
    message: "小心【删除】数据!",
  },
  {
    selector: "#btn3",
    message: "可通过此按钮【修改】数据!",
  },
  {
    selector: "#btn4",
    message: "一键【完成】所有操作!",
  },
];

// Guide.vue
<script setup>
import { computed, onMounted, ref } from "vue";

const props = defineProps({
  selectors: Array,
});

const guideModalRef = ref(null);
const guideBoxRef = ref(null);

const index = ref(0);
const show = ref(true);
let cloneNode = null;
let currNode = null;

let message = computed(() => {
  return props.selectors[index.value]?.message;
});

const genGuide = (hasChange = true) => {
  // 前置操作
  cloneNode && guideModalRef.value?.removeChild(cloneNode);

  // 所有指引完毕
  if (index.value > props.selectors.length - 1) {
    show.value = false;
    return;
  }

  // 获取目标节点信息
  currNode =
    currNode || document.querySelector(props.selectors[index.value].selector);
  const { x, y, width, height } = currNode.getBoundingClientRect();

  // 克隆节点
  cloneNode = hasChange ? currNode.cloneNode(true) : cloneNode;
  cloneNode.id = currNode.id + "_clone";
  cloneNode.style = `
  margin-left: ${x}px;
  margin-top: ${y}px;
  `;

  // 指引相关
  if (guideBoxRef.value) {
    const halfClientHeight = guideBoxRef.value.clientHeight / 2;
    guideBoxRef.value.style = `
   left:${x + width + 10}px;
   top:${y <= halfClientHeight ? y : y - halfClientHeight + height / 2}px;
  `;
    guideModalRef.value?.appendChild(cloneNode);
  }
};

// 页面内容发生变化时,重新计算位置
window.addEventListener("resize", () => genGuide(false));
window.addEventListener("scroll", () => genGuide(false));

// 上一步/下一步
const changeStep = (isPre) => {
  isPre ? index.value-- : index.value++;
  currNode = null;
  genGuide();
};

onMounted(() => {
  genGuide();
});
</script>

<template>
  <teleport to="body">
    <div v-if="show" ref="guideModalRef" class="guide-modal">
      <div ref="guideBoxRef" class="guide-box">
        <div>{{ message }}</div>
        <button class="btn" :disabled="index === 0" @click="changeStep(true)">
          上一步
        </button>
        <button class="btn" @click="changeStep(false)">下一步</button>
      </div>
    </div>
  </teleport>
</template>

<style scoped>
.guide-modal {
  position: fixed;
  z-index: 999;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.3);
}
.guide-box {
  width: 150px;
  min-height: 10px;
  border-radius: 5px;
  background-color: #fff;
  position: absolute;
  transition: 0.5s;
  padding: 10px;
  text-align: center;
}
.btn {
  margin: 20px 5px 5px 5px;
}
</style>

使用 z-index + position + transition 实现

核心思路:

  • 高亮部分 通过控制 z-index 的值,让目标元素展示在蒙层之上
  • 引导部分 通过 position: fixed 实现定位效果,并通过动态修改 left、top 属性实现引导弹窗跟随目标移动
  • 过渡动画 通过 transition 实现位置的平滑移动
  • 页面 位置/内容 发生变化时(如:resize、scroll 事件),需要重新计算位置信息

缺点:

  • 当目标元素的父元素 position: fixed | absolute | sticky 时,目标元素的 z-index 无法超过蒙版层(可参考 shepherd.js 的 svg 解决方案)

核心代码:

<script setup>
import { computed, onMounted, ref } from "vue";

const props = defineProps({
  selectors: Array,
});

const guideModalRef = ref(null);
const guideBoxRef = ref(null);

const index = ref(0);
const show = ref(true);
let preNode = null;

let message = computed(() => {
  return props.selectors[index.value]?.message;
});

const genGuide = (hasChange = true) => {
  // 所有指引完毕
  if (index.value > props.selectors.length - 1) {
    show.value = false;
    return;
  }

  // 修改上一个节点的 z-index
  if (preNode) preNode.style = `z-index: 0;`;

  // 获取目标节点信息
  const target =
    preNode = document.querySelector(props.selectors[index.value].selector);
  target.style = `
  position: relative; 
  z-index: 1000;
  `;
  const { x, y, width, height } = target.getBoundingClientRect();

  // 指引相关
  if (guideBoxRef.value) {
    const halfClientHeight = guideBoxRef.value.clientHeight / 2;
    guideBoxRef.value.style = `
   left:${x + width + 10}px;
   top:${y <= halfClientHeight ? y : y - halfClientHeight + height / 2}px;
  `;
  }
};

// 页面内容发生变化时,重新计算位置
window.addEventListener("resize", () => genGuide(false));
window.addEventListener("scroll", () => genGuide(false));

const changeStep = (isPre) => {
  isPre ? index.value-- : index.value++;
  genGuide();
};

onMounted(() => {
  genGuide();
});
</script>

<template>
  <teleport to="body">
    <div v-if="show" ref="guideModalRef" class="guide-modal">
      <div ref="guideBoxRef" class="guide-box">
        <div>{{ message }}</div>
        <button class="btn" :disabled="index === 0" @click="changeStep(true)">
          上一步
        </button>
        <button class="btn" @click="changeStep(false)">下一步</button>
      </div>
    </div>
  </teleport>
</template>

<style scoped>
.guide-modal {
  position: fixed;
  z-index: 999;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  background-color: rgba(0, 0, 0, 0.3);
}
.guide-box {
  width: 150px;
  min-height: 10px;
  border-radius: 5px;
  background-color: #fff;
  position: absolute;
  transition: 0.5s;
  padding: 10px;
  text-align: center;
}
.btn {
  margin: 20px 5px 5px 5px;
}
</style>

总结

以上就是一些简单实现,但还有很多细节需要考虑,比如:边引导边操作的实现、定位原因导致的图层展示问题等仍需要优化。

相信大部分人第一直觉是:直接使用第三方库实现功能就好了呀,自己实现功能不全、也未必好用,属实没有必要。

对于这一点其实在早前看到的一句话说的挺好:了解底层实现原理比使用库本身更有意义,当然每个人的想法不同,不过如果你想开始了解原理又不能立马挑战一些高深的内容,为什么不先从自己感兴趣的又不是那么复杂的功能开始呢?

扩展第三方引导库

  • driver.js
    driver.js 是一个强大而轻量级的普通 JavaScript 引擎,可在整个页面上驱动用户的注意力,只有 4kb 左右的体积,并且没有外部依赖,不仅高度可定制,还可以支持所有主流浏览器。

  • shepherd.js
    shepherd.js 包含的 API 众多,大多场景都可以通过其对应的配置得到,缺点就是整体的包体积较大,并且配置也比较复杂,配置复杂的内容一般都需要进行二次封装,将可变和不可变的配置项进行抽离,具体效果可见其 官方文档(https://shepherdjs.dev/)。

  • intro.js
    intro.js 是是一个开源的 vanilla Javascript/CSS 库,用于添加分步介绍或提示,大小在 10kB左右,属于轻量级的且无外部依赖,详情可见 官方文档(https://introjs.com/docs/)。

声明:本网站发布的内容(图片、视频和文字)以原创、转载和分享网络内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。邮箱:farmerlzj@163.com。 本站原创内容未经允许不得转载,或转载时需注明出处: 内容转载自: React前端网:https://qianduan.shop/blogs/detail/73

#vue#引导页

相关推荐

vue3 Composition APi详解

Componsition APi全面详解