更新记录

1.0.0(2025-11-17) 下载此版本


平台兼容性

uni-app(3.6.14)

Vue2 Vue2插件版本 Vue3 Vue2插件版本 Chrome Chrome插件版本 Safari Safari插件版本 app-vue app-vue插件版本 app-nvue app-nvue插件版本 Android Android插件版本 iOS iOS插件版本 鸿蒙 鸿蒙插件版本
1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 5.0 1.0.0 12 1.0.0 1.0.0
微信小程序 微信小程序插件版本 支付宝小程序 支付宝小程序插件版本 抖音小程序 抖音小程序插件版本 百度小程序 百度小程序插件版本 快手小程序 快手小程序插件版本 京东小程序 京东小程序插件版本 鸿蒙元服务 鸿蒙元服务插件版本 QQ小程序 QQ小程序插件版本 飞书小程序 飞书小程序插件版本 快应用-华为 快应用-华为插件版本 快应用-联盟 快应用-联盟插件版本
1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 1.0.0 1.0.0

uni-app x(3.6.14)

Chrome Safari Android iOS 鸿蒙 微信小程序
- - - - - -

其他

多语言 暗黑模式 宽屏模式

hy-interactive-tour

🎯 应用内引导 + 高亮遮罩 + 聚焦指示器插件

让用户轻松上手你的应用,就像 App Store 的应用引导一样!

✨ 特性

🌈 核心功能

  • 跨平台支持:完美兼容 H5、小程序(微信/支付宝/抖音/百度/QQ)、App(iOS/Android)、uni-app x
  • 智能高亮:支持矩形、圆形、圆角矩形三种高亮形状,自动适配元素大小
  • 自动滚动:目标元素自动滚动到可视区域,支持平滑滚动动画
  • 智能定位:提示框自动计算最佳位置,避免超出视口边界

🎭 动画效果

  • 手势动画:支持点击、滑动(上下左右四个方向)、脉冲等多种手势动画
  • 平滑过渡:步骤切换时的流畅动画效果
  • Canvas 2D:小程序端支持新版 Canvas 2D API,性能更优,自动降级兼容旧版

🔧 开发友好

  • TypeScript:完整的类型定义支持,智能提示
  • 零依赖:无需额外依赖,开箱即用
  • 灵活配置:支持动态步骤、自定义内容、事件回调等

🚀 快速开始

第一步:引入组件

选项1:全局注册(推荐)

// main.js
import HyTour from '@/uni_modules/hy-interactive-tour/index.js'
Vue.use(HyTour)

选项2:页面内引入

<script>
import HyTour from '@/uni_modules/hy-interactive-tour/components/hy-interactive-tour/hy-tour.vue'

export default {
  components: {
    HyTour
  }
}
</script>

第二步:定义引导步骤

data() {
  return {
    showTour: false,
    tourSteps: [
      {
        selector: '#btn-new',        // 目标元素的 ID 或 class
        content: '点击这里创建新内容', // 提示文字
        shape: 'round',              // 高亮形状:round(圆角矩形) / rect(矩形) / circle(圆形)
        position: 'bottom',          // 提示框位置:bottom / top / left / right
        padding: 10,                 // 高亮区域的内边距
        showGesture: true,           // 显示手势动画
        gestureType: 'tap'           // 手势类型:tap(点击) / swipe(滑动) / default(脉冲)
      },
      {
        selector: '#btn-search',
        content: '这里可以搜索内容',
        shape: 'circle',
        position: 'bottom'
      }
    ]
  }
}

第三步:在模板中使用

Vue 2 语法:

<template>
  <view>
    <!-- 你的页面内容 -->
    <button id="btn-new">新建</button>
    <button id="btn-search">搜索</button>

    <!-- 引导组件(Vue 2 使用 .sync) -->
    <hy-tour
      :visible.sync="showTour"
      :steps="tourSteps"
      :mask-closable="true"
      @finish="onTourFinish"
      @skip="onTourSkip"
    />
  </view>
</template>

Vue 3 语法:

<template>
  <view>
    <!-- 你的页面内容 -->
    <button id="btn-new">新建</button>
    <button id="btn-search">搜索</button>

    <!-- 引导组件(Vue 3 使用 v-model:visible) -->
    <hy-tour
      v-model:visible="showTour"
      :steps="tourSteps"
      :mask-closable="true"
      @finish="onTourFinish"
      @skip="onTourSkip"
    />
  </view>
