@ray-js/ipc-player-integration 0.0.50-beta.0 → 0.0.50-beta.2

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 (38) hide show
  1. package/README.md +11 -11
  2. package/lib/ctx/ctx.composition.js +5 -5
  3. package/lib/features/initPlayerWidgets/index.d.ts +0 -3
  4. package/lib/features/initPlayerWidgets/index.js +1 -2
  5. package/lib/hooks/useBattery/index.js +7 -1
  6. package/lib/iconfont/iconfont.css +127 -7
  7. package/lib/iconfont/iconfont.js +10 -10
  8. package/lib/iconfont/iconfont.json +233 -23
  9. package/lib/iconfont/iconfont.ttf +0 -0
  10. package/lib/iconfont/iconfont.woff +0 -0
  11. package/lib/iconfont/iconfont.woff2 +0 -0
  12. package/lib/interface.d.ts +1 -1
  13. package/lib/interface.js +1 -1
  14. package/lib/ports.output.d.ts +13 -0
  15. package/lib/ports.output.js +13 -0
  16. package/lib/ui/bottomLeftContent.js +141 -27
  17. package/lib/ui/bottomRightContent.d.ts +6 -1
  18. package/lib/ui/bottomRightContent.js +43 -5
  19. package/lib/ui/ui.js +58 -4
  20. package/lib/ui/ui.less +17 -0
  21. package/lib/widgets/battery/battery.js +11 -3
  22. package/lib/widgets/battery/battery.less +11 -0
  23. package/lib/widgets/battery/batteryFull.js +11 -3
  24. package/lib/widgets/zoomTurntable/constants.d.ts +44 -12
  25. package/lib/widgets/zoomTurntable/constants.js +61 -17
  26. package/lib/widgets/zoomTurntable/lensModel.test.js +1 -1
  27. package/lib/widgets/zoomTurntable/lensSkill.d.ts +1 -1
  28. package/lib/widgets/zoomTurntable/lensSkill.js +1 -1
  29. package/lib/widgets/zoomTurntable/turntable/index.js +4 -1
  30. package/lib/widgets/zoomTurntable/turntable/rjs/index.rjs +138 -9
  31. package/lib/widgets/zoomTurntable/turntable/rjs/index.tyml +1 -0
  32. package/lib/widgets/zoomTurntable/useLensZoom.d.ts +3 -4
  33. package/lib/widgets/zoomTurntable/useLensZoom.js +94 -12
  34. package/lib/widgets/zoomTurntable/zoomTurntableDock.js +76 -4
  35. package/lib/widgets/zoomTurntable/zoomTurntableEntry.d.ts +0 -3
  36. package/lib/widgets/zoomTurntable/zoomTurntableEntry.js +20 -5
  37. package/lib/widgets/zoomTurntable/zoomTurntableEntry.less +11 -1
  38. package/package.json +2 -2
@@ -5,8 +5,16 @@ import { PlayState } from '../../interface';
5
5
  import { useDpState } from '../../hooks/useDpState';
6
6
  import { setScaleMultipleEvent, zoomLevelChange } from '../../ui/constant';
7
7
  import { MOCK_ZOOM_SKILL, MULTI_ZOOM_DP_CODE, dumpChannelApis, isChannelChangeSupported, isZoomSkillValid, lensCount, parseZoomSkill, queryCurrentLens, showSmallMonitor, subscribeChannelChange, switchLens } from './lensSkill';
