25 KiB
25 KiB
移除光栅卡硬件陀螺仪 · 设计方案
状态:待审核 · 创建日期:2026-07-06 · 目标分支:feat/actvity
本方案替代 2026-06-30 引入的"加速度计 + One-Euro 滤波"方案(commit
4eab42b)。旧 spec/plan 保留作为历史演进记录。
方案概述(必读)
要解决的问题
业务问题
- 铸爱光栅卡(H5 + App)的倾斜预览在 App 端需要硬件陀螺仪/加速度计权限,UX 摩擦大:
- iOS 13+ Safari / WKWebView 弹出
requestPermission,用户拒绝即不可用 - Android 12+ 强制
BODY_SENSORS权限,应用商店审核严格 - 部分低端 Android 机陀螺仪硬件缺失,只能降级
- iOS 13+ Safari / WKWebView 弹出
- 户外/静止场景下用户无法(或不想)倾斜手机预览,缺少替代交互
技术问题
- 硬件传感器路径有 3 套(Native.js Android / Native.js iOS / DeviceOrientationEvent),代码 ~28KB(
useLenticularStudioTilt.js) - 4 天前刚把陀螺仪改为加速度计(
4eab42b),整套 One-Euro 滤波、跳变拒绝、快慢双通道、预热跳过逻辑投入大、收益不明 - 原生插件
imengyu-UniAndroidGyro(含 AAR + iOS Framework)成为单点依赖 - App 端倾斜效果与触摸拖拽效果已经并存(
LenticularCard.vue的touchstart/move/end事件一直在 emitsimulate),铸爱页(lenticular-result)却用skipBuiltInTouch=true屏蔽触摸、强制走硬件路径
整体实现路径
| Phase | 内容 | 工作量 |
|---|---|---|
| Phase 1 | 在 useLenticularCraftTiltPreview 新增 simulateTilt / simulateTiltFromNormalized / simulateTiltReset 公共方法(保留旧路径并行) |
0.5 天 |
| Phase 2 | 新增 <ManualTiltSlider> 独立组件 |
0.5 天 |
| Phase 3 | <LenticularCard> / <HolographicCard> 加 showSlider / sliderValue prop + update:sliderValue / sliderReset emit(v-model 配套),引入 <ManualTiltSlider> |
0.5 天 |
| Phase 4 | lenticular-result.vue 切换:改触摸接收器、加滑块、删 3 处 scheduleTiltStart/stopTiltPreview 调用 |
0.5 天 |
| Phase 5 | 清理收尾:删 useLenticularStudioTilt.js、useOneEuroFilter.js、原生插件目录、manifest 注册段、权限与隐私描述 |
0.5 天 |
| QA | 4.3 验收清单(iOS 真机 / Android 真机 / H5 Chrome) | 1 天 |
| 合计 | ~3.5 天 |
关键决策
- 彻底移除硬件传感器代码(不保留"硬件 / 手动"切换开关)— 决策依据:CLAUDE.md MVP 先行原则,"应急开关"属于 YAGNI;用户已确认手动模拟可以替代硬件方案
- 滑块做成独立组件
<ManualTiltSlider>,与卡片解耦 — 便于未来页面级复用 - 触摸拖拽路径保留(
LenticularCard/HolographicCard已有),与滑块不互斥,数据驱动 - 离散档位逻辑保留(铸爱页 2 层卡硬切效果不动)— 与硬件源无关
- 删除原生插件目录
frontend/nativePlugins/imengyu-UniAndroidGyro/— 这是物理上解除单点依赖 - iOS
NSMotionUsageDescription同步删(在manifest.json)— 不删会导致应用商店审核仍有"访问运动传感器"声明 - 历史设计文档保留(2026-06-30 accel-oneeuro design/plan)— 作为设计演进记录,不删
核心架构图
┌────────────────────────────────────────────────────────────┐
│ 手动输入(双源) │
│ ┌────────────────────┐ ┌────────────────────────┐ │
│ │ LenticularCard.vue │ │ <ManualTiltSlider/> │ │
│ │ (触摸拖拽) │ │ (滑块 0..1) │ │
│ └─────────┬──────────┘ └───────────┬────────────┘ │
│ │ emit('simulate', x, y) │ change: v │
│ │ 归一化 -1..+1 │ 归一化 0..1 │
└────────────┼─────────────────────────────┼────────────────┘
▼ ▼
┌─────────────────────────────────────────────────────┐
│ useLenticularCraftTiltPreview │
│ │
│ simulateTiltFromNormalized(x, y) // 触摸入口 │
│ simulateTilt(dx, dy) // 滑块入口(度) │
│ simulateFromSignedDegrees(dx, dy) // 内部(度) │
│ simulate(gamma, beta) // 内部(-1..+1) │
│ ↓ │
│ 离散档位映射 (TILT_ACTIVATE/DEACTIVATE 滞回) │
│ ↓ │
│ useLenticularPreview 渲染循环 │
│ ↓ │
│ engine.feedSimulatedTilt() → layerTransforms │
└─────────────────────────────────────────────────────┘
▼
<LenticularCard :transforms />
<HolographicCard :viewAngle />
文档说明
- 适用范围:前端 uni-app 铸爱光栅卡模块(含全息卡);不影响后端、不影响其他业务
- 前置版本/历史:
4eab42b(2026-07-01) 引入加速度计 + One-Euro 滤波方案2855cd5之前使用陀螺仪10168f8AI 生成镭射卡功能889c923镭射新链路
- 工作量估算:约 3.5 天(开发 2.5 天 + QA 1 天)
- 目标读者:铸爱光栅卡模块的开发者、iOS/Android 打包配置维护者、QA 验收人
1. 架构与数据流
参见"核心架构图"小节。
数据流要点
- 彻底删除
useLenticularStudioTilt.js(28KB / 894 行)+useOneEuroFilter.js(~75 行) - API 统一入口:
useLenticularCraftTiltPreview暴露simulateTilt(dx, dy)(度)作为唯一倾斜入口 - 触摸路径:保留
LenticularCard.vue/HolographicCard.vue的触摸事件,把 emit 转发到simulateTiltFromNormalized(在useLenticularCraftTiltPreview内部做归一化 → 度数转换,转换系数 45°/单位) - 滑块路径:页面级
<LenticularCard show-slider v-model:slider-value="tiltValue" @slider-reset="tiltValue = 0.5" />(卡片内部嵌入<ManualTiltSlider>,所有权归调用方,0.5=中位 → 0°) - 铸爱页(lenticular-result):去掉
skipBuiltInTouch=true改为默认(false),去掉scheduleTiltStart调用(无传感器要启动了)
2. 文件变更清单
2.1 删除(3 项)
| 文件 / 目录 | 大小 | 删除理由 |
|---|---|---|
frontend/composables/useLenticularStudioTilt.js |
28KB / 894 行 | 整体硬件传感器模块彻底无用 |
frontend/composables/useOneEuroFilter.js |
~75 行 | 仅 StudioTilt 一处使用(grep 验证:仅 1 处 import) |
frontend/nativePlugins/imengyu-UniAndroidGyro/ |
含 AAR + iOS Framework + package.json | 原生插件无调用方后即可整体删除 |
2.2 新增(1 项)
| 文件 | 说明 |
|---|---|
frontend/components/lenticular/ManualTiltSlider.vue |
uniapp <slider> 包装组件,双向绑定 0..1,含复位按钮 |
2.3 修改(8 个文件)
| 文件 | 关键变更 |
|---|---|
frontend/composables/useLenticularCraftTiltPreview.js |
① 删 useLenticularStudioTilt 引用和 scheduleTiltStart/stopTiltPreview ② 新增公共方法 simulateTilt(dx, dy)(度)③ 新增 simulateTiltFromNormalized(x, y)(适配触摸)④ lockPreviewStill 保留 |
frontend/composables/useLenticularPreview.js |
改动最小:注释更新(移除"接受 simulate() 注入"的隐含承诺),内部逻辑不动 |
frontend/composables/useHolographicPreview.js |
删 startGyro/startDeviceOrientation 等所有 DeviceOrientationEvent 路径;保留 simulate(x, y) 作为唯一入口;移除 OneEuro 引用 |
frontend/components/lenticular/LenticularCard.vue |
① 新增 prop showSlider: Boolean,默认 false ② 新增 prop sliderValue: Number(0..1,双向)③ 模板内 v-if="showSlider" 渲染 <ManualTiltSlider> ④ 触摸开始/移动 emit simulate(x, y) 不变;触摸结束改为不再 emit (0,0)(与滑块数据驱动相容,见 4.1) |
frontend/components/lenticular/HolographicCard.vue |
同上,prop 一致;触摸结束同样不再 emit (0,0) |
frontend/components/lenticular/CastloveLenticularPreview.vue |
加 show-slider prop 透传 |
frontend/pages/castlove/lenticular/lenticular-result.vue |
① 删 scheduleTiltStart/stopTiltPreview 调用(3 处)② 删 :skip-built-in-touch="true",改用触摸 emit ③ 加 show-slider prop ④ @simulate 改为 (x, y) => simulateTiltFromNormalized(x, y) |
frontend/manifest.json |
① 删 app-plus.nativePlugins.imengyu-UniAndroidGyro 段(30-44 行)② 删 BODY_SENSORS 权限(69 行)③ 删 NSMotionUsageDescription 隐私描述(82 行) |
| 构建设置 | 无(iOS Info.plist 由打包机自动生成,修改 manifest.json 即可生效) |
2.4 不修改但需回归验证
frontend/pages/castlove/lenticular/lenticular-create.vue:不挂载 LenticularCard 实际渲染,无需改frontend/pages/castlove/lenticular/lenticular-thinking.vue:同上frontend/utils/lenticular-engine.js:渲染逻辑与输入源无关,无需改
3. API 设计
3.1 useLenticularCraftTiltPreview(layersRef)
返回对象:
| 字段 | 类型 | 状态 | 说明 |
|---|---|---|---|
physics |
Reactive<PhysicsConfig> |
保留 | 物理参数 |
layerTransforms |
Ref<Object> |
保留 | 渲染输出(直接喂给 <LenticularCard :transforms>) |
simulate(gamma, beta) |
(number, number) => void |
保留 | 内部用,归一化输入 |
relax(factor) |
(number=0.85) => void |
保留 | 平滑归零 |
lockPreviewStill() |
() => void |
保留 | 强制回中(清空离散档位状态 lastLayerIdx / snapCooldownUntil / dirSign / dirOppositeStreak + simulate(0, 0))。使用场景:进入页面初始化 / 用户主动点"归位"按钮 / 切换卡类型时。不再由 scheduleTiltStart 触发(Phase 4 之后该调用一并删除)。 |
simulateTilt(dx, dy) |
(number, number) => void |
新增 | 滑块/外部入口,单位度 |
simulateTiltFromNormalized(x, y) |
(number, number) => void |
新增 | 触摸入口,-1..+1 转 °(系数 45°) |
simulateTiltReset() |
() => void |
新增 | 滑块点"复位"时调用 |
gyroSourceLabel |
Ref<string> |
删除 | 不再有硬件源 |
scheduleTiltStart |
() => void |
删除 | 不再启动传感器 |
stopTiltPreview |
() => void |
删除 | 同上 |
内部状态:lastLayerIdx、snapCooldownUntil、dirSign、dirOppositeStreak 全保留(离散档位逻辑不动)。
3.2 useHolographicPreview()
| 字段 | 类型 | 状态 |
|---|---|---|
physics |
Reactive |
保留 |
viewAngle |
Reactive{x,y,z} |
保留 |
isWebGLReady, hasWebGLError, fps |
Ref |
保留 |
simulate(x, y) |
(number, number) => void |
保留(成为唯一驱动入口) |
relax(factor) |
保留 | |
onWebGLReady/onWebGLError/onFPSUpdate |
保留 | |
startGyro/stopGyro |
删除(设备方向监听整套删) | |
gyroSource |
删除 | |
accelHandler, accelBaselineReady, accelBaseX/Y |
删除(基线追踪是 DeviceOrientationEvent 配套) | |
detectPerformanceTier, HOLO_PERFORMANCE_PRESETS |
保留(独立工具函数) |
3.3 <LenticularCard> / <HolographicCard> 新增 props
defineProps({
// ... 既有 props ...
skipBuiltInTouch: { type: Boolean, default: false }, // 保留
showSlider: { type: Boolean, default: false }, // 新增:是否显示内置滑块
sliderValue: { type: Number, default: 0.5 }, // 新增:v-model:sliderValue 双向绑定
})
const emit = defineEmits([
'simulate', // 保留:触摸拖拽 emit (x, y) -1..+1
'update:sliderValue', // 新增:v-model 配套(v-model:sliderValue 双向绑定)
'sliderReset', // 新增:滑块复位按钮 emit ()
])
emit 转发逻辑(卡片内部):
<ManualTiltSlider
v-if="showSlider"
:value="sliderValue"
@change="v => emit('update:sliderValue', v)"
@reset="emit('sliderReset')"
/>
gyroSourceprop 保留但写死为'simulation'(不删 prop 以避免破坏外部传参,向后兼容,仅作为显示字段,不参与业务逻辑)- 滑块所有权归调用方:调用方使用
v-model:sliderValue="tiltValue"双向绑定,滑块拖动时自动更新tiltValue;调用方再将tiltValue通过simulateTilt喂入 composable sliderResetemit 后,调用方负责将tiltValue重置为 0.5
3.4 <ManualTiltSlider> 新组件 API
// 文件:frontend/components/lenticular/ManualTiltSlider.vue
defineProps({
value: { type: Number, default: 0.5, validator: v => v >= 0 && v <= 1 },
disabled: { type: Boolean, default: false },
showReset: { type: Boolean, default: true },
min: { type: Number, default: 0 },
max: { type: Number, default: 1 },
step: { type: Number, default: 0.01 },
})
const emit = defineEmits<{
change: [v: number] // 用户拖动
reset: [] // 点复位
}>()
- 视觉:卡片下方 32rpx 高度的细条 + 居中圆点 thumb + 右侧小图标"↺"
- 平台:使用
<slider>uniapp 组件(App/H5/小程序都支持,原生渲染,跟手)
3.5 数值约定一致性
| 上下文 | 范围 | 备注 |
|---|---|---|
| 触摸 emit | 实际 [-1.1, +1.1],有效 [-1, +1] | 现有 LenticularCard.dispatch() 末尾 * 1.1 系数放大(line 183-184),允许轻微超出;模拟路径中由 3.5 公式截断到 [-45°, +45°] |
| 滑块 value | 0..1 | 中位 0.5 = 0° |
simulateTilt(dx, dy) |
-45..+45 度 | 物理量纲,与旧 simulateFromSignedDegrees 一致(推导:触摸 x∈[-1,+1]×45° 与滑块 v∈[0,1]×(±45°)) |
simulate(gamma, beta) |
-1..+1 | 引擎层归一化 |
| 滑块 → 度 | 公式 | dx = (v - 0.5) * 90(v ∈ [0,1] → dx ∈ [-45°, +45°]) |
| 触摸 → 度 | 公式 | dx = x * 45(x ∈ [-1, +1] → dx ∈ [-45°, +45°]) |
4. UI + 错误处理 + 测试
4.1 滑块 UI 形态
┌────────────────────────────────────────────────┐
│ │
│ ┌──────────────────┐ │
│ │ │ │
│ │ LenticularCard │ ← 触摸区 │
│ │ │ (emit simulate) │
│ │ │ │
│ └──────────────────┘ │
│ │
│ ↺ [────●──────────] │
│ 0 0.5 1 │
└────────────────────────────────────────────────┘
| 元素 | 规格 |
|---|---|
| 位置 | 卡片下方 16rpx 间距,水平居中 |
| 高度 | 48rpx |
| 滑块本体 | <slider> uniapp 组件,min=0 / max=1 / step=0.01 |
| Thumb | 圆点 24rpx 直径,主题色高亮 |
| 复位按钮 | 左侧 ↺ 图标 32rpx,点击 emit sliderReset |
| 颜色变量 | 与 LenticularCard 主题色一致(透传 prop 或写死 #6E7AFF) |
两种输入源的优先级规则:
- 触摸拖拽 + 滑块不互斥:都向同一
simulateTilt*入口打,最后一次操作生效 - 触摸结束 emit
(0, 0),会盖掉滑块当前位置 → 这是 bug - 修复方案:触摸结束 emit 改为不发送
(0,0),改由调用方决定是否relax(0.86),或lockPreviewStill() - 滑块拖动时也类似:不在拖动结束时 emit reset,完全由数据驱动(滑块 0.5 = 中位 = 不倾斜)
4.2 错误处理 / 边界场景
| 场景 | 处理 |
|---|---|
skipBuiltInTouch=true + showSlider=true |
都允许,触摸被屏蔽,滑块生效 |
skipBuiltInTouch=false + 触摸与滑块同时操作 |
最后一次事件胜出,无须加锁 |
触摸事件 e.touches 为空(changedTouches 也为空) |
现有代码已用 if (!touch) return 兜底,保留 |
| 滑块 value 超出 0..1(外部 bug) | 组件 prop validator 拦截,console.warn |
simulateTilt 收到 NaN/Infinity |
入口处 Number.isFinite() 检查,否则 console.warn 丢弃 |
layersRef 长度为 0 或 1 |
离散档位逻辑已有 if (count < 2) return 兜底,保留 |
| App 端 WebView 触摸延迟 | uniapp <slider> 原生组件,跟手性 OK;触摸事件延迟是 uniapp 通用问题,沿用现状 |
| 旧硬件传感器代码残留 | useLenticularStudioTilt.js 整体删除,无残留风险 |
Phase 5 漏删 BODY_SENSORS 权限 |
§2.3 已列入修改清单;QA 用附录 grep 命令兜底校验 |
Phase 5 漏删 iOS NSMotionUsageDescription |
§2.3 已列入修改清单;QA 用附录 grep 命令兜底校验 |
4.3 测试策略
前端测试原则(来自 memory share-impl-test-policy.md):前端不写 Vitest,走手动验收 + 后端接口测试。
| 类型 | 覆盖范围 | 方法 |
|---|---|---|
| 后端接口测试 | 不涉及本次(无后端变更) | — |
| 前端手动验收 | 所有修改页面(iOS 真机 / Android 真机 / H5 Chrome) | 见下方验收清单 |
| 单元验证 | useLenticularCraftTiltPreview 的 simulateTilt / simulateTiltFromNormalized / 离散档位逻辑 |
在 DevTools console 用 console.log 直接调用验证(不再单独写测试文件) |
| 平台覆盖 | iOS 真机、Android 真机、H5 Chrome | QA 走查 |
手动验收清单(每平台各一遍):
- 打开
lenticular-result页面,确认卡片显示正常 - 触摸拖拽:手指在卡片上左右拖动,卡片光栅效果跟随;松开手指不强制回中(验证 4.1 修复)
- 滑块拖动:拖动滑块 0 → 1,卡片光栅从最左切到最右(铸爱页 2 层卡应硬切档位)
- 复位按钮:点击 ↺,滑块回 0.5,卡片回中间
- 组合:拖动滑块到 0.3,然后触摸卡片到最右 → 触摸胜出
manifest.json中BODY_SENSORS/NSMotionUsageDescription/nativePlugins.imengyu-UniAndroidGyro已删除(grep 验证)- 旧 iOS 用户授权提示不再弹出(验证 DeviceOrientationEvent 已删干净)
- Android 真机扫描:APP 启动时不再申请 BODY_SENSORS(adb logcat | grep SENSORS)
lenticular-create/lenticular-thinking页面无回归(这两个页面无 LenticularCard 实际渲染,但要确认不报错)
5. 实施顺序 + 风险 + 回退
5.1 实施顺序(5 个阶段,每阶段独立可回退)
| Phase | 内容 | 涉及文件 | 风险 | 验证 |
|---|---|---|---|---|
| 1. 底层 API | 在 useLenticularCraftTiltPreview 新增 simulateTilt / simulateTiltFromNormalized / simulateTiltReset,保留旧 scheduleTiltStart 等方法(先并行不删) |
1 文件 | 极低(纯新增) | 单元调用验证 |
| 2. 新组件 | 新增 ManualTiltSlider.vue,独立可单测 |
1 新文件 | 极低 | H5 Chrome 单独挂载测 |
| 3. 卡片接入 | LenticularCard / HolographicCard 加 showSlider/sliderValue prop + update:sliderValue/sliderReset emit(v-model 配套),引入 <ManualTiltSlider> |
2 文件 | 中(prop 增减可能影响外部调用方) | lenticular-result 页面挂载验证 |
| 4. 页面切换 | lenticular-result.vue:① 改 @simulate 接收器 ② 加 show-slider ③ 删 scheduleTiltStart 等 3 处调用 |
1 文件 | 中 | 触摸 + 滑块组合验收 |
| 5. 清理收尾 | ① 删 useLenticularStudioTilt.js ② 删 useOneEuroFilter.js ③ 删原生插件目录 frontend/nativePlugins/imengyu-UniAndroidGyro/ ④ 删 manifest.json 中 nativePlugins 注册段、BODY_SENSORS、NSMotionUsageDescription ⑤ PR 描述提示 |
3 文件 + 1 目录 | 中(删除 28KB + 改 manifest) | grep 残留 + 真机无传感器调用 |
关键不变量:Phase 1-4 完成后仍然可回退到当前状态(旧 scheduleTiltStart 路径保留运行);只有 Phase 5 是真正不可逆。
5.2 风险点
| 风险 | 等级 | 缓解措施 |
|---|---|---|
useLenticularStudioTilt.js 删除后存在隐藏引用 |
高 | Phase 5 之前全局 grep useLenticularStudioTilt / useOneEuroFilter / BODY_SENSORS / NSMotionUsageDescription / imengyu-UniAndroidGyro 必须 0 命中;CI 加 grep 校验 |
触摸结束 emit (0,0) 导致与滑块状态打架 |
中 | 已在 4.1 给出修复方案(触摸结束不强制发 0,0,由数据驱动) |
HolographicCard.vue 当前无页面引用 |
低 | 按"全套都改"原则同步清理,不单独删除该组件(可能未来用) |
4eab42b 新增的两个 accel-oneeuro 文档变成历史 |
低 | 保留不删(作为设计演进记录),但在新 spec 开头加"本方案替代 2026-06-30 accel-oneeuro 方案"说明 |
iOS NSMotionUsageDescription 不在仓库 |
中 | PR 描述 / iOS 开发者文档显式提示 |
Android BODY_SENSORS 权限是 4eab42b 才加的 |
低 | manifest 改动是单点,git blame 清晰 |
| 触屏 + 滑块同时操作的边界 | 中 | 设计上接受"最后一次胜出",无锁;4.2 已说明 |
| 真机回归范围 | 中 | iOS/Android/H5 三平台均需过 4.3 验收清单 |
| 设计师未确认滑块视觉 | 低 | 4.1 已给出 ASCII mock + 规格,UI 沿用主题色即可 |
滑块 emit 命名(update:sliderValue)与 simulate 风格不同 |
极低 | 滑块用 Vue 3 标准 v-model 模式(update:sliderValue),与现有 simulate(x, y) 风格不同但符合 Vue 约定;§3.3 命名表已标注 |
5.3 兼容回退方案
| 阶段 | 回退方式 | 粒度 |
|---|---|---|
| Phase 1 出问题 | revert 单 commit | 1 文件 |
| Phase 2 出问题 | 删除新文件 | 1 文件 |
| Phase 3 出问题 | revert prop 改动 | 2 文件 |
| Phase 4 出问题 | revert 页面改动 | 1 文件 |
| Phase 5 出问题 | revert 删除 + manifest | 3 文件 + 需回补权限字符串 + 恢复插件目录 |
| 整体回退 | git revert <merge-commit> |
一次到位 |
应急开关(可选,不实施除非 QA 需要):
- 保留一个
LENTICULAR_USE_HARDWARE_GYRO全局开关,默认false - 如果线上发现严重问题,远程打开临时回退到旧路径
- 决定:不实现。理由:CLAUDE.md MVP 先行原则 + 28KB 代码就为"应急"留着属于 YAGNI。Phase 1-4 并行保留旧路径已经足够
5.4 范围确认
| 维度 | 范围 |
|---|---|
| 包含 | 5 个 composables(删 2 改 3:删 useLenticularStudioTilt + useOneEuroFilter,改 useLenticularCraftTiltPreview + useLenticularPreview + useHolographicPreview)+ 1 新组件 + 1 原生插件目录(删)+ 2 卡片组件(LenticularCard + HolographicCard)+ 1 包装预览组件(CastloveLenticularPreview)+ 1 页面 + manifest |
| 包含 | 1 个新 spec 文档 docs/superpowers/specs/2026-07-06-remove-hardware-gyro-design.md(本文件) |
| 不包含 | 后端代码(无后端变更) |
| 不包含 | lenticular-create.vue / lenticular-thinking.vue(无 LenticularCard 实际渲染) |
| 不包含 | 删除 4eab42b 的 accel-oneeuro 设计/实施文档(保留为历史记录) |
| 不包含 | 重构 LenticularEngine 渲染逻辑(本次不改) |
| 不包含 | "未来接回硬件"的接口预留(YAGNI) |
5.5 文档同步
- 新增:
docs/superpowers/specs/2026-07-06-remove-hardware-gyro-design.md(本文件) - 保留:
docs/superpowers/specs/2026-06-30-lenticular-android-accel-oneeuro-design.md(历史) - 保留:
docs/superpowers/plans/2026-06-30-android-accel-oneeuro-implementation.md(历史) - 保留:
docs/superpowers/specs/2026-06-26-lenticular-webgl-engine-design.md(WebGL 引擎与倾斜源无关) - 保留:
docs/superpowers/specs/2026-05-22-lenticular-gyro-optimization-design.md(原始陀螺仪方案)
附:grep 校验清单(Phase 5 前必跑)
# 应全部 0 命中
grep -rn "useLenticularStudioTilt" frontend/
grep -rn "useOneEuroFilter" frontend/
grep -rn "imengyu-UniAndroidGyro" frontend/
grep -rn "BODY_SENSORS" frontend/
grep -rn "NSMotionUsageDescription" frontend/
grep -rn "imengyu" frontend/