</template>

第四步:触发引导

methods: {
  // 在适当的时机开启引导
  onReady() {
    // 首次使用时显示引导
    const hasShownTour = uni.getStorageSync('hasShownTour')
    if (!hasShownTour) {
      setTimeout(() => {
        this.showTour = true
      }, 500) // 延迟500ms,确保页面渲染完成
    }
  },

  onTourFinish() {
    console.log('引导完成')
    uni.setStorageSync('hasShownTour', true)
    uni.showToast({ title: '引导完成', icon: 'success' })
  },

  onTourSkip() {
    console.log('用户跳过引导')
    uni.setStorageSync('hasShownTour', true)
  }
}

📚 API 文档

组件 Props

属性 类型 默认值 必填 说明
visible Boolean false 是否显示引导(Vue 2 使用 :visible.sync,Vue 3 使用 v-model:visible
steps Array [] 引导步骤数组,详见下方 Steps 配置
maskClosable Boolean false 点击遮罩是否关闭引导
autoStart Boolean true visibletrue 时是否自动开始引导
zIndex Number 9999 组件层级,可根据实际情况调整

Steps 配置项

每个 step 对象支持以下属性:

属性 类型 默认值 必填 说明
selector String - 目标元素选择器,支持 ID(#id)或 class(.class
content String - 提示内容文本
shape String 'rect' 高亮形状:
rect - 矩形
circle - 圆形
round - 圆角矩形
position String 'bottom' 提示框位置:
top - 元素上方
bottom - 元素下方
left - 元素左侧
right - 元素右侧
auto - 自动计算最佳位置
padding Number 8 高亮区域的内边距(单位:px)
showGesture Boolean false 是否显示手势动画指示器
gestureType String 'tap' 手势类型(需要 showGesture=true):
tap - 点击手势,涟漪效果
swipe - 滑动手势,带方向箭头
default - 默认脉冲手势
swipeDirection String 'right' 滑动方向(仅 gestureType='swipe' 时有效):
left / right / up / down

组件事件

事件名 说明 回调参数 触发时机
finish 完成所有引导步骤 - 用户完成最后一步引导
skip 用户跳过引导 - 用户点击"跳过"按钮
update:visible visible 状态更新 (visible: Boolean) visible 值变化时

💡 使用示例

示例 1:电商应用首页引导

<template>
  <view>
    <!-- 顶部搜索栏 -->
    <view id="search-bar" class="search">
      <input placeholder="搜索商品" />
    </view>

    <!-- 购物车按钮 -->
    <view id="cart-btn" class="cart-icon">
      🛒
    </view>

    <!-- 商品列表 -->
    <view id="product-list" class="products">
      <!-- 商品... -->
    </view>

    <!-- 引导组件 -->
    <hy-tour
      :visible.sync="showTour"
      :steps="tourSteps"
      @finish="onTourFinish"
    />
  </view>
</template>

<script>
export default {
  data() {
    return {
      showTour: false,
      tourSteps: [
        {
          selector: '#search-bar',
          content: '🔍 在这里搜索你想要的商品',
          shape: 'round',
          position: 'bottom',
          padding: 12
        },
        {
          selector: '#cart-btn',
          content: '🛒 点击查看购物车',
          shape: 'circle',
          position: 'left',
          showGesture: true,
          gestureType: 'tap',
          padding: 15
        },
        {
          selector: '#product-list',
          content: '👈 向左滑动查看更多商品',
          shape: 'rect',
          position: 'top',
          showGesture: true,
          gestureType: 'swipe',
          swipeDirection: 'left'
        }
      ]
    }
  },
  onLoad() {
    // 首次进入显示引导
    const isFirstVisit = !uni.getStorageSync('visited_home')
    if (isFirstVisit) {
      setTimeout(() => {
        this.showTour = true
      }, 1000)
    }
  },
  methods: {
    onTourFinish() {
      uni.setStorageSync('visited_home', true)
      uni.showToast({ title: '欢迎使用!', icon: 'success' })
    }
  }
}
</script>

示例 2:社交应用功能引导

<template>
  <view>
    <view id="post-btn" class="post-button">+ 发布</view>
    <view id="message-tab" class="tab">消息</view>
    <view id="profile-tab" class="tab">我的</view>

    <hy-tour
      :visible.sync="showGuide"
      :steps="guideSteps"
      :mask-closable="true"
      @finish="handleFinish"
      @skip="handleSkip"
    />
  </view>
</template>

<script>
export default {
  data() {
    return {
      showGuide: false,
      guideSteps: [
        {
          selector: '#post-btn',
          content: '点击这里发布你的第一条动态吧!',
          shape: 'round',
          position: 'bottom',
          showGesture: true,
          gestureType: 'tap',
          padding: 10
        },
        {
          selector: '#message-tab',
          content: '在这里查看好友消息',
          shape: 'round',
          position: 'top',
          padding: 8
        },
        {
          selector: '#profile-tab',
          content: '个人资料和设置都在这里',
          shape: 'round',
          position: 'top',
          padding: 8
        }
      ]
    }
  },
  methods: {
    // 手动触发引导
    startGuide() {
      this.showGuide = true
    },

    handleFinish() {
      console.log('用户完成引导')
      // 记录用户已完成引导
      this.markGuideAsCompleted()
    },

    handleSkip() {
      console.log('用户跳过引导')
      // 询问是否下次再显示
      uni.showModal({
        title: '提示',
        content: '下次进入还要显示引导吗?',
        success: (res) => {
          if (!res.confirm) {
            this.markGuideAsCompleted()
          }
        }
      })
    },

    markGuideAsCompleted() {
      uni.setStorageSync('guide_completed', true)
    }
  }
}
</script>

示例 3:动态生成引导步骤

export default {
  data() {
    return {
      showTour: false,
      tourSteps: [],
      userPermissions: ['create', 'edit', 'export'] // 用户权限
    }
  },

  onReady() {
    // 根据用户权限动态生成引导步骤
    this.tourSteps = this.generateTourSteps()

    // 检查是否需要显示引导
    if (this.shouldShowTour()) {
      setTimeout(() => {
        this.showTour = true
      }, 800)
    }
  },

  methods: {
    generateTourSteps() {
      const steps = []

      // 基础步骤,所有用户都看到
      steps.push({
        selector: '#home-btn',
        content: '这是首页,显示最新内容',
        shape: 'round',
        position: 'bottom'
      })

      // 根据权限添加步骤
      if (this.userPermissions.includes('create')) {
        steps.push({
          selector: '#create-btn',
          content: '点击这里创建新内容',
          shape: 'round',
          position: 'bottom',
          showGesture: true,
          gestureType: 'tap'
        })
      }

      if (this.userPermissions.includes('export')) {
        steps.push({
          selector: '#export-btn',
          content: '可以导出数据为 Excel',
          shape: 'circle',
          position: 'left'
        })
      }

      return steps
    },

    shouldShowTour() {
      // 版本更新后显示新功能引导
      const lastVersion = uni.getStorageSync('last_version')
      const currentVersion = '1.2.0'

      if (lastVersion !== currentVersion) {
        uni.setStorageSync('last_version', currentVersion)
        return true
      }

      return false
    }
  }
}

🎨 高级用法

1. 自定义提示框内容

虽然组件暂不支持插槽,但你可以在 content 中使用富文本或 Emoji:

tourSteps: [
  {
    selector: '#feature1',
    content: '✨ 这是新功能\n👉 点击体验',
    shape: 'round',
    position: 'bottom'
  }
]

2. 分批次引导

对于复杂应用,可以分多个场景进行引导:

export default {
  data() {
    return {
      showTour: false,
      currentScene: 'home', // home, profile, settings
      allSteps: {
        home: [/* 首页引导步骤 */],
        profile: [/* 个人中心引导步骤 */],
        settings: [/* 设置页引导步骤 */]
      }
    }
  },
  computed: {
    tourSteps() {
      return this.allSteps[this.currentScene] || []
    }
  },
  methods: {
    startSceneGuide(scene) {
      this.currentScene = scene
      this.showTour = true
    }
  }
}

3. 版本更新引导

在版本更新后,只展示新功能引导:

methods: {
  checkVersionUpdate() {
    const lastVersion = uni.getStorageSync('app_version') || '1.0.0'
    const currentVersion = '1.2.0'

    if (this.compareVersion(lastVersion, currentVersion) < 0) {
      // 显示新版本功能引导
      this.showNewFeatureGuide()
      uni.setStorageSync('app_version', currentVersion)
    }
  },

  showNewFeatureGuide() {
    this.tourSteps = [
      {
        selector: '#new-feature-1',
        content: '🎉 新功能:支持批量导出',
        shape: 'round',
        position: 'bottom'
      }
    ]
    this.showTour = true
  },

  compareVersion(v1, v2) {
    const arr1 = v1.split('.')
    const arr2 = v2.split('.')
    for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
      const num1 = parseInt(arr1[i]) || 0
      const num2 = parseInt(arr2[i]) || 0
      if (num1 !== num2) return num1 - num2
    }
    return 0
  }
}

4. 带条件的步骤显示

根据用户状态或权限显示不同的引导:

computed: {
  tourSteps() {
    const steps = []

    // 基础步骤(所有用户)
    steps.push({
      selector: '#btn-home',
      content: '欢迎来到首页',
      shape: 'round',
      position: 'bottom'
    })

    // VIP 专属功能引导
    if (this.userInfo.isVip) {
      steps.push({
        selector: '#vip-feature',
        content: '🎖️ VIP 专享:高级功能',
        shape: 'round',
        position: 'bottom',
        showGesture: true,
        gestureType: 'tap'
      })
    }

    // 管理员功能引导
    if (this.userInfo.role === 'admin') {
      steps.push({
        selector: '#admin-panel',
        content: '👑 管理员面板',
        shape: 'rect',
        position: 'left'
      })
    }

    return steps
  }
}

❓ 常见问题

Q1: 为什么引导没有显示?

A: 检查以下几点:

  1. 元素是否已渲染:确保目标元素已经渲染到页面上

    onReady() {
     // ✅ 正确:在 onReady 后延迟启动
     setTimeout(() => {
       this.showTour = true
     }, 500)
    }
  2. 选择器是否正确:检查 ID 或 class 是否正确

    // ❌ 错误
    selector: 'btn-search'
    
    // ✅ 正确
    selector: '#btn-search'  // ID 选择器
    selector: '.btn-search'  // class 选择器
  3. steps 数组是否为空:确保 steps 数组有内容

    console.log('tourSteps:', this.tourSteps)

Q2: 小程序中提示框被遮挡怎么办?

A: 调整 zIndex 属性:

<hy-tour
  :visible.sync="showTour"
  :steps="tourSteps"
  :z-index="10000"
/>

Q3: 如何在多个页面使用同一套引导?

A: 建议封装为 mixin 或 composition API:

// mixins/tourMixin.js
export default {
  data() {
    return {
      showTour: false,
      tourSteps: []
    }
  },
  methods: {
    initTour(steps) {
      this.tourSteps = steps
      const key = `tour_${this.$route.path}`
      const hasShown = uni.getStorageSync(key)

      if (!hasShown) {
        setTimeout(() => {
          this.showTour = true
        }, 500)
      }
    },
    onTourFinish() {
      const key = `tour_${this.$route.path}`
      uni.setStorageSync(key, true)
    }
  }
}

// 页面中使用
import tourMixin from '@/mixins/tourMixin'

export default {
  mixins: [tourMixin],
  onReady() {
    this.initTour([
      { selector: '#btn1', content: '按钮1' },
      { selector: '#btn2', content: '按钮2' }
    ])
  }
}

Q4: 如何让用户可以重新查看引导?

A: 提供一个"查看引导"的入口:

<template>
  <view>
    <!-- 设置页面的帮助按钮 -->
    <button @click="showTourAgain">📖 查看功能引导</button>

    <hy-tour
      :visible.sync="showTour"
      :steps="tourSteps"
      @finish="onTourFinish"
    />
  </view>
</template>

<script>
export default {
  methods: {
    showTourAgain() {
      this.showTour = true
    }
  }
}
</script>

Q5: Vue 2 和 Vue 3 语法区别?

A:

<!-- Vue 2 项目使用 .sync 修饰符 -->
<hy-tour :visible.sync="showTour" :steps="tourSteps" />

<!-- Vue 3 项目使用 v-model: -->
<hy-tour v-model:visible="showTour" :steps="tourSteps" />

Q6: 在 nvue 页面中如何使用?

A: nvue 页面中选择器有所不同,需要注意:

  1. 只能使用 refid 选择器
  2. 不支持 class 选择器
  3. 建议使用 id 选择器
<!-- nvue 页面 -->
<template>
  <view>
    <view id="target-element">目标元素</view>

    <hy-tour
      :visible.sync="showTour"
      :steps="[{ selector: '#target-element', content: '提示' }]"
    />
  </view>
</template>

Q7: 如何自定义提示框样式?

A: 组件内部样式目前是固定的,如需深度定制,可以:

  1. 修改组件源码 hy-step.vue 中的样式
  2. 或者提 Issue 反馈需求,后续版本会支持更多自定义选项

💪 最佳实践

1. 引导时机选择

// ✅ 推荐:首次使用时显示
onLoad() {
  const isFirstTime = !uni.getStorageSync('has_used_app')
  if (isFirstTime) {
    this.showGuideOnReady = true
  }
}

onReady() {
  if (this.showGuideOnReady) {
    setTimeout(() => {
      this.showTour = true
    }, 800) // 给页面渲染留足时间
  }
}

// ❌ 不推荐:每次都显示,会让用户反感
onReady() {
  this.showTour = true
}

2. 步骤数量控制

// ✅ 推荐:3-5 个步骤,重点突出
tourSteps: [
  { selector: '#key-feature-1', content: '核心功能1' },
  { selector: '#key-feature-2', content: '核心功能2' },
  { selector: '#key-feature-3', content: '核心功能3' }
]

// ❌ 不推荐:步骤过多,用户容易失去耐心
tourSteps: [
  // 10+ 个步骤...
]

3. 文案编写建议

// ✅ 推荐:简洁明了,友好亲切
content: '👋 欢迎!点击这里开始创作'

// ❌ 不推荐:过于冗长或专业
content: '此按钮用于触发内容创建流程,点击后将进入编辑器界面,您可以在编辑器中...'

4. 善用手势动画

// ✅ 推荐:关键操作使用手势提示
{
  selector: '#submit-btn',
  content: '完成后点击提交',
  showGesture: true,
  gestureType: 'tap'  // 点击手势
}

{
  selector: '#slider',
  content: '向左滑动查看更多',
  showGesture: true,
  gestureType: 'swipe',
  swipeDirection: 'left'  // 滑动手势
}

5. 记录用户行为

methods: {
  onTourFinish() {
    // 记录完成时间
    uni.setStorageSync('tour_completed_time', Date.now())

    // 上报统计
    this.reportAnalytics('tour_completed', {
      duration: this.tourDuration,
      steps: this.tourSteps.length
    })
  },

  onTourSkip() {
    // 记录跳过行为
    uni.setStorageSync('tour_skipped', true)

    // 上报统计
    this.reportAnalytics('tour_skipped', {
      step_index: this.currentStepIndex
    })
  }
}

🔧 注意事项

1. 元素渲染时机

⚠️ 必须确保目标元素已渲染

// ✅ 正确做法
onReady() {
  // 页面渲染完成后再启动
  setTimeout(() => {
    this.showTour = true
  }, 500)
}

// ❌ 错误做法
onLoad() {
  // 页面还没渲染完,元素可能不存在
  this.showTour = true
}

2. 平台兼容性

平台 支持情况 注意事项
H5 ✅ 完全支持 使用 box-shadow 实现遮罩
微信小程序 ✅ 完全支持 优先使用 Canvas 2D API
支付宝小程序 ✅ 完全支持 自动降级到旧版 Canvas
App (iOS/Android) ✅ 完全支持 性能最佳
uni-app x ✅ 支持 需要 HBuilderX 3.6+
nvue ⚠️ 部分支持 只能使用 ID 选择器

3. 小程序限制

  • 部分小程序平台不支持 Canvas 2D API,组件会自动降级
  • 小程序中 Canvas 层级较高,确保 zIndex 设置合理
  • 自定义导航栏可能影响定位,需要调整 padding

4. 性能优化

// ✅ 推荐:按需加载
onReady() {
  const needGuide = this.checkIfNeedGuide()
  if (needGuide) {
    // 只在需要时才渲染引导组件
    this.showTour = true
  }
}

// ❌ 不推荐:始终渲染引导组件
<hy-tour :visible.sync="showTour" :steps="tourSteps" />

5. 滚动容器

如果目标元素在自定义滚动容器(如 scroll-view)内:

<scroll-view scroll-y>
  <view id="target">目标元素</view>
</scroll-view>

<!-- 自动滚动功能可能无效,需要手动处理 -->

6. 动态内容

对于动态加载的内容,确保内容加载完成后再启动引导:

async onReady() {
  // 等待数据加载
  await this.loadData()

  // 等待列表渲染
  await this.$nextTick()

  // 再启动引导
  setTimeout(() => {
    this.showTour = true
  }, 500)
}

祝你的应用引导体验越来越好! 🎉

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

暂无用户评论。