8
- import { DEFAULT_STEP, D_MIN, buildMarks, buildTickValues, indexOfLensId, matchLens, maxScaleMultiple, toDisplayZoom, toScaleMultiple, zoomBounds } from './lensModel';
9
- import { CHANNEL_SETTLE_TIMEOUT, D_MATCH_EPS, GESTURE_SWITCH_DEBOUNCE, SWITCH_LENS_TIMEOUT, zoomTurntableLensChange, zoomTurntableZoomChange } from './constants';
8
+ import { DEFAULT_STEP, D_MIN, buildMarks, buildTickValues, indexOfLensId, lensRange, matchLens, maxScaleMultiple, toDisplayZoom, toScaleMultiple, zoomBounds } from './lensModel';
9
+ import { CHANNEL_SETTLE_TIMEOUT, D_MATCH_EPS, GESTURE_SWITCH_DEBOUNCE, SWITCH_LENS_TIMEOUT, ZOOM_PREVIEW_THROTTLE, zoomTurntableLensChange, zoomTurntableSwitching, zoomTurntableZoomChange } from './constants';
10
+ /**
11
+ * 内部调试开关:置 true 时用 MOCK_ZOOM_SKILL 顶替 ipc_multi_zoom 的上报值。
12
+ *
13
+ * 只在没有该 DP 的设备上自测转盘时临时打开,**提交前必须回到 false**。
14
+ * 不做成 props:mock 是组件内部的调试手段,不该出现在对外 API 上。
15
+ */
16
+ const USE_MOCK_ZOOM_SKILL = false;
17
+
10
18
  /**
11
19
  * 最近一次的本地数字缩放 d,按 devId 记录。
12
20
  * 放模块级:Dock 动态挂载/卸载,组件内 ref 存不住,但它只是派生中间量,不值得占一个 atom。
@@ -57,7 +65,6 @@ export const useLensZoom = options => {
57
65
  devId,
58
66
  event,
59
67
  showZoomTurntable = false,
60
- mockZoomSkill,
61
68
  skipLoad = false,
62
69
  setZoomSkill,
63
70
  setZoomLensIndex,
@@ -82,24 +89,48 @@ export const useLensZoom = options => {
82
89
 
83
90
  /** 画面绝对倍率;挂载时按已记录的 d 恢复读数 */
84
91
  const [curM, setCurM] = useState(() => zoomSkill ? toDisplayZoom(zoomSkill, zoomLensIndex, getLastD(devId)) : D_MIN);
92
+ /**
93
+ * 正在切往的目标镜头下标(-1 表示没有切换进行中)。
94
+ * 切换真空期里镜头下标还没动,倍率 tab 要靠它把高亮提前落到目标镜头,
95
+ * 免得原镜头的 tab 上冒出目标镜头的数值。
96
+ */
97
+ const [switchingIndex, setSwitchingIndex] = useState(-1);
85
98
  /** 拖动中:屏蔽回读,避免指针被拽回 */
86
99
  const draggingRef = useRef(false);
87
100
  /** 手势缩放跨临界点判定的 trailing 防抖 */
88
101
  const gestureTimerRef = useRef();
102
+ /** 拖动中连续下发的节流时间戳 */
103
+ const previewAtRef = useRef(0);
89
104
  const lensIndexRef = useRef(zoomLensIndex);
90
105
  lensIndexRef.current = zoomLensIndex;
91
106
  const skillRef = useRef(zoomSkill);
92
107
  skillRef.current = zoomSkill;
93
108
 
109
+ /** 标记 / 清除「切换进行中」,并广播给其它 hook 实例 */
110
+ const markSwitching = useMemoizedFn(index => {
111
+ setSwitchingIndex(index);
112
+ event.emit(zoomTurntableSwitching, {
113
+ index
114
+ });
115
+ });
116
+ useEffect(() => {
117
+ const onSwitching = _ref => {
118
+ let {
119
+ index
120
+ } = _ref;
121
+ return setSwitchingIndex(index);
122
+ };
123
+ event.on(zoomTurntableSwitching, onSwitching);
124
+ return () => {
125
+ event.off(zoomTurntableSwitching, onSwitching);
126
+ };
127
+ }, [event]);
128
+
94
129
  // ---------------- 能力加载 ----------------
