新手引导-纯js手搓

背景

因业务需求做一个首次登录新手引导,查资料以后,由于没有合适的插件,于是自己做了一个

思路

主要参考了掘金上的一个文章思路,然后再加上自己的理解,然后根据业务的需求做的

参考链接 利用这个css属性,你也能轻松实现一个新手引导库

另外 评论区的一个人建议加上了 阻止阴影区域点击

废话不多说上代码

1. PC端

js 复制代码
 mounted() {
  if (!window.localStorage.getItem("isFirstLogin")) {
    this.creatIntro();
  }
},
 methods: {
  creatIntro() {
    // 初始化步骤
    this.step = 0;
    //获取目标元素 'menu_item_10031603'
    document.body.style.pointerEvents = "none";
    // 创建 高亮区域
    this.hightEl1 = document.createElement("div");
    this.circle1 = document.createElement("div");
    this.circle2 = document.createElement("div");
    this.circle3 = document.createElement("div");
    this.infoEl = document.createElement("div");
    this.creatHightElement(this.hightEl1, "menu_item_10031603", 1);
    // 按钮 点击
    this.creatOperateElement(this.infoEl, "menu_item_10031603");
    //小椭圆1
    this.creatElementToBody(
      this.circle1,
      "menu_item_10031603",
      30,
      20,
      10,
      -10
    );
    this.creatElementToBody(
      this.circle2,
      "menu_item_10031603",
      35,
      25,
      50,
      -30
    );
    this.creatElementToBody(
      this.circle3,
      "menu_item_10031603",
      60,
      35,
      100,
      -60
    );
    this.btnEl = document.getElementById("btnClick");
    this.btnEl.addEventListener("click", e => {
      console.log(e, this.step, "tap");
      switch (this.step) {
        case 0:
          this.introNext();
          break;
        case 1:
          this.introDone();
          break;
        case 2:
          this.removeAll();
          break;
        default:
          break;
      }
    });
  },
  creatElementToBody(element, id, width, height, offsetWidth, offsetHight) {
    //获取目标元素
    let targetElment = document.getElementById(id);
    const rect = targetElment.getBoundingClientRect();
    element.style.cssText = `
              position: absolute;
               z-index: 999999999;
  	        	background-color: #fff;
  	        	border-radius: 100%;
              width:${width}px;
              height:${height}px;
               border:2px solid #555
               
              `;
    document.body.appendChild(element);
    element.style.left =
      offsetWidth + rect.width + rect.left + window.pageXOffset + "px";
    element.style.top =
      rect.bottom + window.pageXOffset - rect.height + offsetHight + "px";
  },
  creatHightElement(element, id, multiple) {
    let targetElment = document.getElementById(id);
    // 创建 高亮区域
    const rect = targetElment.getBoundingClientRect();
    // document.body.style.pointerEvents = 'none'
    element.style.cssText = `
              position: absolute;
               box-shadow: 0 0 0 3000px rgba(0, 0, 0, 0.5);
              z-index: 99999998;
              border-radius: 5px;
              transition: all 0.3s ease-out;
              `;
    document.body.appendChild(element);
    element.style.left = rect.left + window.pageXOffset + "px";
    element.style.top = rect.top + window.pageYOffset + "px";
    element.style.width = rect.width + "px";
    element.style.height = rect.height * multiple + "px";
  },
  creatOperateElement(element, id) {
    let targetElment = document.getElementById(id);
    const rect = targetElment.getBoundingClientRect();
    element.style.cssText = `
              position: absolute;
               z-index: 99999999;
  	       	background-color: #fff;
  	       	border-radius: 100%;
            width:400px;
            height:150px;
            border:2px solid #555;
              `;
    document.body.appendChild(element);
    element.innerHTML = `
          <div id="contentOperate" style='width:350px;margin-top:50px;margin-left:25px'>
              <div>xxxxxxxxxxxxx</div>
          </div>
          <div>
              <button id='btnClick' style=' pointer-events:all;margin-left:300px;color:red;background-color:transparent;border:transparent' >下一步</button>
         	</div>
          `;
    element.style.left =
      160 + rect.width + rect.left + window.pageXOffset + "px";
    element.style.top =
      rect.bottom + window.pageXOffset - rect.height - 150 + "px";
  },
  moveCircle(element, id, offsetWidth, offsetHight) {
    let targetElment = document.getElementById(id);
    // 创建 高亮区域
    const rect = targetElment.getBoundingClientRect();
    element.style.left =
      offsetWidth + rect.width + rect.left + window.pageXOffset + "px";
    element.style.top =
      rect.bottom + window.pageXOffset - rect.height + offsetHight + "px";
  },
  moveOperate(element, id, offsetWidth, offsetHight, content) {
    let targetElment = document.getElementById(id);
    // 创建 高亮区域
    const rect = targetElment.getBoundingClientRect();
    let contentDiv = document.getElementById("contentOperate");
    contentDiv.innerHTML = content;
    element.style.left =
      offsetWidth + rect.width + rect.left + window.pageXOffset + "px";
    element.style.top =
      rect.bottom + window.pageXOffset - rect.height + offsetHight + "px";
  },
  introNext() {
    this.step = 1;
    this.moveCircle(this.circle1, "menu_item_10031603", 10, 10);
    this.moveCircle(this.circle2, "menu_item_10031603", 50, 30);
    this.moveCircle(this.circle3, "menu_item_10031603", 100, 60);
    this.moveOperate(
      this.infoEl,
      "menu_item_10031603",
      160,
      80,
      `
          <div style='width:350px;margin-top:50px;margin-left:25px'>
              <div>xxxxxxxx啦~</div>
          </div>
          `
    );
  },

  introDone() {
    this.step = 2;
    console.log(this.hightEl1, "this.hightEl1");
    document.body.removeChild(this.hightEl1);
    this.hightEl2 = document.createElement("div");
    this.creatHightElement(this.hightEl2, "menu_item_1031700", 4);
    this.moveCircle(this.circle1, "menu_item_1031700", 10, 80);
    this.moveCircle(this.circle2, "menu_item_1031700", 50, 100);
    this.moveCircle(this.circle3, "menu_item_1031700", 100, 120);
    this.moveOperate(
      this.infoEl,
      "menu_item_1031700",
      160,
      80,
      `
          <div style='width:350px;margin-top:50px;margin-left:25px'>
              <div>xxxxxxxxxx哦~</div>
          </div>
          `
    );
    this.btnEl.innerText = "完成";
  },
  removeAll() {
    document.body.removeChild(this.hightEl2);
    document.body.removeChild(this.circle1);
    document.body.removeChild(this.circle2);
    document.body.removeChild(this.circle3);
    document.body.removeChild(this.infoEl);
    document.body.style.pointerEvents = "all";
    window.localStorage.setItem("isFirstLogin", "true");
  }
},  

