ng-tour-guide
v1.0.5
Published
An Angular tour guide component with Ionic support
Readme
ng-tour-guide
一個基於 Angular 和 Ionic 的導覽指引元件,提供互動式的功能導覽體驗。
功能特點
- 🎯 智能定位:自動計算並調整提示框位置
- 🎨 完全可自定義的樣式
- ⌨️ 支援鍵盤導航(方向鍵和 ESC)
- ♿ 無障礙支援(ARIA 標籤)
- 🔄 響應式設計:自動處理視窗大小變化
- ✅ 進階驗證和條件控制
- 即時驗證 (validate)
- 步驟前檢查 (beforeNext)
- 條件觸發 (triggerWhen)
- 🔒 TypeScript 型別支援
- 📱 支援 Ionic 框架
- 🌐 支援 Angular 14-19 版本
安裝
npm install ng-tour-guide基本使用
- 在你的模組中導入 TourComponent:
import { TourComponent } from "ng-tour-guide";
@NgModule({
imports: [TourComponent], // 已經是 standalone component
})
export class YourModule {}- 在組件中定義導覽步驟:
import { TourStep } from "ng-tour-guide";
export class YourComponent {
isTourVisible = false;
tourSteps: TourStep[] = [
{
target: '.title-button', // CSS 選擇器
title: '開始按鈕',
content: '點擊這裡開始使用系統',
placement: 'bottom', // 提示框位置
ariaLabel: '開始按鈕導覽', // 無障礙標籤
},
{
target: '[data-tour="design-version"]',
title: '版次清單',
content: '這裡可以看到所有的版次',
placement: 'right',
validate: () => this.isMenuReady(), // 驗證函數
beforeNext: async () => {
// 下一步前的檢查
return await this.checkMenuStatus();
},
}
];
onTourFinish() {
this.isTourVisible = false;
console.log('導覽完成');
}
}- 在模板中使用:
<app-tour
[steps]="tourSteps"
[(visible)]="isTourVisible"
(stepChange)="onStepChange($event)"
(finishTour)="onTourFinish()"
>
</app-tour>
<ion-button class="title-button" [class.active-button]="num === 0" (click)="chTitle(0)">開始</ion-button>
<ion-button
*ngIf="num === 0"
class="title-button"
(click)="titleChange(0)"
data-tour="design-version"
>設計版次</ion-button
>API 詳解
TourStep 介面
interface TourStep {
target: string; // 目標元素的 CSS 選擇器
title: string; // 步驟標題
content: string; // 步驟內容
placement?: "top" | "bottom" | "left" | "right"; // 提示框位置
validate?: () => boolean; // 驗證函數
beforeNext?: () => boolean | Promise<boolean>; // 下一步前的檢查
triggerWhen?: () => boolean | Promise<boolean>; // 觸發條件
ariaLabel?: string; // 無障礙標籤
}進階功能說明
✅ 1. validate 驗證函數
用於驗證當前步驟是否可以繼續。
📌 功能:
「這個步驟的 target 元素是否存在或顯示出來」的同步檢查。如果返回 false,下一步按鈕將被禁用。
🕒 何時執行?
在切換到這個步驟之前,會進行一次驗證,用來確認這個步驟的 DOM 目標是否「準備好」。
🔧 用途實例:
確保 .title-button:first-child 真的已經出現在 DOM 上
確保某個 tab 切換後才有的內容已經可見
example1
tourSteps: TourStep[] = [
{
target: "#form-submit",
title: "表單提交",
content: "填寫完表單後點擊提交",
validate: () => {
// 檢查表單是否已填寫
return this.form.valid;
}
}
];example2
validate: () => {
const el = document.querySelector('.some-class');
return el !== null && el.offsetParent !== null; // 避免導覽找不到目標
}✅ 2. beforeNext 下一步檢查
📌 功能:
「當使用者點擊【下一步】前」,是否允許進入下一步(可加強互動,通常會是 非同步的確認或警告)
🕒 何時執行?
當你按下「下一步」按鈕時,在跳下一步之前會先執行它。
🔧 用途實例:
跳出確認視窗(是否已經了解這一步的重要性?)
做一些非同步檢查(例如確保資料填完或儲存成功)
example1
tourSteps: TourStep[] = [
{
target: '[data-tour="design-version"]', // 設計版次按鈕
title: '設計版次',
content: '在這裡可以查看和管理不同版本的設計',
placement: 'bottom',
// 進入下一步前確認用戶已了解設計版次的重要性
beforeNext: async () => {
const confirmed = await this.presentAlert({
header: '重要提醒',
message: '設計版次管理對於追蹤設計變更非常重要,您確定要繼續嗎?',
buttons: ['取消', '確定'],
});
return confirmed;
},
},
];
async presentAlert(alertOptions: { header: string; message: string; buttons: string[] }) {
const alert = await this.alertCtrl.create({
header: alertOptions.header,
message: alertOptions.message,
buttons: [
{
text: alertOptions.buttons[0],
role: 'cancel',
cssClass: 'secondary'
},
{
text: alertOptions.buttons[1],
handler: () => {
return true;
}
}
],
cssClass: 'custom-alert',
backdropDismiss: false
});
await alert.present();
const { role } = await alert.onDidDismiss();
return role !== 'cancel';
}✅ 3. triggerWhen 觸發條件
📌 功能:
「是否進入這個步驟」的條件判斷(可以是非同步)。只有當返回 true 或 Promise 解析為 true 時,該步驟才會顯示。
🕒 何時執行?
在導覽流程還沒顯示這個步驟時,就會執行這個函式來判斷「是否跳過這個步驟」。
🔧 用途實例:
登入檢查(導覽只能在登入狀態下執行)
等待某些資料或元件載入完成(例如圖表、地圖)
example1
triggerWhen: async () => {
return await this.isReady(); // 等待某項前置條件
}example2
tourSteps: TourStep[] = [
{
target: "#advanced-feature",
title: "進階功能",
content: "這是進階功能說明",
triggerWhen: async () => {
// 檢查用戶權限
const hasPermission = await this.checkUserPermission();
return hasPermission;
}
}
];組合使用示例
export class YourComponent {
tourSteps: TourStep[] = [
{
target: '.title-button:first-child', // 規劃設計按鈕
title: '功能選擇',
content: '這裡可以選擇不同的功能區域:規劃設計、監造測試、使用檢修',
placement: 'bottom',
// 確保用戶已經登入才能開始導覽
triggerWhen: () => {
// return this.authService.isLoggedIn();
return this.login_name !== '';
},
// 確保按鈕元素已經完全載入
validate: () => {
const button = document.querySelector('.title-button:first-child') as HTMLElement;
return button !== null && button.offsetParent !== null;
},
},
{
target: '[data-tour="design-version"]', // 設計版次按鈕
title: '設計版次',
content: '在這裡可以查看和管理不同版本的設計',
placement: 'bottom',
// 基本驗證:確保在規劃設計模式
validate: () => {
return this.num === 0;
},
// 進入下一步前確認用戶已了解設計版次的重要性
beforeNext: async () => {
const confirmed = await this.presentAlert({
header: '重要提醒',
message: '設計版次管理對於追蹤設計變更非常重要,您確定要繼續嗎?',
buttons: ['取消', '確定'],
});
return confirmed;
},
},
{
target: '[data-tour="analysis-tools"]', // 分析工具按鈕
title: '分析工具',
content: '這裡提供各種分析工具',
placement: 'bottom',
// 基本驗證:確保在規劃設計模式
validate: () => {
return this.num === 0;
},
// 等待分析工具載入完成
triggerWhen: async () => {
// 假設有一個方法檢查分析工具是否已載入
const toolsLoaded = await this.checkAnalysisToolsLoaded();
return toolsLoaded;
},
// 進入下一步前確認用戶已了解分析工具的使用方式
beforeNext: async () => {
const confirmed = await this.presentAlert({
header: '使用提示',
message: '分析工具需要正確的數據才能提供準確的分析結果,您確定要繼續嗎?',
buttons: ['取消', '確定'],
});
return confirmed;
},
},
];
}最佳實踐
validate 使用建議:
- 用於即時驗證,如表單驗證
- 避免執行耗時操作
- 返回結果應該立即可用
beforeNext 使用建議:
- 用於執行必要的操作,如保存數據
- 可以包含異步操作
- 適合處理需要用戶確認的操作
triggerWhen 使用建議:
- 用於控制步驟的顯示時機
- 可以根據用戶權限或系統狀態決定
- 適合實現條件性導覽流程
錯誤處理:
- 在 beforeNext 中妥善處理錯誤
- 提供適當的錯誤提示
- 考慮重試機制
性能優化:
- 避免在 validate 中執行耗時操作
- 合理使用異步操作
- 考慮使用緩存機制
元件輸入屬性
| 屬性 | 類型 | 描述 | |------|------|------| | steps | TourStep[] | 導覽步驟陣列 | | visible | boolean | 控制導覽的顯示/隱藏 |
元件輸出事件
| 事件 | 類型 | 描述 | |------|------|------| | visibleChange | EventEmitter | 可見性變更事件 | | stepChange | EventEmitter | 步驟變更事件 | | finishTour | EventEmitter | 導覽完成事件 |
樣式自定義
你可以通過覆蓋以下 CSS 類別來自定義樣式:
基礎樣式
.tour-overlay {
// 覆蓋層樣式
// 預設: 半透明黑色背景
background: rgba(0, 0, 0, 0.4);
z-index: 9999;
}
.tour-highlight {
// 高亮區域樣式
// 包含脈衝動畫效果
&::after {
border: 2px solid #007bff;
animation: pulse 2s infinite;
}
}
// 脈衝動畫效果
@keyframes pulse {
0% {
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0.4);
}
70% {
box-shadow: 0 0 0 10px rgba(0, 123, 255, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(0, 123, 255, 0);
}
}提示框樣式
.tour-popup {
// 提示框基本樣式
background: rgba(255, 255, 255, 0.95);
backdrop-filter: blur(10px);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
// 不同位置的變體
&.placement-top {
transform-origin: bottom center;
}
&.placement-bottom {
transform-origin: top center;
}
&.placement-left {
transform-origin: right center;
}
&.placement-right {
transform-origin: left center;
}
}
.tour-header {
// 標題區域樣式
border-bottom: 1px solid #eee;
h3 {
font-size: 18px;
color: #333;
}
.close-button {
// 關閉按鈕樣式
opacity: 0.6;
&:hover {
opacity: 1;
}
}
}按鈕和進度指示器
.tour-button {
// 基本按鈕樣式
border: 1px solid #ddd;
background: white;
&:hover {
background: #f5f5f5;
}
&.primary {
// 主要按鈕樣式
background: linear-gradient(45deg, #007bff, #0056b3);
color: white;
box-shadow: 0 2px 4px rgba(0, 123, 255, 0.2);
&:hover {
background: linear-gradient(45deg, #0056b3, #004094);
transform: translateY(-1px);
}
}
}
.tour-progress {
.progress-dot {
// 進度點樣式
background: #ddd;
&.active {
background: linear-gradient(45deg, #007bff, #0056b3);
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.2);
}
}
}無障礙和動畫
// 無障礙焦點樣式
:focus {
outline: 2px solid #007bff;
outline-offset: 2px;
}
// 動畫效能優化
.tour-overlay,
.tour-popup,
.tour-highlight {
will-change: transform, opacity;
}自定義主題
你可以通過修改以下顏色和效果來自定義主題:
- 主色調:修改
#007bff為你的品牌色 - 背景透明度:調整
rgba(0, 0, 0, 0.4)的透明度 - 陰影效果:調整
box-shadow參數 - 動畫時間:修改
transition和animation的持續時間
鍵盤支援
→或Enter:下一步←:上一步Esc:關閉導覽
瀏覽器支援
- Chrome (最新版)
- Firefox (最新版)
- Safari (最新版)
- Edge (最新版)
注意事項
- 確保目標元素在導覽開始時是可見的
- 建議在元素完全渲染後再啟動導覽
- 使用
validate和beforeNext來確保更好的用戶體驗 - 考慮使用
ariaLabel來提供更好的無障礙支援
授權
MIT SHIH MING
聯絡與支援
問題回報
如果您發現任何問題或有改進建議,請透過以下方式聯繫我們:
- Email: [email protected]
- GitHub Issues: 提交問題
貢獻指南
我們歡迎任何形式的貢獻,包括但不限於:
- 代碼貢獻
- 文檔改進
- 問題回報
- 功能建議