95
130
  useEffect(() => {
96
131
  if (skipLoad || !devId || !showZoomTurntable) return undefined;
97
-
98
- // mockZoomSkill 接管 DP 值:没有 ipc_multi_zoom 的设备上入口出不来,无法自测
99
- if (mockZoomSkill) {
100
- console.log('[zoomTurntable] mock 模式:跳过 ipc_multi_zoom DP 解析');
101
- }
102
- const raw = mockZoomSkill === true ? MOCK_ZOOM_SKILL : mockZoomSkill || multiZoomDpValue;
132
+ const raw = multiZoomDpValue;
133
+ console.log('[zoomTurntable] ipc_multi_zoom 上报值', JSON.stringify(raw !== null && raw !== void 0 ? raw : null));
103
134
  const skill = parseZoomSkill(raw);
104
135
  if (!isZoomSkillValid(skill)) {
105
136
  // DP 未上报 / 值畸形 / 单镜头设备:能力不可用,入口不出现
@@ -108,7 +139,7 @@ export const useLensZoom = options => {
108
139
  }
109
140
  setZoomSkill(skill);
110
141
  return undefined;
111
- }, [devId, showZoomTurntable, mockZoomSkill, multiZoomDpValue, skipLoad]);
142
+ }, [devId, showZoomTurntable, multiZoomDpValue, skipLoad]);
112
143
 
113
144
  /** 多镜头能力就绪:ipc_multi_zoom 解析出的 cnt > 1(单镜头设备不出现) */
114
145
  const multiLensReady = showZoomTurntable && lensCount(zoomSkill) > 1;
@@ -178,9 +209,37 @@ export const useLensZoom = options => {
178
209
  }, [devId, multiLensReady, playState, skipLoad, syncToLens, syncSmallMonitorByD]);
179
210
 
180
211
  // ---------------- 下发:curM → 切镜头 + d ----------------
212
+
213
+ /**
214
+ * 拖动过程中的连续下发:画面跟着转盘实时变倍,松手才落地的话中间是"死"的。
215
+ *
216
+ * 只在当前镜头区间内下发本地数字缩放 d,超出区间的部分夹到边界 ——
217
+ * 跨镜头要 switchChannel,逐帧下发扛不住,留给松手后的 applyZoom。
218
+ */
219
+ const previewZoom = useMemoizedFn(targetM => {
220
+ const skill = skillRef.current;
221
+ if (!skill) return;
222
+ const now = Date.now();
223
+ if (now - previewAtRef.current < ZOOM_PREVIEW_THROTTLE) return;
224
+ previewAtRef.current = now;
225
+ const index = lensIndexRef.current;
226
+ const [lo, hi] = lensRange(skill, index);
227
+ const clamped = Math.min(hi, Math.max(lo, targetM));
228
+ const d = toScaleMultiple(skill, index, clamped);
229
+ setLastD(devId, d);
230
+ // immediate:连续拖动时属性本来就在变,不需要 ui.tsx 那套"先对齐再设目标"的中间态,
231
+ // 走对齐反而会插入一帧 actual 值,画面看起来会抖
232
+ event.emit(setScaleMultipleEvent, {
233
+ value: d,
234
+ immediate: true
235
+ });
236
+ // 小窗按下发值切换,内部按 devId 去重,只在跨过 d=1 时真正调一次
237
+ syncSmallMonitorByD(d);
238
+ });
181
239
  const beginDrag = useMemoizedFn(value => {
182
240
  draggingRef.current = true;
183
241
  setCurM(value);
242
+ previewZoom(value);
184
243
  });
185
244
 