效果图就不上了

app端

js 复制代码
mounted () {
   if (!window.localStorage.getItem('isFirstLogin')) {
     this.initIntro()
   }
 },
  methods: {
   initIntro () {
     this.clientWidthRatio =
       (document.documentElement.clientWidth || document.body.clientWidth) /
       375
     this.step = 0
     this.hightElement1 = document.createElement('div')
     this.operateElement = document.createElement('div')
     this.triangleElement1 = document.createElement('div')
     document.body.style.pointerEvents = 'none'
     this.creatHightElement(this.hightElement1, 1)
     this.creatOperateElement(this.operateElement, 1)
     this.creatElementToBody(this.triangleElement1, 1, 'right', -6, 15)
     this.btnEl = document.getElementById('btnClick')
     this.btnEl.addEventListener('click', e => {
       console.log(e, this.step, 'tap')
       switch (this.step) {
         case 0:
           this.introNext()
           break
         case 1:
           this.introDone()
           break
         case 2:
           this.removeAll()
           break
         default:
           break
       }
     })
   },
   creatHightElement (element, num) {
     let targetElment = document.getElementById('application-wrapper-intro')
       .children[num]
     // 创建 高亮区域
     const rect = targetElment.getBoundingClientRect()
     // document.body.style.pointerEvents = 'none'
     element.style.cssText = `
               position: absolute;
                box-shadow: 0 0 0 3000px rgba(0, 0, 0, 0.5);
               z-index: 99999998;
               border-radius: 5px;
               transition: all 0.3s ease-out;
               `
     document.body.appendChild(element)
     element.style.left = rect.left + window.pageXOffset + 'px'
     element.style.top = rect.top + window.pageYOffset + 'px'
     element.style.width = rect.width + 'px'
     element.style.height = rect.height + 'px'
   },
   creatOperateElement (element, num) {
     let targetElment = document.getElementById('application-wrapper-intro')
       .children[num]
     const rect = targetElment.getBoundingClientRect()
     element.style.cssText = `
               position: absolute;
                z-index: 99999999;
   	       	background-color: #fff;
   	       	border-radius: 10px;
             width:120px;
             height:60px;
             border:2px solid #555;
               `
     document.body.appendChild(element)
     element.innerHTML = `
           <div id="contentOperate" style='width:100px;margin-top:10px;margin-left:6px'>
               <div>xxxxxx喽~</div>
           </div>
           <div>
               <button id='btnClick' style='pointer-events:all;margin-left:60px;color:red;background-color:transparent;border:transparent' >下一步</button>
          	</div>
           `
     element.style.left =
       10 + rect.width + rect.left + window.pageXOffset + 'px'
     element.style.top =
       rect.bottom + window.pageXOffset - rect.height - 10 + 'px'
   },
   creatElementToBody (element, num, arrow, offsetWidth, offsetHight) {
     //获取目标元素
     let targetElment = document.getElementById('application-wrapper-intro')
       .children[num]
     const rect = targetElment.getBoundingClientRect()
     element.style.cssText = `
               position: absolute;
                z-index: 999999999;
                 width: 0;
                 height: 0;
                 border: 12px solid;
                 border-color:${
                   arrow === 'right'
                     ? 'transparent  #fff transparent transparent '
                     : 'transparent transparent  #fff transparent'
                 };

               `
     document.body.appendChild(element)
     element.style.left =
       offsetWidth * this.clientWidthRatio +
       rect.width +
       rect.left +
       window.pageXOffset +
       'px'
     element.style.top =
       rect.bottom +
       window.pageXOffset -
       rect.height +
       offsetHight * this.clientWidthRatio +
       'px'
   },
   moveOperate (element, num, offsetWidth, offsetHight, content) {
     let targetElment = document.getElementById('application-wrapper-intro')
       .children[num]
     // 创建 高亮区域
     const rect = targetElment.getBoundingClientRect()
     let contentDiv = document.getElementById('contentOperate')
     contentDiv.innerHTML = content
     element.style.left =
       this.clientWidthRatio * offsetWidth +
       rect.width +
       rect.left +
       window.pageXOffset +
       'px'
     element.style.top =
       rect.bottom +
       window.pageXOffset -
       rect.height +
       offsetHight * this.clientWidthRatio +
       'px'
   },
   introNext () {
     this.step = 1
     document.body.removeChild(this.triangleElement1)
     this.triangleElement2 = document.createElement('div')
     this.creatElementToBody(this.triangleElement2, 1, 'top', -40, 70)
     this.moveOperate(
       this.operateElement,
       1,
       -60,
       90,
       `
           <div style='width:100px;margin-top:10px;margin-left:10px'>
               <div>xxxxxxxxx哦~</div>
           </div>
           `
     )
   },
   introDone () {
     this.step = 2
     document.body.removeChild(this.hightElement1)
     document.body.removeChild(this.triangleElement2)
     this.triangleElement3 = document.createElement('div')
     this.creatElementToBody(this.triangleElement3, 1, 'top', -40, 146)
     this.hightElement2 = document.createElement('div')
     this.creatHightElement(this.hightElement2, 5)
     this.moveOperate(
       this.operateElement,
       1,
       -60,
       166,
       `
           <div style='width:100px;margin-top:10px;margin-left:10px'>
               <div>xxxxx咯~</div>
           </div>
           `
     )
     this.btnEl.innerText = '完成'
   },
   removeAll () {
     document.body.removeChild(this.triangleElement3)
     document.body.removeChild(this.hightElement2)
     document.body.removeChild(this.operateElement)
     document.body.style.pointerEvents = 'all'
     window.localStorage.setItem('isFirstLogin', 'true')
   }
 }

需要注意

  • app端有适配问题,需要做适配
  • 还有是否判断是首次登录的问题

有更好的建议可以在评论区提

相关推荐
阿虎儿25 分钟前
MCP
前端
毕小宝38 分钟前
编写一个网页版的音频播放器,AI 加持,So easy!
前端·javascript
万水千山走遍TML38 分钟前
JavaScript性能优化
开发语言·前端·javascript·性能优化·js·js性能
Aphasia31138 分钟前
react必备JS知识点(一)——判断this指向👆🏻
前端·javascript·react.js
会飞的鱼先生1 小时前
vue3中slot(插槽)的详细使用
前端·javascript·vue.js
小小小小宇1 小时前
一文搞定CSS Grid布局
前端
0xHashlet1 小时前
Dapp实战案例002:从零部署链上计数器合约并实现前端交互
前端
知心宝贝1 小时前
🔍 从简单到复杂:JavaScript 事件处理的全方位解读
前端·javascript·面试
安余生大大1 小时前
关于Safari浏览器在ios<16.3版本不支持正则表达式零宽断言的解决办法
前端
前端涂涂1 小时前
express查看文件上传报文,处理文件上传,以及formidable包的使用
前端·后端