背景
因业务需求做一个首次登录新手引导,查资料以后,由于没有合适的插件,于是自己做了一个
思路
主要参考了掘金上的一个文章思路,然后再加上自己的理解,然后根据业务的需求做的
另外 评论区的一个人建议加上了 阻止阴影区域点击
废话不多说上代码
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端有适配问题,需要做适配
- 还有是否判断是首次登录的问题