跳转到主要内容

概述

本教程向您展示如何使用单个 HTML 应用内消息创建多步骤引导轮播。与依赖滑动手势的传统轮播不同,此方法使用按钮驱动导航,并将所有步骤保持在一条消息中。 您将构建的内容:
  • 包含图片、文本和按钮的两步引导流程
  • 按钮导航(点击”下一步”前进,点击”开始使用”关闭)
  • 进度指示点
  • 步骤之间的平滑淡入淡出过渡
展示欢迎屏幕、图片、文本和下一步按钮的引导轮播
在以下情况使用此方法:
  • 引导用户完成简短的引导或教育流程(2-5 步)
  • 要求用户明确点击按钮才能继续(无滑动手势)
  • 将所有内容保持在一个 HTML 应用内消息中以简化操作
  • 流程完成时自动关闭消息
本指南使用 HTML 应用内消息以获得完全控制。您也可以使用拖放编辑器构建基于卡片的引导流程——这些卡片支持滑动但定制性较低。

前提条件

开始之前,请确保您具备:

多步骤流程的工作原理

在深入代码之前,了解技术方法很重要。此实现使用一个 HTML 应用内消息,通过显示和隐藏内容在步骤之间切换,而不是加载多个单独的消息。 该架构依赖于四个核心组件
1

每个步骤的卡片容器

每个步骤都包装在一个带有 card 类和唯一 ID 的 <div> 中:
<div id="card-0" class="card active">...</div>
<div id="card-1" class="card">...</div>
  • 所有卡片同时存在于 DOM 中
  • 一次只有一张卡片可见(由 active 类控制)
2

CSS 可见性控制

CSS 使用透明度和指针事件处理显示/隐藏逻辑:
.card {
  opacity: 0;
  pointer-events: none;  /* 防止与隐藏卡片的交互 */
  transition: opacity .25s ease;
}

.card.active {
  opacity: 1;
  pointer-events: auto;  /* 允许与可见卡片的交互 */
}
为什么这很重要:
  • opacity: 0 在视觉上隐藏卡片但保留其在布局中的位置
  • pointer-events: none 防止意外点击隐藏的卡片
  • transition 创建平滑的淡入淡出效果
3

JavaScript 状态管理

setActive(i) 函数控制哪张卡片可见:
function setActive(i) {
  // 更新卡片可见性
  document.getElementById("card-0").className = i === 0 ? "card active" : "card";
  document.getElementById("card-1").className = i === 1 ? "card active" : "card";

  // 更新进度点
  var dots = document.getElementById("dots").children;
  dots[0].classList.toggle("active", i === 0);
  dots[1].classList.toggle("active", i === 1);
}
此函数:
  • 从所有卡片中移除 active
  • active 添加到目标卡片
  • 更新进度指示点
4

按钮事件监听器

按钮触发导航或关闭:
// 前进到下一步
document.getElementById("next-0").addEventListener("click", function () {
  setActive(1);
});

// 关闭应用内消息
document.getElementById("done").addEventListener("click", function (e) {
  if (window.OneSignalIamApi && OneSignalIamApi.close) {
    OneSignalIamApi.close(e);
  }
});
重要提示: OneSignalIamApi.close(e) 是从 HTML 内部关闭应用内消息的 OneSignal SDK 方法。
关键见解: 这是应用内消息中的单页应用 (SPA) 模式。所有内容一次性加载,JavaScript 管理状态变化而无需重新加载。

步骤 1:创建新的 HTML 应用内消息

  1. 在 OneSignal 仪表板中,转到 Messages → In-App Messages
  2. 点击 New In-App Message
  3. 选择 HTML 作为消息类型
  4. 选择 Full ScreenLarge 布局(推荐用于引导以最大化视觉效果)
  5. 继续进入 HTML 编辑器
HTML 编辑器预览可能无法完全反映运行时行为。请始终在真实设备或测试用户上进行测试,以验证动画、按钮行为和关闭操作。

步骤 2:添加 HTML 模板

用以下模板替换编辑器内容。此模板包括:
  • 自包含代码: 所有 HTML、CSS 和 JavaScript 在一个文件中
  • 按钮驱动导航: 无滑动手势(跨设备更可靠)
  • 淡入淡出过渡: 步骤之间平滑的透明度变化
  • OneSignal SDK 集成: 使用 OneSignalIamApi.close(e) 关闭消息
  • 移动端优化: 带有 viewport meta 标签的响应式布局
