前言
实现效果:
由引导线指向位置,框内自定义内容及下一步按钮。点击下一步自动指向下一提示位置,可以设置提示框上下左右位置。
实现思路:
通过ref确定指向位置,使用slot插槽自定义提示内容,修改样式调整提示框位置。
效果图
使用主要方法&遇到问题
问题:目标元素超出可视窗口时显示问题
// 指定dom元素滚动到可视窗口
const scrollDom = (targetDom) => {
console.log('指定dom元素滚动到可视窗口',targetDom);
targetDom?.scrollIntoView({ block: 'nearest' })
// 通过scrollIntoView方法滚动到可视窗口中间(block的值:start、center、end、nearest)
}
问题:tour组件出现后,页面还可以滑动
目标:tour出现后,固定页面
解决:tour出现后设置页面不可滚动,tour结束后设置页面可以滚动
// 禁止页面滚动:(initStepsListFn)
document.body.style.overflow='hidden'
// 允许页面滚动:(closeFn)
document.body.style.overflow='visible'
问题:tour出现后页面不可点击
// 禁止页面点击事件
let getId = document.getElementById('items');
getId.style.pointerEvents='none';
// 允许页面点击事件
let getId = document.getElementById('items');
getId.style.pointerEvents='auto'
问题:ref获取不到dom问题:
情况1:ref获取dom为undefined
情况2:ref获取dom不为undefined,但没有拿到dom
<div ref="stepRef1" >测试tour</div>
<TourGuidelines :stepsList="stepsList" :key="stepsList" :show="show" >
<script setup>
let stepRef1 = ref(null);
let stepsList = ref([]);
const initStepsListFn = ()=>{
stepsList.value = [
{
step: 1,
content: '测试Tour',
target: stepRef1.value,
},
]
}
// 在onMounted中进行赋值:在dom渲染完成后进行赋值ref,否则拿不到dom元素
onMounted(() => {
initStepsListFn()
// 如果ref组件中存在if会导致获取不到ref,使用nextTick保证dom更新完毕后再执行
// nextTick(() => initStepsListFn())
})
</script>
// 普通标签可以直接获取dom节点元素
// 组件不能直接获取到dom节点元素,需要在$el中拿到,再从对应children方法中找到具体标签
<van-field label="绑定ref" placeholder="请选择" ref="stepRef1" />
<TourGuidelines :stepsList="stepsList" :key="stepsList" :show="show" >
<script setup>
let stepRef1 = ref(null);
let stepsList = ref([]);
const initStepsListFn = ()=>{
stepsList.value = [
{
step: 1,
content: '测试Tour',
target: stepRef1.value?.$el?.children?.[1]
},
]
}
// 在onMounted中进行赋值:在dom渲染完成后进行赋值ref,否则拿不到dom元素
onMounted(() => {
nextTick(() => initStepsListFn())
})
</script>
问题:vue3中ref绑定自定义组件没有获取到dom
解决方法:使用vue3的defineExpose将子组件的属性暴露出去
# 子组件代码片段
<div ref="childrenDom">子组件</div>
<script setup>
import {ref} from "vue";
const childrenDom = ref();
defineExpose({
childrenDom
});
</script>
#父组件代码片段
<Children ref="child"/>
<script setup>
import Children from "./Children.vue";
const child = ref(null);
onMounted(() => {
console.log(child.value)
})
</script>
问题:在子组件中暴露ref dom元素时,父组件依旧拿不到
原因:ref写在了v-if 中
解决:在外层写一个div,将ref下载外层div中
#父组件代码片段
<div ref="child">
<Children/>
</div>
<script setup>
const child = ref(null);
onMounted(() => {
console.log(child.value)
})
</script>
问题:绑定的ref在v-if中时,无法获取到dom
原因:ref获取dom时,在v-if中当为false时,此时dom未被渲染,无法获取到该元素
解决方法:在v-if标签外层添加一个div标签,并给div标签绑定ref即可。获取子组件dom必须在dom渲染后才可以获取到
问题:组件在手机上不显示背景色
原因:css设置颜色为十六进制颜色,故不显示
<template>
<div ref="testTopRef">测试0</div>
<van-row style="margin-top: 10rem;">
<van-col span="6">
<div ref="testBottomRef">
<div>测试1</div>
</div>
</van-col>
<van-col span="6" >
<div class="fonts1" ref="testBottomRef2">测试2</div>
</van-col>
<van-col span="6">
<div ref="testBottomRef3">测试3</div>
</van-col>
<van-col span="6">
<div class="fonts1" ref="testBottomRef4">测试4</div>
</van-col>
<van-col span="6" style="padding-top:10rem">
<div ref="testScrollRef"> 测试滚动</div>
</van-col>
<van-col span="24" style="padding-top:10rem">
<div > 底部</div>
</van-col>
</van-row>
<TourGuidelines :stepsList="stepsList" :key="stepsList" :show="show" @change="changeFn" @close="closeFn">
<!-- <div>
<span style="color: aqua;">插槽-公共内容</span>
</div> -->
</TourGuidelines>
</template>
<script setup>
import { nextTick,onMounted, ref } from 'vue';
import TourGuidelines from '../../components/TourGuidelines/index.vue';
// let show = ref(true)
let show = ref(!localStorage.getItem('showTour')) // 显示tour
let testScrollRef = ref(null)
let testTopRef = ref(null)
let testBottomRef = ref(null)
let testBottomRef3 = ref(null)
const stepsList = ref([])
const changeFn = (value) => {
console.log('change', value);
}
const closeFn = (value) => {
console.log('closeFn', value);
show.value = value;
// 允许页面滚动:
document.body.style.overflow='visible'
// 不显示tour - 当仅显示一次时需要本地存储
localStorage.setItem('showTour',true);
}
// tour初始赋值
const initStepsListFn = ()=>{
// 禁止页面滚动:
document.body.style.overflow='hidden';
nextTick(()=>{
stepsList.value = [
{
step: 1,
content: '文字内容',
buttonContent: '下一步?',
target: testTopRef.value,
location: 'bottom',
cover: (
`<div style="color:blue;">啊
<div>换行</div>
<div> <span style="color:red;">自定义内容 - 标签</span></div>
嗷</div>`
),
},
{
step: 2,
content: '222222',
target: testBottomRef.value,
location: 'top',
cover: '自定义内容 - 純字符串left',
lineLocation: 'right',
},
{
step: 3,
content: '333333333',
buttonContent: '下一步',
target: testBottomRef3.value,
lineLocation: 'left',
cover: (
`<span style="color:red;">自定义内容-模板字符串right</span>`
),
},
{
step: 4,
content: '测试滚动',
buttonContent: '知道了',
target: testScrollRef.value,
location: 'top',
},
]
})
}
onMounted(() => {
show.value&&initStepsListFn()
})
</script>
使用中bug
适配问题:Android无问题,在ios系统中个别Tour组件是出现位置不准确问题
Android ios
原因:当target值为ref获取值下的子节点时,在ios系统中会发生位置错乱,如下图stepRef2.value?.[0]?.$el会发生错乱
ios版本低的不兼容,版本高的不会发生错乱