更新记录

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


平台兼容性

uni-app(3.6.14)

Vue2 Vue2插件版本 Vue3 Vue3插件版本 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 否 visible 为 true 时是否自动开始引导
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. 只能使用 ref 或 id 选择器
  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协议

暂无用户评论。