原生微信小程序实现导航漫游(Tour)

server/2025/3/18 14:14:01/

效果:

小程序实现导航漫游

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.jsimport { 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博客


http://www.ppmy.cn/server/175978.html

相关文章

CAN总线的CC帧和FD帧之间如何仲裁

为满足CAN总线日益提高的带宽需求&#xff0c;博世公司于2012年推出CAN FD&#xff08;具有灵活数据速率的CAN&#xff09;标准&#xff0c;国际标准化组织&#xff08;ISO&#xff09;2015年通过ISO 11898-1:2015标准&#xff0c;正式将CAN FD纳入国际标准&#xff0c;以示区别…

Linux第六讲----git与gdb

1.创建仓库并上传代码 打开gitee,新建仓库 按下图操作&#xff1a;之后点创建 在点击橙色的克隆下载&#xff1a;并复制 之后登录xshell并进入家目录 输入git clone 右键粘贴 如下图所示 之后我们进入for_delete cd for_delete/ 创建一个文件&#xff0c;code.c,输入如下内…

2024年第十五届蓝桥杯软件C/C++大学A组——五子棋对弈

蓝桥杯原题&#xff1a; 题目描述&#xff1a; “在五子棋的对弈中&#xff0c;友谊的小船说翻就翻&#xff1f; ” 不&#xff01;对小蓝和小桥来说&#xff0c;五子棋不仅是棋盘上的较量&#xff0c;更是心与心之间的沟通。这两位挚友秉承着 “ 友谊第一&#xff0c;比赛第二…

WebSocket和长轮询

WebSocket 原理与实现 原理 WebSocket 是一种在单个TCP连接上进行全双工通信的协议。它允许服务器主动向客户端推送数据&#xff0c;而不需要客户端发起请求。一旦连接建立&#xff0c;客户端和服务器可以随时互相发送消息。 握手阶段&#xff1a;客户端通过HTTP请求升级到W…

告别数据库束缚!用esProc在 csv 文件上执行 SQL

esProc SPL 支持简单 SQL&#xff0c;可以直接在 csv 等结构化文本文件上执行 SQL 语句&#xff0c;这样&#xff0c;不用数据库也可以用 SQL 计算了。 先下载 esProc SPL&#xff1a;免费下载 不想折腾源代码的话&#xff0c;可以用标准版&#xff0c;找到相应版本下载后安装…

用 Vue 3.5 TypeScript 重新开发3年前甘特图的核心组件

回顾 3年前曾经用 Vue 2.0 开发了一个甘特图组件&#xff0c;如今3年过去了&#xff0c;计划使用Vue 3.5 TypeScript 把组件重新开发&#xff0c;有机会的话再开发一个React版本。 关于之前的组件以前文章 Vue 2.0 甘特图组件 下面录屏是是 用 Vue 3.5 TypeScript 开发的目前…

AGI大模型(8):提示词的安全与防护

1 前言 著名的「奶奶漏洞」&#xff0c;⽤套路把 AI 绕懵。 2 常⻅的提示词攻击技术 2.1 同类型⽬标劫持 同类⽬标劫持攻击&#xff0c;特别是在同类型任务的背景下&#xff0c;涉及到攻击者通过⾮法⼿段控制模型&#xff0c;并迫使其执行与原始任务性质相同但⽬标不同的操作…

Linux《进度条》

在之前的Linux基础开发工具当中我们已经了解了vim、gcc、makefile等基本的开发工具&#xff0c;那么有了这些开发工具我们就可以来实现我们Linux旅程当中的第一个程序——进度条。相信通过该项目的实现能让你对vim等开发工具更加的熟悉。一起加油吧&#xff01;&#xff01;&am…