对于一些复杂的页面,往往可以添加功能引导蒙层帮助用户快速上手功能。如下图:
显然,我们需要获取到操作按钮在页面当中的位置坐标及大小,然后在对应位置上方设置一个透明可视区域,这个可以利用css box-shadow属性来创建阴影蒙层。接着,对于有多个步骤引导的,在切换下一个时为了不那么生硬,需要添加动画过渡效果,这里可以借助微信小程序的animate API实现。
一个功能页当中的代码,应该尽可能只保留该页面功能业务相关逻辑代码,对于用户引导这种与业务无关的代码应该抽离到外部组件当中,下面就来带大家如何封装实现。
可以看到,一个提示引导有两部分组成:操作区域+操作提示。通常设计,操作提示往往是一张图片素材,但是这里为了简化代码,改为纯文字描述,在理解了原理之后大家可以自己在组件增加属性配置即可。
在src->components目录下,新建user-guide.vue组件,组件代码如下:
<template>
<view v-if="show" class="user-guide">
<view id="visual-view" class="visual-view">
<view class="tip">{{ currentTip.tip }}</view>
</view>
<view class="btn-list">
<button class="btn" @click="close">知道了</button>
<button v-if="!isEnd" class="btn next" @click="moveView">下一步</button>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, getCurrentInstance, onUpdated } from 'vue'
interface IProps {
/** 是否显示 */
show: boolean
/** 提示信息列表 */
list: TipItem[]
}
interface TipItem {
/** 操作区域位置坐标及大小 */
width: number
height: number
top: number
left: number
/** 操作提示内容 */
tip: string
}
const props = defineProps<IProps>()
const emit = defineEmits(['update:show'])
const step = ref(0)
const isEnd = computed(() => step.value === props.list.length - 1)
let isFirstUpdate = false
onUpdated(() => {
if (!isFirstUpdate) {
// 初始化第一个提示
currentTip.value = props.list[0]
isFirstUpdate = true
}
})
// 关闭提示
function close() {
emit('update:show', false)
}
// @ts-ignore
const { ctx } = getCurrentInstance()
// 给个初始值,否则template中渲染对象报错
const currentTip = ref<TipItem>({
width: 0,
height: 0,
top: 0,
left: 0,
tip: '',
})
// 切换下一个
function moveView() {
const preTip = currentTip.value
step.value += 1
currentTip.value = props.list[step.value]
const nextTip = currentTip.value
ctx.$scope.animate(
'#visual-view',
[
{
top: `${preTip.top}px`,
left: `${preTip.left}px`,
width: `${preTip.width}px`,
height: `${preTip.height}px`,
},
{
top: `${nextTip.top}px`,
left: `${nextTip.left}px`,
width: `${nextTip.width}px`,
height: `${nextTip.height}px`,
},
],
300, // 动画过渡时间
() => {
// 调用 animate API 后会在节点上新增一些样式属性覆盖掉原有的对应样式,在动画结束后需要清除增加的属性
ctx.$scope.clearAnimation()
},
)
}
</script>
<style lang="scss">
.user-guide {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9;
.visual-view {
position: absolute;
width: 200rpx;
height: 100rpx;
background: transparent;
border-radius: 10px;
box-shadow: 0 0 0 1999px rgba(0, 0, 0, 0.55);
z-index: 10;
.tip {
position: absolute;
z-index: 11;
bottom: -30px;
color: #fff;
}
}
.guide-btn {
position: absolute;
z-index: 11;
bottom: 200px;
}
}
.btn-list {
position: fixed;
bottom: 100px;
display: flex;
width: 100%;
justify-content: center;
z-index: 11;
.btn {
margin: 0;
&.next {
color: #5080ff;
margin-left: 10px;
}
}
}
</style>
需要说明的是,vue3 compositon写法中是没有this写法的,如果我们要获取挂载在this上面的API,可以通过
import { getCurrentInstance } from 'vue'
const { ctx } = getCurrentInstance()
拿到。而uniapp 小程序端this属性或方法是挂载到ctx下的$scope属性上,所以moveView方法中才有了如下写法:
ctx.$scope.animate()
<template>
<view id="tip-one" >点击区域一</view>
<view id="tip-two" >点击区域二</view>
<view id="tip-three" >点击区域三</view>
<UserGuide v-model:show="showGuide" :list="guideList" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import UserGuide from '@/components/user-guide.vue'
interface TipItem {
/** 操作区域位置坐标及大小 */
width: number
height: number
top: number
left: number
/** 操作提示内容 */
tip: string
}
const showGuide = ref(true)
const guideList = ref<TipItem[]>([])
// 假设我们页面中需要提示三个操作区域,提示内容如下:
const guideTips = ['点击这里赚积分', '点击这里得礼品', '点击分享赚...']
// 获取点击区域在页面中的坐标信息
const query = uni.createSelectorQuery()
query.select('#tip-one').boundingClientRect()
query.select('#tip-two').boundingClientRect()
query.select('#tip-three').boundingClientRect()
query.exec((res) => {
guideList.value = res.map((item: TipItem, index: number) => {
item.tip = guideTips[index]
return item
})
})
</script>
值得一提的是,在vue3中,已经移除了.sync修饰符,改为v-model:propName写法,如上面代码中v-model:show
本文实现了用户指引蒙层组件的封装,介绍了uniapp中如何使用微信小程序挂载在this下的动画方法api及如何获取页面元素坐标位置信息大小,大家理解原理后可以根据自己的需求改造增加组件配置属性即可。
阅读量:2026
点赞量:0
收藏量:0