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

news2025/3/17 21:14:28

效果:

小程序实现导航漫游

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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2316815.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LLM论文笔记 25: Chain-of-Thought Reasoning without Prompting

Arxiv日期&#xff1a;2024.5.31机构&#xff1a;Google DeepMind 关键词 cot-decoding推理路径pretrain 核心结论 1. LLMs 不需要prompting就可以生成链式推理路径&#xff0c;prompting只是将这些能力显性化的一种手段 2. cot path 往往与更高的model confidence相关&…

新型XCSSET恶意软件利用增强混淆技术攻击macOS用户

微软威胁情报团队发现了一种新型的XCSSET变种&#xff0c;这是一种复杂的模块化macOS恶意软件&#xff0c;能够感染Xcode项目&#xff0c;并在开发者构建这些项目时执行。 这是自2022年以来的首个已知XCSSET变种&#xff0c;采用了增强的混淆方法、更新的持久化机制以及新的感…

C++初阶——类和对象(三) 构造函数、析构函数

C初阶——类和对象&#xff08;三&#xff09; 上期内容&#xff0c;我们围绕类对象模型的大小计算&#xff0c;成员存储方式&#xff0c;this指针&#xff0c;以及C实现栈和C语言的比较&#xff0c;进一步认识了C的封装特性。本期内容&#xff0c;我们开始介绍类的默认成员函…

【Function】使用托管身份调用Function App触发器,以增强安全性

推荐超级课程: 本地离线DeepSeek AI方案部署实战教程【完全版】Docker快速入门到精通Kubernetes入门到大师通关课AWS云服务快速入门实战目录 1. 背景介绍2. 设置3. 使用Web应用调用Function App触发器(Node.js示例)4. 执行结果此方法允许您使用托管身份(Managed Identity)调…

x012-MSP430F249智能步进电动百叶窗_proteus_光敏电阻_步进电机_仿真

https://www.dong-blog.fun/post/1997 46 、智能步进电动百叶窗 基本要求&#xff1a; 用一台步进电机控制百叶窗叶片的旋转&#xff08;正转/反转&#xff09; 用 LED 数码管显示旋转角度 设置按键&#xff1a; 手动/自动切换、手动正转和手动反转&#xff0c;停止/启动键 用一…

牛客周赛85 题解 Java ABCDEFG

