@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.
- package/README.md +11 -11
- package/lib/ctx/ctx.composition.js +5 -5
- package/lib/features/initPlayerWidgets/index.d.ts +0 -3
- package/lib/features/initPlayerWidgets/index.js +1 -2
- package/lib/hooks/useBattery/index.js +7 -1
- package/lib/iconfont/iconfont.css +127 -7
- package/lib/iconfont/iconfont.js +10 -10
- package/lib/iconfont/iconfont.json +233 -23
- package/lib/iconfont/iconfont.ttf +0 -0
- package/lib/iconfont/iconfont.woff +0 -0
- package/lib/iconfont/iconfont.woff2 +0 -0
- package/lib/interface.d.ts +1 -1
- package/lib/interface.js +1 -1
- package/lib/ports.output.d.ts +13 -0
- package/lib/ports.output.js +13 -0
- package/lib/ui/bottomLeftContent.js +141 -27
- package/lib/ui/bottomRightContent.d.ts +6 -1
- package/lib/ui/bottomRightContent.js +43 -5
- package/lib/ui/ui.js +58 -4
- package/lib/ui/ui.less +17 -0
- package/lib/widgets/battery/battery.js +11 -3
- package/lib/widgets/battery/battery.less +11 -0
- package/lib/widgets/battery/batteryFull.js +11 -3
- package/lib/widgets/zoomTurntable/constants.d.ts +44 -12
- package/lib/widgets/zoomTurntable/constants.js +61 -17
- package/lib/widgets/zoomTurntable/lensModel.test.js +1 -1
- package/lib/widgets/zoomTurntable/lensSkill.d.ts +1 -1
- package/lib/widgets/zoomTurntable/lensSkill.js +1 -1
- package/lib/widgets/zoomTurntable/turntable/index.js +4 -1
- package/lib/widgets/zoomTurntable/turntable/rjs/index.rjs +138 -9
- package/lib/widgets/zoomTurntable/turntable/rjs/index.tyml +1 -0
- package/lib/widgets/zoomTurntable/useLensZoom.d.ts +3 -4
- package/lib/widgets/zoomTurntable/useLensZoom.js +94 -12
- package/lib/widgets/zoomTurntable/zoomTurntableDock.js +76 -4
- package/lib/widgets/zoomTurntable/zoomTurntableEntry.d.ts +0 -3
- package/lib/widgets/zoomTurntable/zoomTurntableEntry.js +20 -5
- package/lib/widgets/zoomTurntable/zoomTurntableEntry.less +11 -1
- package/package.json +2 -2
|
@@ -13,6 +13,16 @@ export declare const zoomTurntableOpen = "zoomTurntableOpen";
|
|
|
13
13
|
export declare const zoomTurntableLensChange = "zoomTurntableLensChange";
|
|
14
14
|
/** 事件:画面绝对倍率变化,载荷 { curM, d, lensIndex } */
|
|
15
15
|
export declare const zoomTurntableZoomChange = "zoomTurntableZoomChange";
|
|
16
|
+
/**
|
|
17
|
+
* 事件:物理镜头切换进行中,载荷 { index }(-1 表示结束)。
|
|
18
|
+
*
|
|
19
|
+
* 切换从下发到 channelChange 回来有一段真空期(真机上常常走到 10s 超时兜底),
|
|
20
|
+
* 这期间镜头下标还没动、读数却已经指向目标值。倍率 tab 要据此把高亮提前落到目标镜头,
|
|
21
|
+
* 否则原镜头的 tab 会显示目标镜头的数值,看起来就是一次跳变。
|
|
22
|
+
*
|
|
23
|
+
* 放事件而不是组件内 state:Entry / Dock 是两个 hook 实例,发起方与消费方常常不是同一个。
|
|
24
|
+
*/
|
|
25
|
+
export declare const zoomTurntableSwitching = "zoomTurntableSwitching";
|
|
16
26
|
/** 长按判定时长(ms) */
|
|
17
27
|
export declare const LONG_PRESS_MS = 500;
|
|
18
28
|
/** 长按期间允许的位移容差(px),超出视为横滑并取消长按 */
|
|
@@ -24,11 +34,11 @@ export declare const SWITCH_LENS_TIMEOUT = 3000;
|
|
|
24
34
|
* 超时按下发结果自己收尾,**不回滚** —— 指令已被受理,物理镜头其实已经切了,
|
|
25
35
|
* 回滚读数只会让读数和画面对不上。
|
|
26
36
|
*
|
|
27
|
-
*
|
|
37
|
+
* 取 10s,比 SWITCH_LENS_TIMEOUT 宽得多:设备真正切完镜头本身要一会儿,
|
|
28
38
|
* 给短了会在事件晚到时提前自己收尾,等于白订阅。
|
|
29
39
|
* 真机现状:设备侧暂未推 channelChange,切镜头基本都靠这条超时收尾。
|
|
30
40
|
*/
|
|
31
|
-
export declare const CHANNEL_SETTLE_TIMEOUT =
|
|
41
|
+
export declare const CHANNEL_SETTLE_TIMEOUT = 10000;
|
|
32
42
|
/**
|
|
33
43
|
* 判定「画面是否正好停在镜头光学正倍数」的容差。
|
|
34
44
|
* 回读的 d 是浮点,1.0 附近会有 1.0000001 这种抖动,严格相等判不出来。
|
|
@@ -42,8 +52,6 @@ export declare const QUERY_LENS_TIMEOUT = 3000;
|
|
|
42
52
|
* 待 APP 提供 zoomFree 的「手势是否结束」参数后可改为由该参数触发。
|
|
43
53
|
*/
|
|
44
54
|
export declare const GESTURE_SWITCH_DEBOUNCE = 500;
|
|
45
|
-
/** 横屏 dock 距播放器右边的距离,负值时向右探出 */
|
|
46
|
-
export declare const ZOOM_DOCK_RIGHT = -10;
|
|
47
55
|
/** dock 层级,需高于四角容器的 z-index: 100 */
|
|
48
56
|
export declare const ZOOM_DOCK_Z_INDEX = 120;
|
|
49
57
|
/**
|
|
@@ -53,22 +61,46 @@ export declare const ZOOM_DOCK_Z_INDEX = 120;
|
|
|
53
61
|
*/
|
|
54
62
|
export declare const CROP_RATIO_BOTTOM = 0.5;
|
|
55
63
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
64
|
+
* 触摸响应余量:canvas 是唯一能收 touch 的元素,把它向外扩一圈,
|
|
65
|
+
* **整个转盘范围(椭圆截面 + 上方文案区 + 两侧空白)都能拖动**,而不是只有刻度线那一带。
|
|
66
|
+
*
|
|
67
|
+
* 绘制坐标由 renderer 按同样的余量补偿,视觉不变;下面的 ZOOM_DOCK_BOTTOM /
|
|
68
|
+
* ZOOM_DOCK_RIGHT 也都从「内容边距」反推,改这两个值不会让转盘位置偏移。
|
|
58
69
|
*/
|
|
59
|
-
export declare const HIT_PADDING_X =
|
|
60
|
-
export declare const HIT_PADDING_Y =
|
|
70
|
+
export declare const HIT_PADDING_X = 48;
|
|
71
|
+
export declare const HIT_PADDING_Y = 56;
|
|
61
72
|
/**
|
|
62
73
|
* 竖屏 dock 高度:必须 >= canvas 实际高度(椭圆截面 + 上下触摸余量),
|
|
63
74
|
* 否则承载它的 CoverView 会把转盘裁掉一截。随 CROP_RATIO_BOTTOM / HIT_PADDING_Y 自动跟随,留 8px 余量。
|
|
64
75
|
*/
|
|
65
76
|
export declare const ZOOM_DOCK_HEIGHT: number;
|
|
66
77
|
/**
|
|
67
|
-
* 竖屏 dock
|
|
68
|
-
*
|
|
69
|
-
*
|
|
78
|
+
* 竖屏 dock 距播放器底边的距离,固定 0(贴底,不再探出)。
|
|
79
|
+
*
|
|
80
|
+
* 这里曾用负值让浮层整体下沉、把椭圆下半探出播放器底边。副作用是 CoverView 顶边
|
|
81
|
+
* 跟着被压低,canvas 顶部(含刻度线那一带)露在 CoverView 之外被裁掉 —— 原生层裁掉的
|
|
82
|
+
* 部分收不到 touch,表现就是「只有刻度尺下方能滑,上半部分直接拖到播放器」。
|
|
83
|
+
* 实测:dock 可命中高度只有 155,而 canvas 高 287.5,顶部溢出 132.5。
|
|
84
|
+
*
|
|
85
|
+
* 现在改由 canvas 自己用负 margin 向下探出(见 ZOOM_CANVAS_OVERSHOOT_BOTTOM),
|
|
86
|
+
* 浮层顶边因此上移,canvas 完整落在可命中区内,而内容视觉位置不变。
|
|
87
|
+
*/
|
|
88
|
+
export declare const ZOOM_DOCK_BOTTOM = 0;
|
|
89
|
+
/**
|
|
90
|
+
* 竖屏 canvas 底边探出播放器底边的距离。
|
|
91
|
+
* canvas 在 dock 内底部对齐,靠这个负 margin 下移,使绘制内容仍然铺到播放器底边:
|
|
92
|
+
* 内容底探出 = ZOOM_CANVAS_OVERSHOOT_BOTTOM - HIT_PADDING_Y
|
|
93
|
+
* 探出去的部分由 .ipc-player-content 的 overflow: hidden 裁掉,与改动前的观感一致。
|
|
70
94
|
*/
|
|
71
|
-
export declare const
|
|
95
|
+
export declare const ZOOM_CANVAS_OVERSHOOT_BOTTOM: number;
|
|
96
|
+
/**
|
|
97
|
+
* 拖动转盘时连续下发缩放的节流间隔(ms)。
|
|
98
|
+
* 转盘每跨一个 step 就回调一次,不节流会把 setScaleMultiple 打爆;
|
|
99
|
+
* 松手时 applyZoom 还会按最终值精确下发一次,所以这里丢掉尾值也没关系。
|
|
100
|
+
*/
|
|
101
|
+
export declare const ZOOM_PREVIEW_THROTTLE = 80;
|
|
102
|
+
/** 横屏 dock 距播放器右边的距离,负值时向右探出 */
|
|
103
|
+
export declare const ZOOM_DOCK_RIGHT: number;
|
|
72
104
|
/** 横屏截取比例(右侧展示空间较窄,截得更深) */
|
|
73
105
|
export declare const CROP_RATIO_RIGHT = 0.45;
|
|
74
106
|
/** 布局沉降兜底:onShow / onResize 后的重挂载延时 */
|
|
@@ -19,6 +19,17 @@ export const zoomTurntableLensChange = 'zoomTurntableLensChange';
|
|
|
19
19
|
/** 事件:画面绝对倍率变化,载荷 { curM, d, lensIndex } */
|
|
20
20
|
export const zoomTurntableZoomChange = 'zoomTurntableZoomChange';
|
|
21
21
|
|
|
22
|
+
/**
|
|
23
|
+
* 事件:物理镜头切换进行中,载荷 { index }(-1 表示结束)。
|
|
24
|
+
*
|
|
25
|
+
* 切换从下发到 channelChange 回来有一段真空期(真机上常常走到 10s 超时兜底),
|
|
26
|
+
* 这期间镜头下标还没动、读数却已经指向目标值。倍率 tab 要据此把高亮提前落到目标镜头,
|
|
27
|
+
* 否则原镜头的 tab 会显示目标镜头的数值,看起来就是一次跳变。
|
|
28
|
+
*
|
|
29
|
+
* 放事件而不是组件内 state:Entry / Dock 是两个 hook 实例,发起方与消费方常常不是同一个。
|
|
30
|
+
*/
|
|
31
|
+
export const zoomTurntableSwitching = 'zoomTurntableSwitching';
|
|
32
|
+
|
|
22
33
|
/** 长按判定时长(ms) */
|
|
23
34
|
export const LONG_PRESS_MS = 500;
|
|
24
35
|
|
|
@@ -33,11 +44,11 @@ export const SWITCH_LENS_TIMEOUT = 3000;
|
|
|
33
44
|
* 超时按下发结果自己收尾,**不回滚** —— 指令已被受理,物理镜头其实已经切了,
|
|
34
45
|
* 回滚读数只会让读数和画面对不上。
|
|
35
46
|
*
|
|
36
|
-
*
|
|
47
|
+
* 取 10s,比 SWITCH_LENS_TIMEOUT 宽得多:设备真正切完镜头本身要一会儿,
|
|
37
48
|
* 给短了会在事件晚到时提前自己收尾,等于白订阅。
|
|
38
49
|
* 真机现状:设备侧暂未推 channelChange,切镜头基本都靠这条超时收尾。
|
|
39
50
|
*/
|
|
40
|
-
export const CHANNEL_SETTLE_TIMEOUT =
|
|
51
|
+
export const CHANNEL_SETTLE_TIMEOUT = 10000;
|
|
41
52
|
|
|
42
53
|
/**
|
|
43
54
|
* 判定「画面是否正好停在镜头光学正倍数」的容差。
|
|
@@ -55,9 +66,6 @@ export const QUERY_LENS_TIMEOUT = 3000;
|
|
|
55
66
|
*/
|
|
56
67
|
export const GESTURE_SWITCH_DEBOUNCE = 500;
|
|
57
68
|
|
|
58
|
-
/** 横屏 dock 距播放器右边的距离,负值时向右探出 */
|
|
59
|
-
export const ZOOM_DOCK_RIGHT = -10;
|
|
60
|
-
|
|
61
69
|
/** dock 层级,需高于四角容器的 z-index: 100 */
|
|
62
70
|
export const ZOOM_DOCK_Z_INDEX = 120;
|
|
63
71
|
|
|
@@ -72,11 +80,14 @@ export const CROP_RATIO_BOTTOM = 0.5;
|
|
|
72
80
|
const BASE_ELLIPSE_HEIGHT = 351;
|
|
73
81
|
|
|
74
82
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
83
|
+
* 触摸响应余量:canvas 是唯一能收 touch 的元素,把它向外扩一圈,
|
|
84
|
+
* **整个转盘范围(椭圆截面 + 上方文案区 + 两侧空白)都能拖动**,而不是只有刻度线那一带。
|
|
85
|
+
*
|
|
86
|
+
* 绘制坐标由 renderer 按同样的余量补偿,视觉不变;下面的 ZOOM_DOCK_BOTTOM /
|
|
87
|
+
* ZOOM_DOCK_RIGHT 也都从「内容边距」反推,改这两个值不会让转盘位置偏移。
|
|
77
88
|
*/
|
|
78
|
-
export const HIT_PADDING_X =
|
|
79
|
-
export const HIT_PADDING_Y =
|
|
89
|
+
export const HIT_PADDING_X = 48;
|
|
90
|
+
export const HIT_PADDING_Y = 56;
|
|
80
91
|
|
|
81
92
|
/**
|
|
82
93
|
* 竖屏 dock 高度:必须 >= canvas 实际高度(椭圆截面 + 上下触摸余量),
|
|
@@ -85,18 +96,51 @@ export const HIT_PADDING_Y = 24;
|
|
|
85
96
|
export const ZOOM_DOCK_HEIGHT = Math.ceil(BASE_ELLIPSE_HEIGHT * CROP_RATIO_BOTTOM) + HIT_PADDING_Y * 2 + 8;
|
|
86
97
|
|
|
87
98
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
99
|
+
* 绘制内容(椭圆截面)顶边到播放器底边的距离 —— 真正的视觉基准。
|
|
100
|
+
*
|
|
101
|
+
* canvas 在 dock 内是底部对齐的,内容在 canvas 里又偏移了一份 HIT_PADDING_Y,
|
|
102
|
+
* 所以位置要从内容边界反推、而不是从 dock 边界反推,否则一改触摸余量转盘就整体挪位。
|
|
103
|
+
* 内容顶距播放器底 = 截面高 + HIT_PADDING_Y + ZOOM_DOCK_BOTTOM
|
|
104
|
+
*/
|
|
105
|
+
const ZOOM_CONTENT_TOP_GAP = 137;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 竖屏 dock 距播放器底边的距离,固定 0(贴底,不再探出)。
|
|
109
|
+
*
|
|
110
|
+
* 这里曾用负值让浮层整体下沉、把椭圆下半探出播放器底边。副作用是 CoverView 顶边
|
|
111
|
+
* 跟着被压低,canvas 顶部(含刻度线那一带)露在 CoverView 之外被裁掉 —— 原生层裁掉的
|
|
112
|
+
* 部分收不到 touch,表现就是「只有刻度尺下方能滑,上半部分直接拖到播放器」。
|
|
113
|
+
* 实测:dock 可命中高度只有 155,而 canvas 高 287.5,顶部溢出 132.5。
|
|
114
|
+
*
|
|
115
|
+
* 现在改由 canvas 自己用负 margin 向下探出(见 ZOOM_CANVAS_OVERSHOOT_BOTTOM),
|
|
116
|
+
* 浮层顶边因此上移,canvas 完整落在可命中区内,而内容视觉位置不变。
|
|
117
|
+
*/
|
|
118
|
+
export const ZOOM_DOCK_BOTTOM = 0;
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 竖屏 canvas 底边探出播放器底边的距离。
|
|
122
|
+
* canvas 在 dock 内底部对齐,靠这个负 margin 下移,使绘制内容仍然铺到播放器底边:
|
|
123
|
+
* 内容底探出 = ZOOM_CANVAS_OVERSHOOT_BOTTOM - HIT_PADDING_Y
|
|
124
|
+
* 探出去的部分由 .ipc-player-content 的 overflow: hidden 裁掉,与改动前的观感一致。
|
|
91
125
|
*/
|
|
92
|
-
const
|
|
126
|
+
export const ZOOM_CANVAS_OVERSHOOT_BOTTOM = Math.ceil(BASE_ELLIPSE_HEIGHT * CROP_RATIO_BOTTOM) + HIT_PADDING_Y - ZOOM_CONTENT_TOP_GAP;
|
|
93
127
|
|
|
94
128
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
129
|
+
* 横屏内容右边相对播放器右边的内缩距离(视觉基准)。
|
|
130
|
+
* canvas 在 dock 内右对齐,内容右边又内缩一份 HIT_PADDING_Y(横屏画布旋转 90°):
|
|
131
|
+
* 内容右边内缩 = ZOOM_DOCK_RIGHT + HIT_PADDING_Y
|
|
98
132
|
*/
|
|
99
|
-
|
|
133
|
+
const ZOOM_CONTENT_RIGHT_INSET = 14;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 拖动转盘时连续下发缩放的节流间隔(ms)。
|
|
137
|
+
* 转盘每跨一个 step 就回调一次,不节流会把 setScaleMultiple 打爆;
|
|
138
|
+
* 松手时 applyZoom 还会按最终值精确下发一次,所以这里丢掉尾值也没关系。
|
|
139
|
+
*/
|
|
140
|
+
export const ZOOM_PREVIEW_THROTTLE = 80;
|
|
141
|
+
|
|
142
|
+
/** 横屏 dock 距播放器右边的距离,负值时向右探出 */
|
|
143
|
+
export const ZOOM_DOCK_RIGHT = ZOOM_CONTENT_RIGHT_INSET - HIT_PADDING_Y;
|
|
100
144
|
|
|
101
145
|
/** 横屏截取比例(右侧展示空间较窄,截得更深) */
|
|
102
146
|
export const CROP_RATIO_RIGHT = 0.45;
|
|
@@ -97,7 +97,7 @@ describe('ipc_multi_zoom 上报值解析', () => {
|
|
|
97
97
|
});
|
|
98
98
|
expect(zoomBounds(skill)).toEqual([1, 6]);
|
|
99
99
|
});
|
|
100
|
-
it('
|
|
100
|
+
it('对象值直接透传(内部 mock 常量)', () => {
|
|
101
101
|
expect(parseZoomSkill(DOUBLE)).toBe(DOUBLE);
|
|
102
102
|
});
|
|
103
103
|
it('空值 / 畸形 JSON 返回 null,不抛错', () => {
|
|
@@ -53,7 +53,7 @@ export declare const lensCount: (skill?: ZoomSkill | null) => number;
|
|
|
53
53
|
export declare const isZoomSkillValid: (skill?: ZoomSkill | null) => boolean;
|
|
54
54
|
/**
|
|
55
55
|
* 解析 ipc_multi_zoom DP 上报的 string 值为镜头能力。
|
|
56
|
-
* @param value DP
|
|
56
|
+
* @param value DP 值;内部 mock 常量是对象、DP 上报是 string,两条来源共用同一校验链路
|
|
57
57
|
*/
|
|
58
58
|
export declare const parseZoomSkill: (value?: unknown) => ZoomSkill | null;
|
|
59
59
|
/**
|
|
@@ -90,7 +90,7 @@ export const isZoomSkillValid = skill => {
|
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* 解析 ipc_multi_zoom DP 上报的 string 值为镜头能力。
|
|
93
|
-
* @param value DP
|
|
93
|
+
* @param value DP 值;内部 mock 常量是对象、DP 上报是 string,两条来源共用同一校验链路
|
|
94
94
|
*/
|
|
95
95
|
export const parseZoomSkill = value => {
|
|
96
96
|
if (value == null || value === '') return null;
|
|
@@ -30,6 +30,9 @@ const ZoomTurntable = props => {
|
|
|
30
30
|
styleOptions = {}
|
|
31
31
|
} = props;
|
|
32
32
|
const canvasId = useRef(`ipc-zoom-turntable-${++canvasIdCounter}`).current;
|
|
33
|
+
|
|
34
|
+
// 起始值兜底:非受控用法下没有后续回写纠正,非法初值会让指针停在 0.0X
|
|
35
|
+
const safeDefaultValue = Number(defaultValue) > 0 ? Math.min(max, Math.max(min, Number(defaultValue))) : min;
|
|
33
36
|
console.log('[zoomTurntable/logic] render', JSON.stringify({
|
|
34
37
|
canvasId,
|
|
35
38
|
min,
|
|
@@ -45,7 +48,7 @@ const ZoomTurntable = props => {
|
|
|
45
48
|
min: min,
|
|
46
49
|
max: max,
|
|
47
50
|
step: step,
|
|
48
|
-
defaultValue:
|
|
51
|
+
defaultValue: safeDefaultValue,
|
|
49
52
|
value: controlledValue,
|
|
50
53
|
scale: scale,
|
|
51
54
|
orientation: orientation,
|
|
@@ -29,6 +29,15 @@ const MINOR_DIVS = 60;
|
|
|
29
29
|
const MAJOR_SKIP = 0.004;
|
|
30
30
|
const LABEL_WINDOW = 0.28;
|
|
31
31
|
const TOP_SAFE_AREA = 50;
|
|
32
|
+
// 点击(非拖动)判定:手指位移与按压时长都在阈值内才算 tap
|
|
33
|
+
const TAP_MOVE_TOLERANCE = 10;
|
|
34
|
+
const TAP_MAX_MS = 350;
|
|
35
|
+
// tap 命中弧带的归一化半径范围(1 = 刻度轨道所在的椭圆)。
|
|
36
|
+
// 太靠圆心或太靠外都不算点刻度,直接忽略,避免误触把倍率甩飞。
|
|
37
|
+
const TAP_HIT_MIN_R = 0.55;
|
|
38
|
+
const TAP_HIT_MAX_R = 1.4;
|
|
39
|
+
// 点击后滚到目标刻度的动画时长(ms)
|
|
40
|
+
const TAP_ANIM_MS = 220;
|
|
32
41
|
// 拖动方向:-1 表示「拖动的是转盘本身」——向左拖,盘面左移,右侧的大刻度转到指针处,倍率变大,
|
|
33
42
|
// 与实体相机的调焦环手感一致;+1 则是「拖动指针」的反向手感。
|
|
34
43
|
const DRAG_DIRECTION = -1;
|
|
@@ -56,7 +65,11 @@ class ZoomTurntableRenderer {
|
|
|
56
65
|
: this._style('cropRatio', 0.2);
|
|
57
66
|
this.callMethod = options.callMethod;
|
|
58
67
|
|
|
59
|
-
|
|
68
|
+
// 起始值必须落在 [min, max] 内:外部传进来的初值可能是尚未就绪的占位值(0 / NaN),
|
|
69
|
+
// 非受控模式下没有后续回写来纠正,直接夹住
|
|
70
|
+
var initial = Number(options.defaultValue);
|
|
71
|
+
if (!(initial > 0)) initial = this.min;
|
|
72
|
+
this.value = Math.max(this.min, Math.min(this.max, initial));
|
|
60
73
|
// progress(0~1) 是拖动时的唯一真相,保持浮点,避免 step 量化造成的抖动
|
|
61
74
|
this.progress = this.getProgress(this.value);
|
|
62
75
|
this.dragging = false;
|
|
@@ -527,6 +540,8 @@ class ZoomTurntableRenderer {
|
|
|
527
540
|
this.lastTouchPoint = this.orientation === 'right'
|
|
528
541
|
? touch.clientY - rectTop
|
|
529
542
|
: touch.clientX - rectLeft;
|
|
543
|
+
// tap 判定的起点:位移 / 时长都在阈值内,touchend 时按「点击刻度」处理
|
|
544
|
+
this._tapStart = { x: touch.clientX, y: touch.clientY, t: Date.now(), moved: false };
|
|
530
545
|
dlog('touchstart lastTouchPoint=' + this.lastTouchPoint, 'value=' + this.value);
|
|
531
546
|
}
|
|
532
547
|
|
|
@@ -549,6 +564,15 @@ class ZoomTurntableRenderer {
|
|
|
549
564
|
? touch.clientY - rectTop
|
|
550
565
|
: touch.clientX - rectLeft;
|
|
551
566
|
|
|
567
|
+
if (this._tapStart && !this._tapStart.moved) {
|
|
568
|
+
if (
|
|
569
|
+
Math.abs(touch.clientX - this._tapStart.x) > TAP_MOVE_TOLERANCE ||
|
|
570
|
+
Math.abs(touch.clientY - this._tapStart.y) > TAP_MOVE_TOLERANCE
|
|
571
|
+
) {
|
|
572
|
+
this._tapStart.moved = true;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
552
576
|
// 像素增量直接映射到「值」的等量变化,而非 progress;
|
|
553
577
|
// 这样无论处于高倍率还是低倍率端,等量滑动产生等量值变化,手感一致(消除 log 曲线导致的两端灵敏度差异)。
|
|
554
578
|
var deltaX = (currentPoint - this.lastTouchPoint) * DRAG_DIRECTION; // 本次拖动的像素增量(含方向修正)
|
|
@@ -579,19 +603,118 @@ class ZoomTurntableRenderer {
|
|
|
579
603
|
this._scheduleRender();
|
|
580
604
|
}
|
|
581
605
|
|
|
582
|
-
_handTouchEnd() {
|
|
606
|
+
_handTouchEnd(e) {
|
|
583
607
|
dlog('touchend/cancel 触发', 'dragging=' + this.dragging, 'value=' + this.value);
|
|
584
|
-
|
|
585
|
-
|
|
608
|
+
var wasDragging = this.dragging;
|
|
609
|
+
this.dragging = false;
|
|
610
|
+
|
|
611
|
+
// 没怎么移动、按得也短 —— 当作「点击某个刻度」,直接滚过去并下发
|
|
612
|
+
var tapped = false;
|
|
613
|
+
if (wasDragging && this._tapStart && !this._tapStart.moved &&
|
|
614
|
+
Date.now() - this._tapStart.t <= TAP_MAX_MS) {
|
|
615
|
+
var touch = e && ((e.changedTouches && e.changedTouches[0]) || (e.touches && e.touches[0]));
|
|
616
|
+
if (touch) {
|
|
617
|
+
tapped = this._handleTapAt(touch.clientX, touch.clientY);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
this._tapStart = null;
|
|
621
|
+
|
|
622
|
+
if (wasDragging) {
|
|
586
623
|
this._cachedRect = null;
|
|
587
|
-
//
|
|
588
|
-
|
|
589
|
-
|
|
624
|
+
// tap 命中时由动画结束统一发 onChange / onChangeEnd,这里不能再按旧值发一次
|
|
625
|
+
if (!tapped) {
|
|
626
|
+
// 300ms 冷却期,吸收跨线程残余的 onChange 回写,避免松手后闪动
|
|
627
|
+
this._cooldownUntil = Date.now() + 300;
|
|
628
|
+
this.callMethod('onChangeEnd', this.value);
|
|
629
|
+
}
|
|
590
630
|
}
|
|
591
631
|
// 无论是否处于拖动,都要恢复原生手势,避免异常路径下 nativeDisabled 卡在 true
|
|
592
632
|
this.callMethod('onTouchEnd');
|
|
593
633
|
}
|
|
594
634
|
|
|
635
|
+
/**
|
|
636
|
+
* canvas 内坐标 → 绘制坐标系(逆掉 _render 里的 translate / rotate)。
|
|
637
|
+
* 竖屏只有一次平移;横屏画布整体旋转过 -90°,要连旋转一起逆算。
|
|
638
|
+
*/
|
|
639
|
+
_drawPointFromCanvas(cx, cy) {
|
|
640
|
+
var offX = -this.viewportLeft + (this.hitPaddingX || 0);
|
|
641
|
+
var offY = -this.viewportTop + (this.hitPaddingY || 0) + (this.contentOffsetY || 0);
|
|
642
|
+
if (this.orientation === 'right') {
|
|
643
|
+
// 正变换:canvas = ( py + offY, baseWidth - (px + offX) )
|
|
644
|
+
return { x: this.baseWidth - cy - offX, y: cx - offY };
|
|
645
|
+
}
|
|
646
|
+
return { x: cx - offX, y: cy - offY };
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
/**
|
|
650
|
+
* 点击刻度:由触点反推该处对应的倍率。
|
|
651
|
+
*
|
|
652
|
+
* 刻度盘整体旋转 rotation = POINTER_ANGLE - getAngle(progress),所以某个 p 的绘制角
|
|
653
|
+
* angle(p) = POINTER_ANGLE + (p - progress) * ARC_SPAN
|
|
654
|
+
* 反过来由触点的椭圆参数角 θ 就能解出 p:
|
|
655
|
+
* p = progress + (θ - POINTER_ANGLE) / ARC_SPAN
|
|
656
|
+
*
|
|
657
|
+
* @returns {boolean} 是否命中刻度带(未命中则按普通 touchend 走)
|
|
658
|
+
*/
|
|
659
|
+
_handleTapAt(clientX, clientY) {
|
|
660
|
+
var rect = this._cachedRect;
|
|
661
|
+
if (!rect) return false;
|
|
662
|
+
var pt = this._drawPointFromCanvas(clientX - rect.left, clientY - rect.top);
|
|
663
|
+
// 归一化到单位圆:半径 1 即刻度轨道所在的椭圆
|
|
664
|
+
var nx = (pt.x - this.CX) / this.rx;
|
|
665
|
+
var ny = (pt.y - this.CY) / this.ry;
|
|
666
|
+
var r = Math.sqrt(nx * nx + ny * ny);
|
|
667
|
+
if (r < TAP_HIT_MIN_R || r > TAP_HIT_MAX_R) {
|
|
668
|
+
dlog('tap 未命中刻度带,忽略', 'r=' + r.toFixed(2));
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
var theta = Math.atan2(ny, nx) * RAD_TO_DEG;
|
|
672
|
+
if (theta < 0) theta += 360; // 上半椭圆对应 [180, 360]
|
|
673
|
+
if (theta < START_ANGLE || theta > END_ANGLE) {
|
|
674
|
+
dlog('tap 角度超出弧段,忽略', 'theta=' + theta.toFixed(1));
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
var p = this.progress + (theta - POINTER_ANGLE) / ARC_SPAN;
|
|
678
|
+
if (p < 0) p = 0;
|
|
679
|
+
else if (p > 1) p = 1;
|
|
680
|
+
var target = this._snapValue(this._valueFromProgress(p));
|
|
681
|
+
dlog('tap 命中', 'theta=' + theta.toFixed(1), 'r=' + r.toFixed(2), 'target=' + target);
|
|
682
|
+
// 点的就是当前值:算命中(不再按旧值重复下发),但不用动
|
|
683
|
+
if (Math.abs(target - this.value) < this.step * 0.5) return true;
|
|
684
|
+
this._animateTo(target);
|
|
685
|
+
return true;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/** 点击后滚到目标刻度:动画期间只重绘,落位后才发一次 onChange + onChangeEnd */
|
|
689
|
+
_animateTo(target) {
|
|
690
|
+
var self = this;
|
|
691
|
+
var from = this.value;
|
|
692
|
+
var startAt = Date.now();
|
|
693
|
+
var raf = this._getRaf();
|
|
694
|
+
// 动画期间屏蔽外部受控回写,否则会被 value 回写打断
|
|
695
|
+
this._cooldownUntil = startAt + TAP_ANIM_MS + 300;
|
|
696
|
+
var tick = function () {
|
|
697
|
+
var t = (Date.now() - startAt) / TAP_ANIM_MS;
|
|
698
|
+
if (t > 1) t = 1;
|
|
699
|
+
var eased = 1 - Math.pow(1 - t, 3); // ease-out cubic
|
|
700
|
+
var v = from + (target - from) * eased;
|
|
701
|
+
self.value = self._snapValue(v);
|
|
702
|
+
self.progress = self.getProgress(self.value);
|
|
703
|
+
self._scheduleRender();
|
|
704
|
+
if (t < 1) {
|
|
705
|
+
raf(tick);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
self.value = target;
|
|
709
|
+
self.progress = self.getProgress(target);
|
|
710
|
+
self._scheduleRender();
|
|
711
|
+
self._cooldownUntil = Date.now() + 300;
|
|
712
|
+
self.callMethod('onChange', target);
|
|
713
|
+
self.callMethod('onChangeEnd', target);
|
|
714
|
+
};
|
|
715
|
+
raf(tick);
|
|
716
|
+
}
|
|
717
|
+
|
|
595
718
|
setupEventListeners() {
|
|
596
719
|
dlog(
|
|
597
720
|
'setupEventListeners',
|
|
@@ -608,12 +731,18 @@ class ZoomTurntableRenderer {
|
|
|
608
731
|
this.handTouchStart = this._handTouchStart.bind(this);
|
|
609
732
|
this.handTouchMove = this._handTouchMove.bind(this);
|
|
610
733
|
this.handTouchEnd = this._handTouchEnd.bind(this);
|
|
734
|
+
// 手势被系统打断走的是 cancel,不能当作「点击刻度」,先把 tap 起点清掉再收尾
|
|
735
|
+
var self = this;
|
|
736
|
+
this.handTouchCancel = function (e) {
|
|
737
|
+
self._tapStart = null;
|
|
738
|
+
self._handTouchEnd(e);
|
|
739
|
+
};
|
|
611
740
|
this.canvas.addEventListener('touchstart', this.handTouchStart, false);
|
|
612
741
|
this.canvas.addEventListener('touchmove', this.handTouchMove, false);
|
|
613
742
|
this.canvas.addEventListener('touchend', this.handTouchEnd, false);
|
|
614
743
|
// 手势被系统打断时 touchend 不会派发,必须补 touchcancel,
|
|
615
744
|
// 否则 ty.nativeDisabled 会卡在 true,之后播放器所有原生手势都失效
|
|
616
|
-
this.canvas.addEventListener('touchcancel', this.
|
|
745
|
+
this.canvas.addEventListener('touchcancel', this.handTouchCancel, false);
|
|
617
746
|
}
|
|
618
747
|
|
|
619
748
|
destroy() {
|
|
@@ -627,7 +756,7 @@ class ZoomTurntableRenderer {
|
|
|
627
756
|
this.canvas.removeEventListener('touchstart', this.handTouchStart, false);
|
|
628
757
|
this.canvas.removeEventListener('touchmove', this.handTouchMove, false);
|
|
629
758
|
this.canvas.removeEventListener('touchend', this.handTouchEnd, false);
|
|
630
|
-
this.canvas.removeEventListener('touchcancel', this.
|
|
759
|
+
this.canvas.removeEventListener('touchcancel', this.handTouchCancel, false);
|
|
631
760
|
}
|
|
632
761
|
}
|
|
633
762
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { ComponentConfigProps } from '../../interface';
|
|
2
|
-
import { ZoomSkill } from './lensSkill';
|
|
3
2
|
type Options = ComponentConfigProps & {
|
|
4
3
|
/** 业务侧的功能开关;实际是否渲染仍由 ipc_multi_zoom 解析出的 cnt 决定 */
|
|
5
4
|
showZoomTurntable?: boolean;
|
|
6
|
-
/** 调试用:直接注入镜头能力,跳过 ipc_multi_zoom DP 解析 */
|
|
7
|
-
mockZoomSkill?: ZoomSkill | boolean;
|
|
8
5
|
/** 跳过能力加载:Entry 常驻负责加载,Dock 作为消费方传 true 避免重复解析 */
|
|
9
6
|
skipLoad?: boolean;
|
|
10
7
|
};
|
|
@@ -15,10 +12,12 @@ type Options = ComponentConfigProps & {
|
|
|
15
12
|
export declare const useLensZoom: (options: Options) => {
|
|
16
13
|
/** 是否具备多镜头变焦能力(不具备则整个 widget 不出现) */
|
|
17
14
|
multiLensReady: boolean;
|
|
18
|
-
zoomSkill: ZoomSkill | null;
|
|
15
|
+
zoomSkill: import("./lensSkill").ZoomSkill | null;
|
|
19
16
|
/** 镜头列表(按 value 升序),用于渲染倍率 tab */
|
|
20
17
|
lenses: import("./lensSkill").Lens[];
|
|
21
18
|
lensIndex: number;
|
|
19
|
+
/** 正在切往的目标镜头下标;-1 表示没有切换进行中 */
|
|
20
|
+
switchingIndex: number;
|
|
22
21
|
/** 画面绝对倍率 */
|
|
23
22
|
curM: number;
|
|
24
23
|
min: number;
|