<!doctype html>
<html>
<head>
  <meta charset="UTF-8" />
  <!-- viewport-fit=cover 确保在带刘海的设备上覆盖安全区域 -->
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
  <style>
    /* 基础样式 - 重置和系统字体 */
    html, body {
      margin: 0;
      padding: 0;
      background: #ffffff;
      font-family: -apple-system, system-ui;
    }

    /* 带内边距的主容器 */
    .wrap {
      padding: 28px 22px 24px;
    }

    /* 舞台容器 - 将所有卡片保持在同一位置 */
    .stage {
      position: relative;
      min-height: 74vh;  /* 确保足够的垂直空间 */
    }

    /* 卡片 - 引导流程的每个步骤 */
    .card {
      position: absolute;  /* 所有卡片重叠在同一位置 */
      inset: 0;            /* 完全覆盖舞台 */
      display: flex;
      flex-direction: column;
      align-items: center;
      opacity: 0;               /* 默认隐藏 */
      pointer-events: none;     /* 隐藏时防止点击 */
      transition: opacity .25s ease;  /* 平滑淡入淡出效果 */
    }

    /* 活动卡片可见且可交互 */
    .card.active {
      opacity: 1;
      pointer-events: auto;
    }

    /* 排版 */
    h1 {
      margin: 44px 0 12px;
      font-size: 26px;
      text-align: center;
    }

    p {
      margin: 0;
      color: #6b7280;
      text-align: center;
      max-width: 260px;
      line-height: 1.35;
    }

    /* 图片容器 - 带圆角的正方形 */
    .image {
      width: 240px;
      height: 240px;
      border-radius: 16px;
      margin: 24px 0 12px;
      background-size: cover;
      background-position: center;
    }

    /* 主按钮 */
    .btn {
      margin-top: auto;  /* 将按钮推到卡片底部 */
      width: 100%;
      max-width: 260px;
      height: 52px;
      border: 0;
      border-radius: 12px;
      background: #3b82f6;  /* 蓝色 - 可根据您的品牌定制 */
      color: #fff;
      font-size: 18px;
      font-weight: 600;
    }

    /* 进度指示点 */
    .dots {
      display: flex;
      justify-content: center;
      gap: 8px;
      padding: 12px 0 8px;
    }

    .dot {
      width: 8px;
      height: 8px;
      border-radius: 999px;
      background: #d1d5db;  /* 非活动点颜色 */
    }

    .dot.active {
      background: #6b7280;  /* 活动点颜色 */
      transform: scale(1.15);  /* 活动时略大 */
    }
  </style>
</head>

<body>
  <div class="wrap">
    <div class="stage">

      <!-- 步骤 1:欢迎卡片(以 "active" 类开始可见) -->
      <div id="card-0" class="card active">
        <h1>欢迎</h1>
        <div
          class="image"
          style="background-image: url('https://images.pexels.com/photos/6153129/pexels-photo-6153129.jpeg');">
        </div>
        <p>几分钟内养成平静的日常习惯。</p>
        <button
          id="next-0"
          class="btn"
          data-onesignal-unique-label="onboarding_next_0">
          下一步
        </button>
      </div>

      <!-- 步骤 2:呼吸卡片(开始隐藏,用户点击"下一步"时显示) -->
      <div id="card-1" class="card">
        <h1>呼吸</h1>
        <div
          class="image"
          style="background-image: url('https://images.pexels.com/photos/417173/pexels-photo-417173.jpeg');">
        </div>
        <p>随时进行引导式呼吸来重置状态。</p>
        <button
          id="done"
          class="btn"
          data-onesignal-unique-label="onboarding_done">
          开始使用
        </button>
      </div>

    </div>

    <!-- 进度指示器:2 个点,第一个开始时为活动状态 -->
    <div class="dots" id="dots">
      <div class="dot active"></div>
      <div class="dot"></div>
    </div>
  </div>

  <script>
    (function () {
      /**
       * 通过切换 "active" 类在卡片之间切换
       * @param {number} i - 要显示的卡片索引(0 或 1)
       */
      function setActive(i) {
        // 更新卡片可见性
        document.getElementById("card-0").className = i === 0 ? "card active" : "card";
        document.getElementById("card-1").className = i === 1 ? "card active" : "card";

        // 更新进度点
        var dots = document.getElementById("dots").children;
        dots[0].classList.toggle("active", i === 0);
        dots[1].classList.toggle("active", i === 1);
      }

      // 按钮:下一步(卡片 0 → 卡片 1)
      document.getElementById("next-0").addEventListener("click", function () {
        setActive(1);
      });

      // 按钮:开始使用(关闭应用内消息)
      document.getElementById("done").addEventListener("click", function (e) {
        // 检查 OneSignal IAM API 是否可用
        if (window.OneSignalIamApi && OneSignalIamApi.close) {
          OneSignalIamApi.close(e);  // 关闭消息
        }
      });
    })();
  </script>