A小紫的均势博弈 判断输入的 n 是奇数还是偶数 import java.io.*; import java.math.*; import java.util.*;public class Main {static IoScanner sc new IoScanner();static final int mod(int) (1e97);static void solve() throws IOException {int nsc.nextInt();if(n%2…

# RAG 框架 # 一文入门 全链路RAG系统构建与优化 —— 架构、策略与实践

本文全面阐述了RAG系统从数据收集、数据清洗&#xff08;包括领域专有名词处理&#xff09;、智能数据分块与QA对生成&#xff0c;到向量化、向量数据库选择与配置&#xff0c;再到检索方式及重排序&#xff0c;直至整合输出、监控反馈和安全保障的全流程。通过这一完整方案&am…

【Golang】第二弹-----变量、基本数据类型、标识符

笔上得来终觉浅,绝知此事要躬行 &#x1f525; 个人主页&#xff1a;星云爱编程 &#x1f525; 所属专栏&#xff1a;Golang &#x1f337;追光的人&#xff0c;终会万丈光芒 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 一、变量 1.1基本介绍…

linux系统CentOS 7版本搭建NFS共享存储

一、什么是NFS共享存储方式 NFS共享存储方式 是一种分布式文件系统协议&#xff0c;允许客户端通过网络访问远程服务器上的文件&#xff0c;就像访问本地文件一样。 二、 NFS的基本概念 &#xff08;1&#xff09;服务器端&#xff1a;提供共享存储的机器&#xff0c;负责导…

Matlab 基于SVPWM的VF三电平逆变器异步电机速度控制

1、内容简介 略 Matlab 167-基于SVPWM的VF三电平逆变器异步电机速度控制 可以交流、咨询、答疑 2、内容说明 略 3、仿真分析 略 4、参考论文 略

(一)微服务初见之 Spring Cloud 介绍

微服务架构简介 从单体应用架构发展到SOA架构&#xff0c;再到微服务架构&#xff0c;应用架构经历了多年的不断演进。微服务架构不是凭空产生的&#xff0c;而是技术发展的必然结果&#xff0c;分布式云平台的应用环境使得微服务代替单体应用成为互联网大型系统的架构选择。目…

架构思维:软件建模与架构设计的关键要点

文章目录 1. 软件建模的核心概念2. 七种常用UML图及其应用场景类图时序图组件图部署图用例图状态图活动图 3. 软件设计文档的三阶段结构4. 架构设计的关键实践1. 用例图&#xff1a;核心功能模块2. 部署图&#xff1a;架构演进阶段3. 技术挑战与解决方案4. 关键架构图示例5. 架…

【RNN神经网络】序列模型与RNN神经网络

前言 清库存。正式切入大模型后&#xff0c;打算把基础知识都梳理一遍&#xff0c;然后写了两篇就发现写不动了&#xff0c;后面就捡重要的记录。RNN知识仅此一篇记录&#xff0c;扫盲记录。 【自然语言处理】 &#xff08;Natural Language Processing&#xff0c;NLP&#xf…

Python文件管理

目录 一、文本文件读写 1、相关函数 2、读写文件 3、使用readline读取一行 4、读写文件的异常处理 5、添加内容 二、文本文件的编码 1、常见的编码 2、Python程序的编码 3、指定编码 三、文件的路径 1、相对路径 2、绝对路径 3、路径的改变 四、文件夹操作 五、…

vue3 前端路由权限控制与字典数据缓存实践(附Demo)

目录 前言1. 基本知识2. Demo3. 实战 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 从实战中出发&#xff1a; 1. 基本知识 Vue3 和 Java 通信时如何进行字典数据管理 需要了解字典数据的结构。通常&#x…

基于javaweb的SpringBoot精美物流管理系统设计与实现(源码+文档+部署讲解)

技术范围&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容&#xff1a;免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论…

DeepSeek进阶应用(二):结合Kimi制作PPT(双AI协作教程)

&#x1f31f;引言&#xff1a; DeepSeek作为国产AI大模型&#xff0c;以强大的逻辑推理和结构化内容生成能力著称&#xff0c;擅长根据用户需求生成PPT大纲或Markdown文本&#xff1b;Kimi的PPT助手则能解析结构化内容并套用模板快速生成美观的PPT&#xff0c;两者结合实现“内…

SpringBoot——Maven篇

Spring Boot 是一个用于快速开发基于 Spring 框架的应用程序的工具。它具有许多特性&#xff0c;其中一些重要的特性包括&#xff1a; 1. 自动配置&#xff1a;Spring Boot 提供了自动配置的机制&#xff0c;可以根据应用程序的依赖和环境自动配置应用程序的各种组件&#xff…

卷积神经网络(知识点)

一、为了使特征图变小&#xff1a; 由两种方法&#xff1a;1.增大步长&#xff1a;卷积的时候不是一次一步&#xff0c;而是一次多步&#xff0c;类似一张图片&#xff0c;在原来的像素基础上&#xff0c;每隔一个取一个像素点。 其中S就是步长 注意&#xff1a;扩大步长不经…

Vision Transformer (ViT):将Transformer带入计算机视觉的革命性尝试(代码实现)

Vision Transformer (ViT)&#xff1a;将Transformer带入计算机视觉的革命性尝试 作为一名深度学习研究者&#xff0c;如果你对自然语言处理&#xff08;NLP&#xff09;领域的Transformer架构了如指掌&#xff0c;那么你一定不会对它在序列建模中的强大能力感到陌生。然而&am…