web-step-counter-pro 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +42 -10
  2. package/package.json +1 -1
  3. package/step-counter.js +60 -11
package/README.md CHANGED
@@ -35,18 +35,46 @@ document.getElementById('btn').onclick = () => {
35
35
 
36
36
  初始化时可传入配置对象:`new WebStepCounter(config)`
37
37
 
38
+ ### 参数优化说明
39
+
40
+ 默认参数基于市面上通用步行检测标准优化:
41
+ - **正常步行**:60-120步/分钟(1-2步/秒),步间隔500-1000ms
42
+ - **快走/慢跑**:120-180步/分钟(2-3步/秒),步间隔330-500ms
43
+ - **慢走场景**:支持最慢0.67步/秒的步速
44
+
45
+ 这些参数能够有效支持:
46
+ ✅ 手机放在口袋里走路(任意姿态,包括平放)
47
+ ✅ 手机拿在手里走路(竖握/平握/斜握)
48
+ ✅ 室内/地下等弱GPS信号场景
49
+ ✅ 快走、慢走、正常步行等多种步速
50
+
51
+ ### 智能姿态检测
52
+
53
+ 采用**姿态变化检测**算法,精准区分正常走路和作弊行为:
54
+
55
+ | 场景 | 姿态特征 | 判定结果 |
56
+ |------|---------|---------|
57
+ | 🚶 口袋走路 | 水平放置 + 姿态持续波动 | ✅ 正常计步 |
58
+ | 🤚 手持走路 | 任意角度 + 姿态持续波动 | ✅ 正常计步 |
59
+ | 🎯 拍桌子作弊 | 水平放置 + 姿态几乎静止 | ❌ 静默过滤 |
60
+
61
+ **核心原理**:真实走路时,即使手机水平放置,由于身体晃动也会产生持续的姿态变化;而拍桌子时手机姿态几乎不变。
62
+
38
63
  | 属性 | 类型 | 默认值 | 说明 |
39
64
  | :--- | :--- | :--- | :--- |
40
65
  | `sensitivity` | Number | `12.0` | 加速度灵敏度阈值 (m/s²)。数值越小越灵敏,但也越容易误判。 |
41
- | `minStepInterval` | Number | `330` | 最小步频间隔 (ms)。小于此间隔视为震动干扰。 |
42
- | `maxStepInterval` | Number | `2000` | 最大步频间隔 (ms)。超过此间隔视为停顿。 |
43
- | `minConsecutiveSteps`| Number | `7` | 连续步数门槛。连续走满 N 步才开始计入总数,防止误触。 |
44
- | `cheatCheckInterval` | Number | `5000` | 防作弊检查周期 (ms)。每隔多久检查一次 GPS 和姿态。 |
45
- | `gpsAccuracyLimit` | Number | `20` | GPS 精度要求 (米)。精度低于此值时不进行 GPS 相关的防作弊判定。 |
66
+ | `minStepInterval` | Number | `300` | 最小步频间隔 (ms)。对应最快3.3步/秒,覆盖快走和慢跑场景。 |
67
+ | `maxStepInterval` | Number | `1500` | 最大步频间隔 (ms)。对应最慢0.67步/秒,适应慢走场景。 |
68
+ | `minConsecutiveSteps`| Number | `4` | 连续步数门槛。连续走满 N 步才开始计入总数,防止误触。 |
69
+ | `cheatCheckInterval` | Number | `15000` | 防作弊检查周期 (ms)。每隔15秒检查一次 GPS,减少对正常运动的干扰。 |
70
+ | `gpsAccuracyLimit` | Number | `150` | GPS 精度要求 (米)。兼容室内、地下等弱信号场景。 |
71
+ | `accelerationThreshold` | Number | `1.2` | 加速度变化阈值 (m/s²)。匹配正常步行的垂直加速度变化范围。 |
46
72
  | `maxSpeed` | Number | `2.8` | 最大允许速度 (m/s)。约 10km/h,超过此速度不计步。 |
73
+ | `orientationVarianceThreshold` | Number | `3.0` | 姿态变化方差阈值。用于区分"拍桌子"和"口袋走路",方差越小越可能是静止作弊。 |
47
74
  | `debug` | Boolean | `false` | 是否在控制台打印详细调试日志。 |
75
+ | `autoPauseOnBackground` | Boolean | `true` | Capacitor 环境下切后台时自动暂停传感器以省电。 |
48
76
  | `onStep` | Function | `null` | 步数更新回调 `(steps) => void` |
49
- | `onStatus` | Function | `null` | 状态/作弊回调 `(msg, type) => void` |
77
+ | `onStatus` | Function | `null` | 状态/作弊回调 `(msg, type, code) => void` |
50
78
  | `onSensorData` | Function | `null` | 传感器原始数据回调 `(acc, orient) => void` |
51
79
 
52
80
  ---
@@ -68,7 +96,7 @@ document.getElementById('btn').onclick = () => {
68
96
  ### `requestOrientationPermission(): Promise<boolean>`
69
97
  手动请求设备方向权限的辅助方法。
70
98
  * 用于解决 iOS 上某些情况下 Orientation 权限未被正确授予的问题。
71
- * 建议在 UI 上提供一个“修复权限”按钮调用此方法。
99
+ * 建议在 UI 上提供一个"修复权限"按钮调用此方法。
72
100
 
73
101
  ---
74
102
 
@@ -94,13 +122,17 @@ document.getElementById('btn').onclick = () => {
94
122
  | `"检测到机械节奏"` | 步频方差极低 (摇步机) | 最近 20 步的时间间隔标准差 `< 5ms` |
95
123
  | `"检测到原地运动 (GPS)"` | 步数增加但 GPS 未移动 | `StepsDiff > 15` 且 `GPSDistance < 3m` |
96
124
  | `"速度过快 (xx km/h)"` | 移动速度超过阈值 | `Speed > 10 km/h` (判定为骑车/开车) |
97
- | *(静默丢弃)* | 手机水平放置/拍桌子 | `Beta < 5°` `Gamma < 5°` |
125
+ | *(静默丢弃)* | 拍桌子作弊 | 水平放置 + 姿态方差 `< 3.0` (姿态几乎静止) |
98
126
 
99
127
  ---
100
128
 
101
129
  ## 最佳实践
102
130
 
103
131
  1. **HTTPS**: 必须在 HTTPS 环境下使用,否则现代浏览器拒绝访问传感器。
104
- 2. **iOS 权限**: 务必设计引导 UI,提示用户在弹窗中点击“允许”。
105
- 3. **UI 反馈**: 当 `type === 'cheating'` 时,建议在 UI 上显示醒目的红色警告,提示用户“步数可能无效”。
132
+ 2. **iOS 权限**: 务必设计引导 UI,提示用户在弹窗中点击"允许"。
133
+ 3. **UI 反馈**: 当 `type === 'cheating'` 时,建议在 UI 上显示醒目的红色警告,提示用户"步数可能无效"。
134
+ 4. **姿态检测**: 智能姿态检测算法已内置,无需额外配置。它能自动区分"拍桌子"和"口袋走路",支持手机在任意姿态下正常计步。
135
+ 5. **参数调优**: 若在特殊场景下出现误判,可适当调整 `orientationVarianceThreshold` 参数(默认3.0):
136
+ - 增大该值(如5.0):更严格,可能误判部分缓慢行走场景
137
+ - 减小该值(如1.5):更宽松,可能漏过部分拍桌子作弊
106
138
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "web-step-counter-pro",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "A professional-grade web step counting library with advanced anti-cheat mechanisms (GPS, Gyroscope, Rhythm Analysis).",
5
5
  "main": "step-counter.js",
6
6
  "type": "module",
package/step-counter.js CHANGED
@@ -39,17 +39,21 @@ export class WebStepCounter {
39
39
  };
40
40
 
41
41
  constructor(config = {}) {
42
- // ... (保持原有构造函数逻辑,但推荐使用常量)
42
+ // 基于市面上通用步行检测标准优化的参数
43
+ // 正常步行:60-120步/分钟(1-2步/秒),步间隔500-1000ms
44
+ // 快走/慢跑:120-180步/分钟(2-3步/秒),步间隔330-500ms
43
45
  this.config = {
44
- sensitivity: 12.0,
45
- minStepInterval: 330,
46
- maxStepInterval: 2000,
47
- minConsecutiveSteps: 7,
48
- cheatCheckInterval: 5000,
49
- gpsAccuracyLimit: 20,
50
- maxSpeed: 2.8,
46
+ sensitivity: 12.0, // 灵敏度,匹配正常步行的加速度变化(0.8-2.0 m/s²)
47
+ minStepInterval: 300, // 最小步间隔300ms,对应最快3.3步/秒,覆盖快走和慢跑场景
48
+ maxStepInterval: 1500, // 最大步间隔1500ms,对应最慢0.67步/秒,适应慢走场景
49
+ minConsecutiveSteps: 4, // 连续4步才开始计数,避免误触和抖动
50
+ cheatCheckInterval: 15000, // 延长检查间隔到15秒,减少对正常运动的干扰
51
+ gpsAccuracyLimit: 150, // GPS精度限制150米,兼容室内、地下等弱信号场景
52
+ accelerationThreshold: 1.2, // 加速度阈值1.2 m/s²,匹配正常步行的垂直加速度变化
53
+ maxSpeed: 2.8, // 最大允许速度2.8 m/s(约10 km/h)
54
+ orientationVarianceThreshold: 3.0, // 姿态变化阈值:方差小于此值视为姿态静止(可能是拍桌子)
51
55
  debug: false,
52
- autoPauseOnBackground: true, // 新增: Capacitor 环境下切后台自动暂停传感器
56
+ autoPauseOnBackground: true, // Capacitor 环境下切后台自动暂停传感器
53
57
  onStep: null,
54
58
  onStatus: null,
55
59
  onSensorData: null,
@@ -77,6 +81,7 @@ export class WebStepCounter {
77
81
  this.orientationValid = true;
78
82
 
79
83
  this.orientationData = { alpha: 0, beta: 0, gamma: 0 };
84
+ this.orientationHistory = []; // 用于检测姿态变化
80
85
 
81
86
  // 节流控制
82
87
  this._lastUiUpdate = 0;
@@ -252,6 +257,7 @@ export class WebStepCounter {
252
257
  return;
253
258
  }
254
259
 
260
+ // 智能姿态检测:区分"拍桌子"和"口袋走路"
255
261
  if (!this._checkOrientationValidity()) {
256
262
  // 静默丢弃,不频繁刷日志
257
263
  return;
@@ -294,7 +300,7 @@ export class WebStepCounter {
294
300
  if (val > max) max = val;
295
301
  }
296
302
  const range = max - min;
297
- if (range < 2.0) return;
303
+ if (range < this.config.accelerationThreshold) return;
298
304
 
299
305
  const currentThreshold = (min + max) / 2;
300
306
  const midIndex = Math.floor(this.accBuffer.length / 2);
@@ -342,7 +348,50 @@ export class WebStepCounter {
342
348
 
343
349
  _checkOrientationValidity() {
344
350
  const { beta, gamma } = this.orientationData;
345
- return !(Math.abs(beta) < 5 && Math.abs(gamma) < 5);
351
+
352
+ // 记录最近的姿态数据(最多保存20个样本,约1秒的数据)
353
+ this.orientationHistory.push({ beta, gamma, timestamp: Date.now() });
354
+ if (this.orientationHistory.length > 20) {
355
+ this.orientationHistory.shift();
356
+ }
357
+
358
+ // 样本不足时,暂不判断
359
+ if (this.orientationHistory.length < 10) {
360
+ return true;
361
+ }
362
+
363
+ // 检查当前姿态是否接近水平(可能是桌面放置)
364
+ const isHorizontal = Math.abs(beta) < 15 && Math.abs(gamma) < 15;
365
+
366
+ if (!isHorizontal) {
367
+ // 手机明显倾斜,肯定不是平放拍桌子,直接通过
368
+ return true;
369
+ }
370
+
371
+ // 手机接近水平状态,需要进一步判断是"拍桌子"还是"口袋走路"
372
+ // 计算姿态变化的方差
373
+ const betaValues = this.orientationHistory.map(o => o.beta);
374
+ const gammaValues = this.orientationHistory.map(o => o.gamma);
375
+
376
+ const betaVariance = this._calculateVariance(betaValues);
377
+ const gammaVariance = this._calculateVariance(gammaValues);
378
+ const totalVariance = betaVariance + gammaVariance;
379
+
380
+ // 如果姿态几乎不变化(方差很小),且处于水平位置,则可能是拍桌子
381
+ if (totalVariance < this.config.orientationVarianceThreshold) {
382
+ // 姿态静止 + 水平放置 = 疑似拍桌子作弊
383
+ return false;
384
+ }
385
+
386
+ // 姿态有变化,说明人在走动,允许计步
387
+ return true;
388
+ }
389
+
390
+ _calculateVariance(values) {
391
+ if (values.length === 0) return 0;
392
+ const mean = values.reduce((a, b) => a + b, 0) / values.length;
393
+ const variance = values.reduce((sum, val) => sum + Math.pow(val - mean, 2), 0) / values.length;
394
+ return variance;
346
395
  }
347
396
 
348
397
  _checkMechanicalRhythm(interval) {