186
245
  /**
@@ -195,6 +254,7 @@ export const useLensZoom = options => {
195
254
  console.log('[scaleMultiple] zoomTurntable emit setScaleMultipleEvent', D_MIN, `lensIndex=${index}`, `lastD=${getLastD(devId)}`);
196
255
  event.emit(setScaleMultipleEvent, D_MIN);
197
256
  syncToLens(index, D_MIN);
257
+ markSwitching(-1);
198
258
  // 切镜头即回到该镜头的光学正倍数,小窗关掉(onChannelChange / 超时兜底都会走到这里)
199
259
  applySmallMonitor(false);
200
260
  event.emit(zoomTurntableLensChange, {
@@ -233,6 +293,9 @@ export const useLensZoom = options => {
233
293
  // 切物理镜头:从下发到 channelChange 回来这段屏蔽回读
234
294
  console.log('[zoomTurntable] 切换镜头', `${prevIndex} -> ${nextIndex}`, `channel=${skill.lenses[nextIndex].id}`, `targetM=${targetM.toFixed(2)}`);
235
295
  takePendingSwitch(devId); // 上一次没结算完的直接作废,以最新这次为准
296
+ // 高亮先落到目标镜头:这段真空期里 lensIndex 还是原镜头,
297
+ // 不提前挪的话原 tab 会显示目标镜头的读数,看起来就是数值跳变
298
+ markSwitching(nextIndex);
236
299
  switchSeq += 1;
237
300
  const seq = switchSeq;
238
301
  /** 读数退回「当前镜头 + 已记录的 d」;切没切成不确定时才用 */
