原生微信小程序实现导航漫游(Tour)
效果:
小程序实现导航漫游
1、组件
miniprogram/components/tour/index.wxml
<!--wxml-->
<view class="guide" wx:if="{{showGuide}}">
<view style="{{guideStyle}}" class="guide-box">
<view class="tips guide-step-tips" style="{{tipPosition}}">
<view class="text">{{ guideList[index].tips }}</view>
<view class="step-indicator">
<text class="step-text">{{index + 1}}/{{guideList.length}}</text>
</view>
<view class="tool-btn">
<text bind:tap="skip">跳过</text>
<view class="btn-group">
<view class="prev" bind:tap="prev" wx:if="{{index > 0}}">上一步</view>
<view class="next" bind:tap="next">{{index === guideList.length - 1 ? '完成' : '下一步'}}</view>
</view>
</view>
</view>
<view class="arrow" style="{{arrowTop}}"></view>
</view>
<!-- 遮罩层,防止点击 -->
<view class="v-model"></view>
</view>
miniprogram/components/tour/index.ts
// components/xky-guideStep/xky-guideStep.js
import { handleChangeTourType } from "@/utils/util";
// 添加必要的接口定义
interface GuideItem {
el: string;
width: number;
height: number;
left: number;
top: number;
style?: string;
}
interface Step {
name: string;
guideList: GuideItem[];
}
interface DomInfo {
width: number;
height: number;
left: number;
top: number;
[key: string]: any;
}
Component({
/**
* 组件的属性列表
*/
properties: {
step: {
type: Object,
value: {} as Step,
},
},
/**
* 组件的初始数据
*/
data: {
stepName: "step", //该提示步骤的名称,用于不在重复展示
guideList: [] as GuideItem[],
index: 0, // 当前展示的索引
showGuide: true, // 是否显示引导
guideStyle: "", // 默认样式
arrowTop: "", //步骤提示三角形的定位
tipPosition: "", //步骤提示的定位
systemInfo: null as WechatMiniprogram.SystemInfo | null, //屏幕宽度高度等信息
tipWidth: 200, //步骤提示默认的宽度
},
/**
* 组件的方法列表
*/
methods: {
// 展示新手提示
viewTips(data: DomInfo | null, scrollTop: number) {
let {
systemInfo,
tipWidth,
index,
guideList,
arrowTop,
tipPosition,
guideStyle,
} = this.data;
if (data && systemInfo) {
// 如果dom宽度大于或者等于窗口宽度,需要重新调整dom展示宽度
let newWidth = systemInfo.windowWidth - 20;
if (data.width >= newWidth) {
data.width = newWidth;
}
// 如果距离左边为0,自动增加一点左边距
if (data.left == 0) {
data.left = 10;
}
let domRW = systemInfo.windowWidth - data.left;
let left = 0;
// 如果dom距离右边没有tips的宽度大的话,就要让tips向左便宜
if (domRW < tipWidth) {
left = domRW - tipWidth - 30;
}
// const index = index;
// 步骤条展示的高度需要加上屏幕滚动的高度
data.top += scrollTop;
// 根据实际情况需要滚动到展示区域
wx.pageScrollTo({
scrollTop: data.top > 20 ? data.top - 20 : 0,
duration: 100,
});
let obj = Object.assign(guideList[index], data);
// 设置三角形高度
let arrArrowTop = data.height + 9;
arrowTop = "top:" + arrArrowTop + "px;";
// 设置提示框定位
tipPosition = "top:" + (arrArrowTop + 5) + "px;left:" + left + "px;";
// 重新设置guideList的值
guideList.splice(index, 1, obj);
guideStyle = this.getStyle();
this.setData({
arrowTop,
tipPosition,
guideList,
guideStyle,
});
} else {
index += 1;
this.setData({
index,
});
this.getDomInfo();
}
},
// 获取步骤提示的主要样式
getStyle() {
const { guideList, index } = this.data;
const { width, height, left, top, style } = guideList[index];
let newStyle = "width:" + width + "px;";
newStyle += "height:" + height + "px;";
newStyle += "left:" + left + "px;";
newStyle += "top:" + top + "px;";
newStyle +=
"box-shadow: rgb(33 33 33 / 80%) 0px 0px 0px 0px, rgb(33 33 33 / 50%) 0px 0px 0px 5000px;";
newStyle += style;
return newStyle;
},
// 获取dom信息
getDomInfo() {
const { guideList, index } = this.data;
const { el } = guideList[index];
const query = wx.createSelectorQuery();
setTimeout(() => {
query.select(el).boundingClientRect();
query.selectViewport().scrollOffset();
query.exec( (res)=> {
let data = res[0]; // #the-id节点的上边界坐标
let scrollTop = res[1].scrollTop; // 显示区域的竖直滚动位置
this.viewTips(data, scrollTop);
});
}, 10);
},
updateTourMap() {
handleChangeTourType(this.data.stepName)
},
skip() {
this.setData({
showGuide: false,
});
this.updateTourMap()
},
// 添加上一步方法
prev() {
let { index } = this.data;
if (index > 0) {
index -= 1;
this.setData({
index
});
this.getDomInfo();
}
},
// 下一步,修改显示文案
next() {
let { index, guideList } = this.data;
if (index === guideList.length - 1) {
this.setData({
showGuide: false,
});
this.updateTourMap()
} else {
index += 1;
this.setData({
index,
});
this.getDomInfo();
}
},
},
lifetimes: {
attached () {
const { step } = this.properties;
let { guideList, stepName } = this.data;
guideList = step.guideList;
stepName = step.name;
this.setData({
guideList,
stepName,
systemInfo: wx.getSystemInfoSync(),
});
const guide = wx.getStorageSync('tourMap');
if (!guide || !guide[step.name]) {
this.getDomInfo();
} else {
this.setData({
showGuide: false,
});
}
},
detached () {
// 在组件实例被从页面节点树移除时执行
},
},
});
miniprogram/components/tour/index.scss
/* wxss */
.v-model {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 1000;
}
.guide {
z-index: 1001;
}
.guide-box {
position: absolute;
z-index: 10001;
transition: all 0.2s;
}
.guide-box::before {
content: '';
height: 100%;
width: 100%;
border: 1px dashed #fff;
border-radius: 8rpx;
position: absolute;
top: -8rpx;
left: -8rpx;
padding: 7rpx;
}
.arrow {
height: 20rpx;
width: 20rpx;
background: #1cbbb4;
position: absolute;
top: 144rpx;
left: 45%;
transform: rotate(45deg);
}
.tips {
width: 400rpx;
background: linear-gradient(180deg, #1cbbb4, #0081ff);
box-shadow: 0px 2px 9px 0px rgba(0, 0, 0, 0.1);
color: #fff;
position: absolute;
top: 152rpx;
left: -50%;
padding: 15rpx 20rpx;
font-size: 28rpx;
border-radius: 12rpx;
}
.tool-btn {
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 0rpx;
margin-top: 20rpx;
}
/* 新增样式 */
.step-indicator {
display: flex;
justify-content: flex-end;
margin-top: 10rpx;
}
.step-text {
font-size: 24rpx;
color: rgba(255, 255, 255, 0.8);
}
.btn-group {
display: flex;
align-items: center;
}
.prev {
background: rgba(255, 255, 255, 0.2);
height: 48rpx;
width: 100rpx;
text-align: center;
border-radius: 8rpx;
color: #fff;
line-height: 48rpx;
font-size: 24rpx;
margin-right: 10rpx;
}
.next {
background: #fff;
height: 48rpx;
width: 100rpx;
text-align: center;
border-radius: 8rpx;
color: #666;
line-height: 48rpx;
font-size: 24rpx
}
miniprogram/components/tour/index.json
{
"component": true,
"usingComponents": {}
}
miniprogram/utils/util.ts
/** 修改本地的Tour状态 */
export const handleChangeTourType = (tourName: string) => {
const tourMap = wx.getStorageSync('tourMap') || {};
tourMap[tourName] = true;
wx.setStorageSync('tourMap', tourMap);
};
部分转载:微信小程序首次进入引导提示自定义组件-CSDN博客