</body>
</html>

步骤 3:自定义您的内容

可安全自定义的内容

您可以修改这些元素而不会破坏功能: 内容:
  • <h1> 标签中的标题文本
  • <p> 标签中的正文内容
  • 按钮标签(下一步开始使用
  • background-image: url('...') 样式中的图片 URL
视觉样式:
  • 颜色:更改 .btn 背景色、文本颜色或点的颜色
  • 间距:调整内边距和外边距
  • 排版:修改 font-family、font-size、font-weight
  • 圆角:更新按钮和图片的 border-radius

添加更多步骤

要添加第三个步骤,请按照以下模式操作:
  1. 添加 HTML 卡片:
<div id="card-2" class="card">
  <h1>您的标题</h1>
  <div class="image" style="background-image: url('your-image-url');"></div>
  <p>您的描述</p>
  <button id="next-2" class="btn">下一步</button>
</div>
  1. 添加进度点:
<div class="dots" id="dots">
  <div class="dot active"></div>
  <div class="dot"></div>
  <div class="dot"></div> <!-- 新点 -->
</div>
  1. 更新 setActive() 函数:
function setActive(i) {
  document.getElementById("card-0").className = i === 0 ? "card active" : "card";
  document.getElementById("card-1").className = i === 1 ? "card active" : "card";
  document.getElementById("card-2").className = i === 2 ? "card active" : "card"; // 新卡片

  var dots = document.getElementById("dots").children;
  dots[0].classList.toggle("active", i === 0);
  dots[1].classList.toggle("active", i === 1);
  dots[2].classList.toggle("active", i === 2); // 新点
}
  1. 更新上一步骤的按钮 ID: 将卡片 1 按钮的 id="done" 更改为 id="next-1",然后添加点击监听器:
document.getElementById("next-1").addEventListener("click", function () {
  setActive(2);
});
  1. 将关闭按钮添加到新的最后一张卡片(card-2):
document.getElementById("done").addEventListener("click", function (e) {
  if (window.OneSignalIamApi && OneSignalIamApi.close) {
    OneSignalIamApi.close(e);
  }
});
保持引导流程简短(最多 2-4 步)。用户在较长的流程中会快速流失。使用点击跟踪测试完成率。

步骤 4:测试应用内消息

测试清单

  1. 在 OneSignal 仪表板中保存消息
  2. 配置发送设置:
    • 设置触发条件(例如,会话开始、特定页面浏览)
    • 选择目标受众或选择测试用户
  3. 发送到测试设备:
    • 使用测试用户预览而不影响生产用户
    • 在物理设备上安装您的应用(推荐而非模拟器以获得准确行为)
  4. 验证功能:
    • ✓ 第一张卡片显示正确内容
    • ✓ “下一步”按钮前进到卡片 2
    • ✓ 进度点正确更新
    • ✓ 淡入淡出过渡平滑
    • ✓ “开始使用”按钮关闭消息
    • ✓ 消息不会立即重新出现(检查频率限制设置)
模拟器可能无法准确反映真实设备行为,特别是触摸交互和 SDK 集成。在发布到生产环境之前,请务必在物理设备上进行测试。

常见问题故障排除

问题可能原因解决方案
消息未出现未满足触发条件检查应用内消息触发器并验证您的测试用户是否满足条件
按钮不起作用JavaScript 错误或 ID 不匹配检查浏览器控制台是否有错误;验证按钮 ID 与事件监听器 ID 是否匹配
图片未加载CORS 问题或无效 URL使用 HTTPS URL;首先在浏览器中测试图片 URL
消息出现但无法关闭OneSignal SDK 未加载验证 Mobile SDK 设置是否完成

后续步骤

跟踪用户参与度:
  • 使用 data-onesignal-unique-label 属性添加点击跟踪(模板中已包含)以衡量步骤之间的流失率
  • Messages → In-App Messages → [您的消息] → Analytics 中查看点击分析
个性化体验: 高级自定义:
  • 关闭后将用户深度链接到特定屏幕
  • 使用 Liquid 语法用用户名或属性个性化标题
  • 使用不同的引导流程实施 A/B 测试以优化完成率