@@ -244,6 +307,7 @@ export const useLensZoom = options => {
244
307
  index: nextIndex,
245
308
  timer: setTimeout(() => {
246
309
  pendingSwitchStore.delete(devId);
310
+ markSwitching(-1);
247
311
  rollback();
248
312
  console.warn('[zoomTurntable] switchChannel 无回调,读数已回滚', `${prevIndex} -> ${nextIndex}`, `${SWITCH_LENS_TIMEOUT}ms`);
249
313
  }, SWITCH_LENS_TIMEOUT)
@@ -254,6 +318,7 @@ export const useLensZoom = options => {
254
318
 
255
319
  if (!ok) {
256
320
  takePendingSwitch(devId);
321
+ markSwitching(-1);
257
322
  console.warn('[zoomTurntable] 切换镜头失败,读数已回滚', `${prevIndex} -> ${nextIndex}`);
258
323
  setCurM(toDisplayZoom(skill, prevIndex, getLastD(devId)));
259
324
  return;
@@ -307,10 +372,10 @@ export const useLensZoom = options => {
307
372
  * 切镜头的统一收尾入口:自己下发的(switchChannel)与外部来源(设备侧 / 其他端)都走这里。
308
373
  * 自己下发的那次不在 switchChannel 回调里收尾,就是为了等这条事件 —— 设备真的切过去了再动画面。
309
374
  */
310
- const onChannelChange = useMemoizedFn(_ref => {
375
+ const onChannelChange = useMemoizedFn(_ref2 => {
311
376
  let {
312
377
  channel
313
- } = _ref;
378
+ } = _ref2;
314
379
  const skill = skillRef.current;
315
380
  if (!skill) {
316
381
  console.warn('[zoomTurntable] 收到 channelChange 但镜头能力未就绪,已忽略', channel);
@@ -380,6 +445,21 @@ export const useLensZoom = options => {
380
445
  d,
381
446
  lensIndex: lensIndexRef.current
382
447
  });
448
+
449
+ // 画面已经顶到当前镜头区间的上界(也就是下一个镜头的光学倍数,如 1x 镜头放到 3.0):
450
+ // 这个边界值在归属上属于下一个镜头,必须立刻切过去。
451
+ // 走 trailing 防抖的话,那 500ms 里 tab 会挂着「下一镜头的值」显示在当前镜头上。
452
+ const [, upperBound] = lensRange(skill, lensIndexRef.current);
453
+ const nextIndex = matchLens(skill, next);
454
+ if (nextIndex !== lensIndexRef.current && next >= upperBound - D_MATCH_EPS) {
455
+ if (gestureTimerRef.current) {
456
+ clearTimeout(gestureTimerRef.current);
457
+ gestureTimerRef.current = undefined;
458
+ }
459
+ console.log('[zoomTurntable] 触到镜头区间上界,立即切换', `curM=${next.toFixed(2)}`, `${lensIndexRef.current} -> ${nextIndex}`);
460
+ applyToLens(nextIndex, next);
461
+ return;
462
+ }
383
463
  scheduleGestureLensSwitch(next);
384
464
  });
385
465
  useEffect(() => {
@@ -402,6 +482,8 @@ export const useLensZoom = options => {
402
482
  /** 镜头列表(按 value 升序),用于渲染倍率 tab */
403
483
  lenses: (_zoomSkill$lenses$sli = zoomSkill === null || zoomSkill === void 0 || (_zoomSkill$lenses = zoomSkill.lenses) === null || _zoomSkill$lenses === void 0 ? void 0 : _zoomSkill$lenses.slice(0, lensCount(zoomSkill))) !== null && _zoomSkill$lenses$sli !== void 0 ? _zoomSkill$lenses$sli : [],
404
484
  lensIndex: zoomLensIndex,
485
+ /** 正在切往的目标镜头下标;-1 表示没有切换进行中 */
486
+ switchingIndex,
405
487
  /** 画面绝对倍率 */
406
488
  curM,
407
489
  min,
@@ -12,7 +12,7 @@ import { PlayState } from '../../interface';
12
12
  import { pauseTimeToHideAllComponent, playerTap } from '../../ui/constant';
13
13
  import { useLensZoom } from './useLensZoom';
14
14
  import ZoomTurntable from './turntable';
15
- import { CROP_RATIO_BOTTOM, CROP_RATIO_RIGHT, RELAYOUT_DELAYS, STYLE_OPTIONS_BOTTOM, STYLE_OPTIONS_RIGHT, VIBRATE_THROTTLE, zoomTurntableClose } from './constants';
15
+ import { CROP_RATIO_BOTTOM, CROP_RATIO_RIGHT, RELAYOUT_DELAYS, ZOOM_CANVAS_OVERSHOOT_BOTTOM, STYLE_OPTIONS_BOTTOM, STYLE_OPTIONS_RIGHT, VIBRATE_THROTTLE, zoomTurntableClose } from './constants';
16
16
  import './zoomTurntableDock.less';
17
17
  /**
18
18
  * 当前页面 id,ty.nativeDisabled 需要它定位要禁用手势分发的页面。
@@ -40,6 +40,7 @@ const getPageId = () => {
40
40
  * 定位与层级由承载的 CoverView 通过 absolutePosition 提供。
41
41
  */
42
42
  export const ZoomTurntableDock = props => {
43
+ var _initialValueRef$curr;
43
44
  const {
44
45
  event,
45
46
  className
@@ -73,6 +74,27 @@ export const ZoomTurntableDock = props => {
73
74
  /** 上次震动时间与上次震动对应的倍率,用于节流与跨刻度判定 */
74
75
  const lastVibrateAtRef = useRef(0);
75
76
  const lastVibrateValueRef = useRef(curM);
77
+
78
+ /**
79
+ * 转盘起始值:只在打开转盘 / 重建画布时取一次,之后不再跟随 curM 回写。
80
+ *
81
+ * curM 会因为切镜头收尾、画面回读而变,受控回写会在拖动中把指针拽走 ——
82
+ * 转盘自己维护值、只在落位时把结果下发出去,手感才连续。
83
+ *
84
+ * 取值时机要等镜头能力就绪:能力没就绪时 curM 只是 D_MIN 占位,
85
+ * 拿它当起点会让转盘停在错误刻度(非受控之后没有回写能纠正了)。
86
+ */
87
+ const initialValueRef = useRef(null);
88
+ const lastRemountRef = useRef(remountKey);
89
+ if (lastRemountRef.current !== remountKey) {
90
+ lastRemountRef.current = remountKey;
91
+ initialValueRef.current = null;
92
+ }
93
+ if (initialValueRef.current == null && multiLensReady && curM > 0) {
94
+ initialValueRef.current = curM;
95
+ }
96
+ // 兜底夹到 [min, max]:起点绝不能落在倍率域之外(曾出现过 0.0X)
97
+ const initialValue = Math.min(max, Math.max(min, (_initialValueRef$curr = initialValueRef.current) !== null && _initialValueRef$curr !== void 0 ? _initialValueRef$curr : min));
76
98
  const requestClose = useMemoizedFn(() => {
77
99
  event.emit(zoomTurntableClose);
78
100
  });
@@ -111,6 +133,41 @@ export const ZoomTurntableDock = props => {
111
133
  };
112
134
  }, []);
113
135
 
136
+ // 诊断:把浮层与 canvas 的真实布局打出来,核对可拖区域到底覆盖到哪。
137
+ // 期望(竖屏 HIT_PADDING_Y=56):dock 高 296、canvas 高 288,canvas 顶距 dock 顶约 8px。
138
+ // 若 canvas 实际高度远小于 288,说明 rjs 的 canvasWidth/Height 没随 styleOptions 更新,
139
+ // 上半部分自然收不到 touch,会直接落到播放器上。
140
+ useEffect(() => {
141
+ const timer = setTimeout(() => {
142
+ const query = ty.createSelectorQuery();
143
+ query.select('.ipc-player-plugin-zoom-turntable-dock').boundingClientRect();
144
+ query.select('.ipc-zoom-turntable-canvas').boundingClientRect();
145
+ query.selectViewport().boundingClientRect();
146
+ query.exec(res => {
147
+ const dock = res === null || res === void 0 ? void 0 : res[0];
148
+ const canvas = res === null || res === void 0 ? void 0 : res[1];
149
+ const viewport = res === null || res === void 0 ? void 0 : res[2];
150
+ console.log('[zoomTurntable] 浮层布局诊断', JSON.stringify({
151
+ dock: dock && {
152
+ top: dock.top,
153
+ left: dock.left,
154
+ w: dock.width,
155
+ h: dock.height
156
+ },
157
+ canvas: canvas && {
158
+ top: canvas.top,
159
+ left: canvas.left,
160
+ w: canvas.width,
161
+ h: canvas.height
162
+ },
163
+ canvas顶距dock顶: dock && canvas ? canvas.top - dock.top : null,
164
+ viewportH: viewport === null || viewport === void 0 ? void 0 : viewport.height
165
+ }));
166
+ });
167
+ }, 300);
168
+ return () => clearTimeout(timer);
169
+ }, [remountKey]);
170
+
114
171
  // 点击播放器 → 收起转盘。
115
172
  // ui.tsx 的 handPlayerTap 会在此时把其余控件重新显示出来(componentHideState → false)
116
173
  // 并 emit(playerTap),所以这里只需负责关掉转盘。
@@ -193,6 +250,20 @@ export const ZoomTurntableDock = props => {
193
250
  alignItems: 'center',
194
251
  justifyContent: 'flex-end'
195
252
  }), [isRight]);
253
+
254
+ /**
255
+ * canvas 的承载层。竖屏用负 margin 让 canvas 底边探出播放器底边,
256
+ * 浮层本身则贴底不下沉 —— 这样 canvas 顶部(刻度线那一带)才不会被 CoverView 裁掉,
257
+ * 而绘制内容的视觉位置与探出效果都和原来一致。
258
+ */
259
+ const canvasHolderStyle = useMemo(() => isRight ? {
260
+ display: 'flex',
261
+ justifyContent: 'center'
262
+ } : {
263
+ display: 'flex',
264
+ justifyContent: 'center',
265
+ marginBottom: `-${ZOOM_CANVAS_OVERSHOOT_BOTTOM}px`
266
+ }, [isRight]);
196
267
  if (!multiLensReady || playState !== PlayState.PLAYING) {
197
268
  return null;
198
269
  }
@@ -205,10 +276,11 @@ export const ZoomTurntableDock = props => {
205
276
  'ipc-player-plugin-zoom-turntable-dock-right': isRight
206
277
  }),
207
278
  style: turntableStyle
279
+ }, /*#__PURE__*/React.createElement(View, {
280
+ style: canvasHolderStyle
208
281
  }, /*#__PURE__*/React.createElement(ZoomTurntable, {
209
282
  key: `${isRight ? 'right' : 'bottom'}-${remountKey}`,
210
- value: curM,
211
- defaultValue: curM,
283
+ defaultValue: initialValue,
212
284
  min: min,
213
285
  max: max,
214
286
  step: step,
@@ -224,5 +296,5 @@ export const ZoomTurntableDock = props => {
224
296
  onChangeEnd: handleChangeEnd,
225
297
  onTouchStart: handleTouchStart,
226
298
  onTouchEnd: handleTouchEnd
227
- }));
299
+ })));
228
300
  };
@@ -1,13 +1,10 @@
1
1
  import React from 'react';
2
2
  import { ComponentConfigProps } from '../../interface';
3
- import { ZoomSkill } from './lensSkill';
4
3
  import './zoomTurntableEntry.less';
5
4
  type Props = ComponentConfigProps & {
6
5
  className?: string;
7
6
  /** 业务侧的功能开关;实际是否渲染仍由 ipc_multi_zoom 解析出的 cnt 决定 */
8
7
  showZoomTurntable?: boolean;
9
- /** 调试用:直接注入镜头能力,跳过 ipc_multi_zoom DP 解析 */
10
- mockZoomSkill?: ZoomSkill | boolean;
11
8
  /** 长按判定时长,默认 500ms */
12
9
  longPressDuration?: number;
13
10
  /** 关闭圈选引导 */
@@ -49,9 +49,16 @@ export const ZoomTurntableEntry = props => {
49
49
  multiLensReady,
50
50
  lenses,
51
51
  lensIndex,
52
+ switchingIndex,
52
53
  curM,
53
54
  switchToLens
54
55
  } = useLensZoom(props);
56
+
57
+ // 切换真空期(下发成功 ~ channelChange / 超时收尾)里镜头下标还没动,
58
+ // 高亮提前落到目标镜头,且各 tab 一律显示自己的光学倍数:
59
+ // 否则原镜头的 tab 上会先冒出目标镜头的读数,收尾后再跳回去。
60
+ const isLensSwitching = switchingIndex >= 0;
61
+ const activeIndex = isLensSwitching ? switchingIndex : lensIndex;
55
62
  const [dockVisible, setDockVisible] = useState(false);
56
63
  const timerRef = useRef();
57
64
  const startRef = useRef();
@@ -61,6 +68,9 @@ export const ZoomTurntableEntry = props => {
61
68
  const cancelledRef = useRef(false);
62
69
  const dockVisibleRef = useRef(false);
63
70
  const tipCheckedRef = useRef(false);
71
+ /** onTouchEnd 是稳定引用,用 ref 取最新的高亮下标 */
72
+ const activeIndexRef = useRef(activeIndex);
73
+ activeIndexRef.current = activeIndex;
64
74
  const clearTimer = () => {
65
75
  if (timerRef.current) {
66
76
  clearTimeout(timerRef.current);
@@ -211,13 +221,17 @@ export const ZoomTurntableEntry = props => {
211
221
  clearTimer();
212
222
  // firedRef:长按已出转盘,不再当作点击处理
213
223
  if (!firedRef.current && !cancelledRef.current) {
214
- if (index !== lensIndex) {
224
+ // activeIndex 比:正在切往的那个 tab 再点一次不该重复下发
225
+ if (index !== activeIndexRef.current) {
215
226
  event.emit(widgetClick, {
216
227
  widgetId: widgetLabs.ZOOM_TURNTABLE
217
228
  });
218
229
  switchToLens(index);
219
230
  }
220
- event.emit(startTimeToHideAllComponent);
231
+ // 点过 tab 之后不重启自动隐藏倒计时(onTouchStart 已 pause,这里保持暂停):
232
+ // 切镜头真正生效要等 channelChange / 超时收尾,5s 倒计时会在切换过程中把工具条藏掉,
233
+ // 用户看不到读数落位。收起交给用户点播放器(ui.tsx handPlayerTap 直接置隐藏,
234
+ // 不依赖倒计时,暂停状态下照常生效)。
221
235
  }
222
236
  });
223
237
  if (playState !== PlayState.PLAYING || !multiLensReady) {
@@ -230,9 +244,10 @@ export const ZoomTurntableEntry = props => {
230
244
  'ipc-player-plugin-zoom-turntable-tabs-full': screenType === 'full'
231
245
  })
232
246
  }, lenses.map((lens, index) => {
233
- const active = index === lensIndex;
234
- // 当前镜头实时显示画面绝对倍率(手势放大时 1.0X → 2.5X),其余显示各自光学倍数
235
- const text = `${(active ? curM : lens.basicRatio).toFixed(1)}X`;
247
+ const active = index === activeIndex;
248
+ // 当前镜头实时显示画面绝对倍率(手势放大时 1.0X → 2.5X),其余显示各自光学倍数;
249
+ // 切换进行中则一律显示光学倍数 —— 此时 curM 已指向目标值,挂谁身上都是跳变
250
+ const text = `${(active && !isLensSwitching ? curM : lens.basicRatio).toFixed(1)}X`;
236
251
  return /*#__PURE__*/React.createElement(View, {
237
252
  key: lens.id,
238
253
  className: clsx('ipc-player-plugin-zoom-turntable-tab', {
@@ -27,18 +27,28 @@
27
27
  align-items: center;
28
28
  padding: calc(4px * var(--ipc-player-size-scale, 1)) calc(5px * var(--ipc-player-size-scale, 1));
29
29
  background-color: rgba(255, 255, 255, 0.3);
30
+ // 读数每帧都在变(4.1X → 4.8X),比例字体下 1 比 8 窄,胶囊宽度会跟着抖,
31
+ // 还会连带触发左下角工具条的重测。min-width 撑住 "N.NX" 的最宽排布,
32
+ // 加上 box-sizing 让 padding 计入,宽度就不再随数字变化。
33
+ // 超过 4 字符(10.0X)时自然撑开,那是跨十倍才发生一次的变化。
34
+ box-sizing: border-box;
35
+ min-width: calc(38px * var(--ipc-player-size-scale, 1));
30
36
 
31
37
  .ipc-player-plugin-zoom-turntable-tab-text {
32
38
  color: var(--iconColor);
33
39
  font-weight: 600;
34
40
  font-size: calc(12px * var(--ipc-player-size-scale, 1));
41
+ // 等宽数字:让 0-9 占同样的字身宽,从根上消掉逐帧抖动(渲染层支持时生效)
42
+ font-variant-numeric: tabular-nums;
43
+ font-feature-settings: 'tnum';
35
44
  }
36
45
  }
37
46
 
38
- // 横屏:左右 padding 放宽,同 RealTimeMagnification 的 full
47
+ // 横屏:左右 padding 放宽,同 RealTimeMagnification 的 full 态;min-width 同步加宽
39
48
  .ipc-player-plugin-zoom-turntable-tab-full {
40
49
  padding-left: calc(8px * var(--ipc-player-size-scale, 1));
41
50
  padding-right: calc(8px * var(--ipc-player-size-scale, 1));
51
+ min-width: calc(44px * var(--ipc-player-size-scale, 1));
42
52
  }
43
53
 
44
54
  // 选中态:白底黑字(须在基础态之后定义,同优先级下后者生效)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ray-js/ipc-player-integration",
3
- "version": "0.0.50-beta.0",
3
+ "version": "0.0.50-beta.2",
4
4
  "description": "IPC 融合播放器",
5
5
  "main": "lib/index",
6
6
  "files": [
@@ -48,7 +48,7 @@
48
48
  "@commitlint/cli": "^7.2.1",
49
49
  "@commitlint/config-conventional": "^9.0.1",
50
50
  "@ray-js/cli": "^1.7.61",
51
- "@ray-js/ray": "1.7.83-beta.0",
51
+ "@ray-js/ray": "^1.7.83",
52
52
  "@testing-library/react-hooks": "^8.0.1",
53
53
  "@types/jest": "^29.5.14",
54
54
  "ahooks": "^3.8.4",