@tarojs/components-react 4.2.1-beta.2 → 4.2.1

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 (77) hide show
  1. package/dist/components/image/index.js +3 -5
  2. package/dist/components/image/index.js.map +1 -1
  3. package/dist/components/picker/index.js +38 -110
  4. package/dist/components/picker/index.js.map +1 -1
  5. package/dist/components/picker/picker-group.js +127 -477
  6. package/dist/components/picker/picker-group.js.map +1 -1
  7. package/dist/components/refresher/index.js +5 -5
  8. package/dist/components/refresher/index.js.map +1 -1
  9. package/dist/components/scroll-view/index.js +36 -80
  10. package/dist/components/scroll-view/index.js.map +1 -1
  11. package/dist/index.css +1 -1
  12. package/dist/index.js +2 -6
  13. package/dist/index.js.map +1 -1
  14. package/dist/original/components/image/index.js +3 -5
  15. package/dist/original/components/image/index.js.map +1 -1
  16. package/dist/original/components/image/style/index.scss +1 -5
  17. package/dist/original/components/picker/index.js +38 -110
  18. package/dist/original/components/picker/index.js.map +1 -1
  19. package/dist/original/components/picker/picker-group.js +127 -477
  20. package/dist/original/components/picker/picker-group.js.map +1 -1
  21. package/dist/original/components/picker/style/index.scss +8 -9
  22. package/dist/original/components/refresher/index.js +5 -5
  23. package/dist/original/components/refresher/index.js.map +1 -1
  24. package/dist/original/components/scroll-view/index.js +36 -80
  25. package/dist/original/components/scroll-view/index.js.map +1 -1
  26. package/dist/original/index.js +2 -6
  27. package/dist/original/index.js.map +1 -1
  28. package/dist/solid/components/image/index.js +3 -5
  29. package/dist/solid/components/image/index.js.map +1 -1
  30. package/dist/solid/components/picker/index.js +45 -121
  31. package/dist/solid/components/picker/index.js.map +1 -1
  32. package/dist/solid/components/picker/picker-group.js +151 -500
  33. package/dist/solid/components/picker/picker-group.js.map +1 -1
  34. package/dist/solid/components/refresher/index.js +5 -5
  35. package/dist/solid/components/refresher/index.js.map +1 -1
  36. package/dist/solid/components/scroll-view/index.js +40 -84
  37. package/dist/solid/components/scroll-view/index.js.map +1 -1
  38. package/dist/solid/index.css +1 -1
  39. package/dist/solid/index.js +1 -1
  40. package/package.json +6 -8
  41. package/dist/components/map/MapContext.js +0 -628
  42. package/dist/components/map/MapContext.js.map +0 -1
  43. package/dist/components/map/MapCustomCallout.js +0 -91
  44. package/dist/components/map/MapCustomCallout.js.map +0 -1
  45. package/dist/components/map/common.js +0 -4
  46. package/dist/components/map/common.js.map +0 -1
  47. package/dist/components/map/createMapContext.js +0 -34
  48. package/dist/components/map/createMapContext.js.map +0 -1
  49. package/dist/components/map/handler.js +0 -50
  50. package/dist/components/map/handler.js.map +0 -1
  51. package/dist/components/map/index.js +0 -329
  52. package/dist/components/map/index.js.map +0 -1
  53. package/dist/components/switch/index.js +0 -125
  54. package/dist/components/switch/index.js.map +0 -1
  55. package/dist/components/switch/style/index.scss.js +0 -4
  56. package/dist/components/switch/style/index.scss.js.map +0 -1
  57. package/dist/contexts/ScrollElementContext.js +0 -6
  58. package/dist/contexts/ScrollElementContext.js.map +0 -1
  59. package/dist/original/components/map/MapContext.js +0 -628
  60. package/dist/original/components/map/MapContext.js.map +0 -1
  61. package/dist/original/components/map/MapCustomCallout.js +0 -91
  62. package/dist/original/components/map/MapCustomCallout.js.map +0 -1
  63. package/dist/original/components/map/common.js +0 -4
  64. package/dist/original/components/map/common.js.map +0 -1
  65. package/dist/original/components/map/createMapContext.js +0 -34
  66. package/dist/original/components/map/createMapContext.js.map +0 -1
  67. package/dist/original/components/map/handler.js +0 -50
  68. package/dist/original/components/map/handler.js.map +0 -1
  69. package/dist/original/components/map/index.js +0 -329
  70. package/dist/original/components/map/index.js.map +0 -1
  71. package/dist/original/components/switch/index.js +0 -125
  72. package/dist/original/components/switch/index.js.map +0 -1
  73. package/dist/original/components/switch/style/index.scss +0 -35
  74. package/dist/original/contexts/ScrollElementContext.js +0 -6
  75. package/dist/original/contexts/ScrollElementContext.js.map +0 -1
  76. package/dist/solid/contexts/ScrollElementContext.js +0 -6
  77. package/dist/solid/contexts/ScrollElementContext.js.map +0 -1
@@ -3,31 +3,6 @@ import { View, ScrollView } from '@tarojs/components';
3
3
  import Taro from '@tarojs/taro';
4
4
  import * as React from 'react';
5
5
 
6
- function requestAccessibilityFocusOnView(node) {
7
- if (node == null) return;
8
- const fn = Taro.setAccessibilityFocus;
9
- if (typeof fn !== 'function') return;
10
- fn({
11
- viewRef: {
12
- current: node
13
- }
14
- });
15
- }
16
- /** 部分端同 id 连续 scrollIntoView 不生效:先清空再在下一宏任务设回目标 id */
17
- function usePickerItemScrollIntoView() {
18
- const [scrollIntoView, setScrollIntoView] = React.useState('');
19
- const pulseRef = React.useRef(0);
20
- const scrollToItemId = React.useCallback(itemId => {
21
- pulseRef.current += 1;
22
- const token = pulseRef.current;
23
- setScrollIntoView('');
24
- setTimeout(() => {
25
- if (pulseRef.current !== token) return;
26
- setScrollIntoView(itemId);
27
- }, 0);
28
- }, []);
29
- return [scrollIntoView, scrollToItemId];
30
- }
31
6
  // 定义常量
32
7
  const PICKER_LINE_HEIGHT = 34; // px
33
8
  const PICKER_VISIBLE_ITEMS = 7; // 可见行数
@@ -38,85 +13,42 @@ const getIndicatorStyle = lineColor => {
38
13
  borderBottomColor: lineColor
39
14
  };
40
15
  };
41
- // 大屏方案版本要求
42
- const MIN_DESIGN_APP_VERSION = 16;
43
- // 判断是否启用测量值缩放适配(true=启用, false=使用系统侧缩放)
44
- const resolveUseMeasuredScale = res => {
45
- // H5/weapp 不参与大屏系数
46
- if (process.env.TARO_ENV === 'h5' || process.env.TARO_ENV === 'weapp') {
47
- return false;
48
- }
49
- const designAppVersionRaw = res.designAppVersion;
50
- const designAppVersionMajor = designAppVersionRaw != null ? parseInt(String(designAppVersionRaw).trim(), 10) : Number.NaN;
51
- if (!Number.isFinite(designAppVersionMajor) || designAppVersionMajor < MIN_DESIGN_APP_VERSION) {
52
- return false;
53
- }
54
- const platform = String(res.platform || '').toLowerCase();
55
- if (platform === 'harmony' || platform === 'android' || platform === 'ios') {
56
- return true;
57
- }
58
- return false;
59
- };
60
- // 辅助函数:计算 lengthScaleRatio
61
- const calculateLengthScaleRatio = res => {
62
- let lengthScaleRatio = res === null || res === void 0 ? void 0 : res.lengthScaleRatio;
63
- if (lengthScaleRatio == null || lengthScaleRatio === 0) {
64
- console.warn('Taro.getSystemInfo: lengthScaleRatio 不存在,使用计算值');
65
- lengthScaleRatio = 1;
66
- if (res.windowWidth < 320) {
67
- lengthScaleRatio = res.windowWidth / 320;
68
- } else if (res.windowWidth >= 400 && res.windowWidth < 600) {
69
- lengthScaleRatio = res.windowWidth / 400;
70
- }
71
- const shortSide = res.windowWidth < res.windowHeight ? res.windowWidth : res.windowHeight;
72
- const isBigScreen = shortSide >= 600;
73
- if (isBigScreen) {
74
- lengthScaleRatio = shortSide / 720;
75
- }
76
- }
77
- return lengthScaleRatio;
78
- };
79
16
  // 辅助函数:获取系统信息的 lengthScaleRatio 并设置 targetScrollTop
80
- const setTargetScrollTopWithScale = function (setTargetScrollTop, baseValue, randomOffset) {
81
- let lengthScaleRatio = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
17
+ const setTargetScrollTopWithScale = (setTargetScrollTop, baseValue, randomOffset) => {
82
18
  // H5 和 weapp 不参与放大计算,直接使用 baseValue
83
19
  if (process.env.TARO_ENV === 'h5' || process.env.TARO_ENV === 'weapp') {
84
20
  const finalValue = randomOffset !== undefined ? baseValue + randomOffset : baseValue;
85
21
  setTargetScrollTop(finalValue);
86
22
  return;
87
23
  }
88
- if (process.env.TARO_PLATFORM === 'harmony') {
89
- const scaledValue = baseValue;
90
- const finalValue = randomOffset !== undefined ? scaledValue + randomOffset : scaledValue;
91
- setTargetScrollTop(finalValue);
92
- } else {
93
- const scaledValue = baseValue * lengthScaleRatio;
94
- const finalValue = randomOffset !== undefined ? scaledValue + randomOffset : scaledValue;
95
- setTargetScrollTop(finalValue);
96
- }
97
- };
98
- // 根据 scrollTop 计算选中索引
99
- // 系统侧缩放模式:scrollHeight 已被系统缩放,直接相除即可
100
- // 自行缩放模式:需要除以 ratio 换算(harmony 特殊处理,ratio 已内嵌在 itemHeight 中)
101
- const getSelectedIndex = function (scrollTop, itemHeight) {
102
- let lengthScaleRatio = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1;
103
- let useMeasuredScale = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
104
- if (!useMeasuredScale || process.env.TARO_PLATFORM === 'harmony') {
105
- return Math.round(scrollTop / itemHeight);
106
- }
107
- return Math.round(scrollTop / lengthScaleRatio / itemHeight);
108
- };
109
- // 计算单项高度(返回设计稿值)
110
- const calculateItemHeight = function (scrollView, lengthScaleRatio) {
111
- let useMeasuredScale = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
112
- if (process.env.TARO_PLATFORM === 'harmony') {
113
- return useMeasuredScale ? PICKER_LINE_HEIGHT * lengthScaleRatio : PICKER_LINE_HEIGHT;
114
- }
115
- if (scrollView && (scrollView === null || scrollView === void 0 ? void 0 : scrollView.scrollHeight)) {
116
- return useMeasuredScale ? scrollView.scrollHeight / lengthScaleRatio / scrollView.childNodes.length : scrollView.scrollHeight / scrollView.childNodes.length;
117
- }
118
- console.warn('Height measurement anomaly');
119
- return PICKER_LINE_HEIGHT;
24
+ Taro.getSystemInfo({
25
+ success: res => {
26
+ let lengthScaleRatio = res === null || res === void 0 ? void 0 : res.lengthScaleRatio;
27
+ if (lengthScaleRatio == null || lengthScaleRatio === 0) {
28
+ console.warn('Taro.getSystemInfo: lengthScaleRatio 不存在,使用计算值');
29
+ lengthScaleRatio = 1;
30
+ if (res.windowWidth < 320) {
31
+ lengthScaleRatio = res.windowWidth / 320;
32
+ } else if (res.windowWidth >= 400 && res.windowWidth < 600) {
33
+ lengthScaleRatio = res.windowWidth / 400;
34
+ }
35
+ const shortSide = res.windowWidth < res.windowHeight ? res.windowWidth : res.windowHeight;
36
+ const isBigScreen = shortSide >= 600;
37
+ if (isBigScreen) {
38
+ lengthScaleRatio = shortSide / 720;
39
+ }
40
+ }
41
+ const scaledValue = baseValue * lengthScaleRatio;
42
+ const finalValue = randomOffset !== undefined ? scaledValue + randomOffset : scaledValue;
43
+ setTargetScrollTop(finalValue);
44
+ },
45
+ fail: err => {
46
+ console.error('获取系统信息失败:', err);
47
+ // 失败时使用默认值 1
48
+ const finalValue = randomOffset !== undefined ? baseValue + randomOffset : baseValue;
49
+ setTargetScrollTop(finalValue);
50
+ }
51
+ });
120
52
  };
121
53
  function PickerGroupBasic(props) {
122
54
  const {
@@ -127,89 +59,66 @@ function PickerGroupBasic(props) {
127
59
  onColumnChange,
128
60
  selectedIndex = 0,
129
61
  // 使用selectedIndex参数,默认为0
130
- colors = {},
131
- enableClickItemScroll = true
62
+ colors = {}
132
63
  } = props;
133
64
  const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
134
65
  const [targetScrollTop, setTargetScrollTop] = React.useState(0);
135
- const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
136
66
  const scrollViewRef = React.useRef(null);
137
67
  const itemRefs = React.useRef([]);
138
- const selectedIndexPropRef = React.useRef(selectedIndex);
139
- selectedIndexPropRef.current = selectedIndex;
140
- const syncScrollFromPropsRef = React.useRef(false);
141
- const pendingScrollSettleFocusRef = React.useRef(false);
142
68
  // 使用selectedIndex初始化当前索引
143
69
  const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
144
70
  // 触摸状态用于优化用户体验
145
- const isTouchingRef = React.useRef(false);
146
- const lengthScaleRatioRef = React.useRef(1);
147
- const useMeasuredScaleRef = React.useRef(false);
71
+ const [isTouching, setIsTouching] = React.useState(false);
148
72
  const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
149
- // 初始化时计算 lengthScaleRatio 并判定缩放模式
150
73
  React.useEffect(() => {
151
- Taro.getSystemInfo({
152
- success: res => {
153
- lengthScaleRatioRef.current = calculateLengthScaleRatio(res);
154
- useMeasuredScaleRef.current = resolveUseMeasuredScale(res);
155
- itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
156
- },
157
- fail: () => {
158
- lengthScaleRatioRef.current = 1;
159
- useMeasuredScaleRef.current = false;
74
+ var _a;
75
+ if (process.env.TARO_PLATFORM !== 'harmony') {
76
+ if (scrollViewRef.current && ((_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollHeight)) {
77
+ itemHeightRef.current = scrollViewRef.current.scrollHeight / scrollViewRef.current.childNodes.length;
78
+ } else {
79
+ console.warn('Height measurement anomaly');
160
80
  }
161
- });
162
- }, []);
163
- React.useEffect(() => {
164
- itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
81
+ }
165
82
  }, [range.length]); // 只在range长度变化时重新计算
166
- // props 的 selectedIndex 变化:滚至对应项并短时标记程序化滚动(syncScrollFromPropsRef)
83
+ // 获取选中的索引
84
+ const getSelectedIndex = scrollTop => {
85
+ return Math.round(scrollTop / itemHeightRef.current);
86
+ };
87
+ // 当selectedIndex变化时,调整滚动位置
167
88
  React.useEffect(() => {
168
- if (scrollViewRef.current && range.length > 0 && !isTouchingRef.current) {
169
- syncScrollFromPropsRef.current = true;
89
+ if (scrollViewRef.current && range.length > 0 && !isTouching) {
170
90
  const baseValue = selectedIndex * itemHeightRef.current;
171
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, undefined, lengthScaleRatioRef.current);
91
+ setTargetScrollTopWithScale(setTargetScrollTop, baseValue);
172
92
  setCurrentIndex(selectedIndex);
173
- const tid = setTimeout(() => {
174
- syncScrollFromPropsRef.current = false;
175
- }, 400);
176
- return () => clearTimeout(tid);
177
93
  }
178
94
  }, [selectedIndex, range]);
179
95
  // 是否处于归中状态
180
96
  const isCenterTimerId = React.useRef(null);
181
- // 滚动静止后归中并同步选中
97
+ // 简化为直接在滚动结束时通知父组件
182
98
  const handleScrollEnd = () => {
183
99
  if (!scrollViewRef.current) return;
184
100
  if (isCenterTimerId.current) {
185
101
  clearTimeout(isCenterTimerId.current);
186
102
  isCenterTimerId.current = null;
187
103
  }
188
- // 100ms 内无新滚动则归中并提交索引
104
+ // 做一个0.1s延时 0.1s之内没有新的滑动 则把选项归到中间 然后更新选中项
189
105
  isCenterTimerId.current = setTimeout(() => {
190
106
  if (!scrollViewRef.current) return;
191
107
  const scrollTop = scrollViewRef.current.scrollTop;
192
- const newIndex = getSelectedIndex(scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
193
- const allowA11yFocus = !syncScrollFromPropsRef.current;
194
- const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
195
- isTouchingRef.current = false;
108
+ const newIndex = getSelectedIndex(scrollTop);
109
+ setIsTouching(false);
196
110
  const baseValue = newIndex * itemHeightRef.current;
197
111
  const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
198
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset, lengthScaleRatioRef.current);
112
+ setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset);
199
113
  updateIndex(newIndex, columnId);
200
114
  onColumnChange === null || onColumnChange === void 0 ? void 0 : onColumnChange({
201
115
  columnId,
202
116
  index: newIndex
203
117
  });
204
- pendingScrollSettleFocusRef.current = false;
205
- if (allowA11yFocus && shouldFocusOnScrollSettle) {
206
- requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
207
- }
208
- syncScrollFromPropsRef.current = false;
209
118
  isCenterTimerId.current = null;
210
119
  }, 100);
211
120
  };
212
- // 滚动中:按 scrollTop 更新高亮索引
121
+ // 滚动处理 - 在滚动时计算索引然后更新选中项样式
213
122
  const handleScroll = () => {
214
123
  if (!scrollViewRef.current) return;
215
124
  if (isCenterTimerId.current) {
@@ -217,17 +126,7 @@ function PickerGroupBasic(props) {
217
126
  isCenterTimerId.current = null;
218
127
  }
219
128
  const scrollTop = scrollViewRef.current.scrollTop;
220
- const ih = itemHeightRef.current;
221
- const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
222
- const spi = selectedIndexPropRef.current;
223
- if (newIndex !== spi) {
224
- if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
225
- pendingScrollSettleFocusRef.current = true;
226
- }
227
- if (isTouchingRef.current) {
228
- syncScrollFromPropsRef.current = false;
229
- }
230
- }
129
+ const newIndex = getSelectedIndex(scrollTop);
231
130
  if (newIndex !== currentIndex) {
232
131
  setCurrentIndex(newIndex);
233
132
  }
@@ -235,14 +134,11 @@ function PickerGroupBasic(props) {
235
134
  // 渲染选项
236
135
  const pickerItem = range.map((item, index) => {
237
136
  const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
238
- return createComponent(View, mergeProps({
137
+ return createComponent(View, {
239
138
  id: `picker-item-${columnId}-${index}`,
240
139
  key: index,
241
140
  ref: el => itemRefs.current[index] = el,
242
- className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
243
- }, enableClickItemScroll ? {
244
- onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
245
- } : {}, {
141
+ className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`,
246
142
  get style() {
247
143
  return {
248
144
  height: PICKER_LINE_HEIGHT,
@@ -250,7 +146,7 @@ function PickerGroupBasic(props) {
250
146
  };
251
147
  },
252
148
  children: content
253
- }));
149
+ });
254
150
  });
255
151
  const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
256
152
  key: `blank-top-${idx}`,
@@ -265,10 +161,6 @@ function PickerGroupBasic(props) {
265
161
  height: PICKER_LINE_HEIGHT
266
162
  }
267
163
  }))];
268
- const clickScrollViewProps = enableClickItemScroll ? {
269
- scrollIntoView,
270
- scrollIntoViewAlignment: 'center'
271
- } : {};
272
164
  return createComponent(View, {
273
165
  className: "taro-picker__group",
274
166
  get children() {
@@ -278,7 +170,7 @@ function PickerGroupBasic(props) {
278
170
  className: "taro-picker__indicator"
279
171
  }, indicatorStyle ? {
280
172
  style: indicatorStyle
281
- } : {})), createComponent(ScrollView, mergeProps({
173
+ } : {})), createComponent(ScrollView, {
282
174
  ref: scrollViewRef,
283
175
  scrollY: true,
284
176
  showScrollbar: false,
@@ -286,16 +178,13 @@ function PickerGroupBasic(props) {
286
178
  style: {
287
179
  height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
288
180
  },
289
- scrollTop: targetScrollTop
290
- }, clickScrollViewProps, {
181
+ scrollTop: targetScrollTop,
291
182
  onScroll: handleScroll,
292
- onTouchStart: () => {
293
- isTouchingRef.current = true;
294
- },
183
+ onTouchStart: () => setIsTouching(true),
295
184
  onScrollEnd: handleScrollEnd,
296
185
  scrollWithAnimation: true,
297
186
  children: realPickerItems
298
- }))];
187
+ })];
299
188
  }
300
189
  });
301
190
  }
@@ -307,189 +196,63 @@ function PickerGroupTime(props) {
307
196
  columnId,
308
197
  updateIndex,
309
198
  selectedIndex = 0,
310
- colors = {},
311
- timeA11yLimitFocus,
312
- onTimeA11yLimitFocusConsumed,
313
- timeA11yLimitEventNonce,
314
- enableClickItemScroll = true
199
+ colors = {}
315
200
  } = props;
316
201
  const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
317
202
  const [targetScrollTop, setTargetScrollTop] = React.useState(0);
318
- const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
319
203
  const scrollViewRef = React.useRef(null);
320
204
  const itemRefs = React.useRef([]);
321
- const selectedIndexPropRef = React.useRef(selectedIndex);
322
- selectedIndexPropRef.current = selectedIndex;
323
- const syncScrollFromPropsRef = React.useRef(false);
324
- const prevLimitEventNonceRef = React.useRef(undefined);
325
- const suppressScrollSettleFocusNonceRef = React.useRef(null);
326
- const pendingScrollSettleFocusRef = React.useRef(false);
327
- const pendingLimitFocusRef = React.useRef(null);
328
- const limitFocusTimerRef = React.useRef(null);
329
205
  const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
330
- const isTouchingRef = React.useRef(false);
331
- const lengthScaleRatioRef = React.useRef(1);
332
- const useMeasuredScaleRef = React.useRef(false);
206
+ const [isTouching, setIsTouching] = React.useState(false);
333
207
  const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
334
- const clearLimitFocusTimer = React.useCallback(() => {
335
- if (limitFocusTimerRef.current) {
336
- clearTimeout(limitFocusTimerRef.current);
337
- limitFocusTimerRef.current = null;
338
- }
339
- }, []);
340
- const tryFocusPendingLimit = React.useCallback(() => {
341
- const pending = pendingLimitFocusRef.current;
342
- const scrollView = scrollViewRef.current;
343
- if (!pending || !scrollView) return;
344
- const visualIndex = getSelectedIndex(scrollView.scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
345
- const isStable = visualIndex === pending.index;
346
- if (!isStable) {
347
- if (pending.attempts >= 20) {
348
- pendingLimitFocusRef.current = null;
349
- if (suppressScrollSettleFocusNonceRef.current === pending.nonce) {
350
- suppressScrollSettleFocusNonceRef.current = null;
351
- }
352
- onTimeA11yLimitFocusConsumed === null || onTimeA11yLimitFocusConsumed === void 0 ? void 0 : onTimeA11yLimitFocusConsumed();
353
- return;
354
- }
355
- pending.attempts += 1;
356
- clearLimitFocusTimer();
357
- limitFocusTimerRef.current = setTimeout(() => {
358
- tryFocusPendingLimit();
359
- }, 50);
360
- return;
361
- }
362
- const node = itemRefs.current[pending.index];
363
- pendingLimitFocusRef.current = null;
364
- if (suppressScrollSettleFocusNonceRef.current === pending.nonce) {
365
- suppressScrollSettleFocusNonceRef.current = null;
366
- }
367
- clearLimitFocusTimer();
368
- requestAccessibilityFocusOnView(node);
369
- onTimeA11yLimitFocusConsumed === null || onTimeA11yLimitFocusConsumed === void 0 ? void 0 : onTimeA11yLimitFocusConsumed();
370
- }, [clearLimitFocusTimer, onTimeA11yLimitFocusConsumed]);
371
- const schedulePendingLimitFocus = React.useCallback(() => {
372
- if (!pendingLimitFocusRef.current) return;
373
- clearLimitFocusTimer();
374
- limitFocusTimerRef.current = setTimeout(() => {
375
- tryFocusPendingLimit();
376
- }, 100);
377
- }, [clearLimitFocusTimer, tryFocusPendingLimit]);
378
- React.useEffect(() => clearLimitFocusTimer, [clearLimitFocusTimer]);
379
- // 初始化时计算 lengthScaleRatio 并判定缩放模式
380
208
  React.useEffect(() => {
381
- Taro.getSystemInfo({
382
- success: res => {
383
- lengthScaleRatioRef.current = calculateLengthScaleRatio(res);
384
- useMeasuredScaleRef.current = resolveUseMeasuredScale(res);
385
- itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
386
- },
387
- fail: () => {
388
- lengthScaleRatioRef.current = 1;
389
- useMeasuredScaleRef.current = false;
209
+ var _a;
210
+ if (process.env.TARO_PLATFORM !== 'harmony') {
211
+ if (scrollViewRef.current && ((_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollHeight)) {
212
+ itemHeightRef.current = scrollViewRef.current.scrollHeight / scrollViewRef.current.childNodes.length;
213
+ } else {
214
+ console.warn('Height measurement anomaly');
390
215
  }
391
- });
392
- }, []);
393
- React.useEffect(() => {
394
- itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
216
+ }
395
217
  }, [range.length]); // 只在range长度变化时重新计算
396
- // props selectedIndex 变化:滚至对应项并短时标记程序化滚动(syncScrollFromPropsRef)
218
+ const getSelectedIndex = scrollTop => {
219
+ return Math.round(scrollTop / itemHeightRef.current);
220
+ };
221
+ // 当selectedIndex变化时,调整滚动位置
397
222
  React.useEffect(() => {
398
- if (scrollViewRef.current && range.length > 0 && !isTouchingRef.current) {
399
- syncScrollFromPropsRef.current = true;
223
+ if (scrollViewRef.current && range.length > 0 && !isTouching) {
400
224
  const baseValue = selectedIndex * itemHeightRef.current;
401
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, undefined, lengthScaleRatioRef.current);
225
+ setTargetScrollTopWithScale(setTargetScrollTop, baseValue);
402
226
  setCurrentIndex(selectedIndex);
403
- const tid = setTimeout(() => {
404
- syncScrollFromPropsRef.current = false;
405
- }, 400);
406
- return () => clearTimeout(tid);
407
227
  }
408
228
  }, [selectedIndex, range]);
409
- // time 限位 nonce 变更:强制对齐 scrollTop(避免 remount 打断读屏焦点)
410
- React.useEffect(() => {
411
- if (!timeA11yLimitEventNonce) return;
412
- if (!scrollViewRef.current || range.length === 0 || isTouchingRef.current) return;
413
- syncScrollFromPropsRef.current = true;
414
- const baseValue = selectedIndex * itemHeightRef.current;
415
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, Math.random() * 0.001, lengthScaleRatioRef.current);
416
- setCurrentIndex(selectedIndex);
417
- const tid = setTimeout(() => {
418
- syncScrollFromPropsRef.current = false;
419
- }, 400);
420
- return () => clearTimeout(tid);
421
- }, [timeA11yLimitEventNonce]); // 仅依赖 nonce,其余用 ref
422
- React.useLayoutEffect(() => {
423
- if (timeA11yLimitEventNonce === undefined) return;
424
- const prev = prevLimitEventNonceRef.current;
425
- prevLimitEventNonceRef.current = timeA11yLimitEventNonce;
426
- if (timeA11yLimitEventNonce > 0 && (prev === undefined || timeA11yLimitEventNonce > prev)) {
427
- suppressScrollSettleFocusNonceRef.current = timeA11yLimitEventNonce;
428
- }
429
- }, [timeA11yLimitEventNonce]);
430
- // 限位后登记本列待聚焦项,稳定后再 requestAccessibilityFocus
431
- React.useEffect(() => {
432
- const req = timeA11yLimitFocus;
433
- if (!req || req.columnId !== columnId) return;
434
- const idx = selectedIndex;
435
- const nonce = req.nonce;
436
- pendingScrollSettleFocusRef.current = false;
437
- pendingLimitFocusRef.current = {
438
- index: idx,
439
- nonce,
440
- attempts: 0
441
- };
442
- schedulePendingLimitFocus();
443
- }, [timeA11yLimitFocus, columnId, selectedIndex, schedulePendingLimitFocus]);
444
229
  // 是否处于归中状态
445
230
  const isCenterTimerId = React.useRef(null);
446
- // 滚动静止后归中并同步选中
231
+ // 简化为直接在滚动结束时通知父组件
447
232
  const handleScrollEnd = () => {
448
233
  if (!scrollViewRef.current) return;
449
234
  if (isCenterTimerId.current) {
450
235
  clearTimeout(isCenterTimerId.current);
451
236
  isCenterTimerId.current = null;
452
237
  }
453
- // 100ms 内无新滚动则归中并提交索引
238
+ // 做一个0.1s延时 0.1s之内没有新的滑动 则把选项归到中间 然后更新选中项
454
239
  isCenterTimerId.current = setTimeout(() => {
455
240
  if (!scrollViewRef.current) return;
456
241
  const scrollTop = scrollViewRef.current.scrollTop;
457
- const newIndex = getSelectedIndex(scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
458
- const allowA11yFocus = !syncScrollFromPropsRef.current;
459
- const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
460
- isTouchingRef.current = false;
242
+ const newIndex = getSelectedIndex(scrollTop);
243
+ setIsTouching(false);
244
+ // 调用updateIndex执行限位逻辑,获取是否触发了限位
461
245
  const isLimited = Boolean(updateIndex(newIndex, columnId, true));
462
- const hasPendingLimitFocus = pendingLimitFocusRef.current != null;
463
- if (isLimited) {
464
- pendingScrollSettleFocusRef.current = false;
465
- }
246
+ // 如果没有触发限位,才执行归中逻辑
466
247
  if (!isLimited) {
467
248
  const baseValue = newIndex * itemHeightRef.current;
468
249
  const randomOffset = Math.random() * 0.001;
469
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset, lengthScaleRatioRef.current);
470
- }
471
- if (!isLimited && hasPendingLimitFocus) {
472
- pendingScrollSettleFocusRef.current = false;
473
- schedulePendingLimitFocus();
474
- syncScrollFromPropsRef.current = false;
475
- isCenterTimerId.current = null;
476
- return;
250
+ setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset);
477
251
  }
478
- if (!isLimited && pendingLimitFocusRef.current == null && suppressScrollSettleFocusNonceRef.current != null) {
479
- suppressScrollSettleFocusNonceRef.current = null;
480
- }
481
- const suppressByLimitNonce = suppressScrollSettleFocusNonceRef.current != null;
482
- if (!isLimited && allowA11yFocus && shouldFocusOnScrollSettle) {
483
- pendingScrollSettleFocusRef.current = false;
484
- if (!suppressByLimitNonce) {
485
- requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
486
- }
487
- }
488
- syncScrollFromPropsRef.current = false;
489
252
  isCenterTimerId.current = null;
490
253
  }, 100);
491
254
  };
492
- // 滚动中:按 scrollTop 更新高亮索引
255
+ // 滚动处理
493
256
  const handleScroll = () => {
494
257
  if (!scrollViewRef.current) return;
495
258
  if (isCenterTimerId.current) {
@@ -497,35 +260,19 @@ function PickerGroupTime(props) {
497
260
  isCenterTimerId.current = null;
498
261
  }
499
262
  const scrollTop = scrollViewRef.current.scrollTop;
500
- const ih = itemHeightRef.current;
501
- const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
502
- const spi = selectedIndexPropRef.current;
503
- if (newIndex !== spi) {
504
- if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
505
- pendingScrollSettleFocusRef.current = true;
506
- }
507
- if (isTouchingRef.current) {
508
- syncScrollFromPropsRef.current = false;
509
- }
510
- }
263
+ const newIndex = getSelectedIndex(scrollTop);
511
264
  if (newIndex !== currentIndex) {
512
265
  setCurrentIndex(newIndex);
513
266
  }
514
- if (pendingLimitFocusRef.current) {
515
- schedulePendingLimitFocus();
516
- }
517
267
  };
518
268
  // 渲染选项
519
269
  const pickerItem = range.map((item, index) => {
520
270
  const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
521
- return createComponent(View, mergeProps({
271
+ return createComponent(View, {
522
272
  id: `picker-item-${columnId}-${index}`,
523
273
  key: index,
524
274
  ref: el => itemRefs.current[index] = el,
525
- className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
526
- }, enableClickItemScroll ? {
527
- onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
528
- } : {}, {
275
+ className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`,
529
276
  get style() {
530
277
  return {
531
278
  height: PICKER_LINE_HEIGHT,
@@ -533,7 +280,7 @@ function PickerGroupTime(props) {
533
280
  };
534
281
  },
535
282
  children: content
536
- }));
283
+ });
537
284
  });
538
285
  const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
539
286
  key: `blank-top-${idx}`,
@@ -548,10 +295,6 @@ function PickerGroupTime(props) {
548
295
  height: PICKER_LINE_HEIGHT
549
296
  }
550
297
  }))];
551
- const clickScrollViewProps = enableClickItemScroll ? {
552
- scrollIntoView,
553
- scrollIntoViewAlignment: 'center'
554
- } : {};
555
298
  return createComponent(View, {
556
299
  className: "taro-picker__group",
557
300
  get children() {
@@ -561,7 +304,7 @@ function PickerGroupTime(props) {
561
304
  className: "taro-picker__indicator"
562
305
  }, indicatorStyle ? {
563
306
  style: indicatorStyle
564
- } : {})), createComponent(ScrollView, mergeProps({
307
+ } : {})), createComponent(ScrollView, {
565
308
  ref: scrollViewRef,
566
309
  scrollY: true,
567
310
  showScrollbar: false,
@@ -569,16 +312,13 @@ function PickerGroupTime(props) {
569
312
  style: {
570
313
  height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
571
314
  },
572
- scrollTop: targetScrollTop
573
- }, clickScrollViewProps, {
315
+ scrollTop: targetScrollTop,
574
316
  onScroll: handleScroll,
575
- onTouchStart: () => {
576
- isTouchingRef.current = true;
577
- },
317
+ onTouchStart: () => setIsTouching(true),
578
318
  onScrollEnd: handleScrollEnd,
579
319
  scrollWithAnimation: true,
580
320
  children: realPickerItems
581
- }))];
321
+ })];
582
322
  }
583
323
  });
584
324
  }
@@ -589,73 +329,53 @@ function PickerGroupDate(props) {
589
329
  columnId,
590
330
  updateDay,
591
331
  selectedIndex = 0,
592
- colors = {},
593
- enableClickItemScroll = true
332
+ colors = {}
594
333
  } = props;
595
334
  const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
596
335
  const [targetScrollTop, setTargetScrollTop] = React.useState(0);
597
- const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
598
336
  const scrollViewRef = React.useRef(null);
599
- const itemRefs = React.useRef([]);
600
- const selectedIndexPropRef = React.useRef(selectedIndex);
601
- selectedIndexPropRef.current = selectedIndex;
602
- const syncScrollFromPropsRef = React.useRef(false);
603
- const pendingScrollSettleFocusRef = React.useRef(false);
604
337
  const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
605
- const isTouchingRef = React.useRef(false);
606
- const lengthScaleRatioRef = React.useRef(1);
607
- const useMeasuredScaleRef = React.useRef(false);
338
+ const [isTouching, setIsTouching] = React.useState(false);
608
339
  const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
609
- // 初始化时计算 lengthScaleRatio 并判定缩放模式
610
340
  React.useEffect(() => {
611
- Taro.getSystemInfo({
612
- success: res => {
613
- lengthScaleRatioRef.current = calculateLengthScaleRatio(res);
614
- useMeasuredScaleRef.current = resolveUseMeasuredScale(res);
615
- itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
616
- },
617
- fail: () => {
618
- lengthScaleRatioRef.current = 1;
619
- useMeasuredScaleRef.current = false;
341
+ var _a;
342
+ if (process.env.TARO_PLATFORM !== 'harmony') {
343
+ if (scrollViewRef.current && ((_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollHeight)) {
344
+ itemHeightRef.current = scrollViewRef.current.scrollHeight / scrollViewRef.current.childNodes.length;
345
+ } else {
346
+ console.warn('Height measurement anomaly');
620
347
  }
621
- });
622
- }, []);
623
- React.useEffect(() => {
624
- itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
348
+ }
625
349
  }, [range.length]); // 只在range长度变化时重新计算
626
- // props selectedIndex 变化:滚至对应项并短时标记程序化滚动(syncScrollFromPropsRef)
350
+ const getSelectedIndex = scrollTop => {
351
+ return Math.round(scrollTop / itemHeightRef.current);
352
+ };
353
+ // 当selectedIndex变化时,调整滚动位置
627
354
  React.useEffect(() => {
628
- if (scrollViewRef.current && range.length > 0 && !isTouchingRef.current) {
629
- syncScrollFromPropsRef.current = true;
355
+ if (scrollViewRef.current && range.length > 0 && !isTouching) {
630
356
  const baseValue = selectedIndex * itemHeightRef.current;
631
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, undefined, lengthScaleRatioRef.current);
357
+ setTargetScrollTopWithScale(setTargetScrollTop, baseValue);
632
358
  setCurrentIndex(selectedIndex);
633
- const tid = setTimeout(() => {
634
- syncScrollFromPropsRef.current = false;
635
- }, 400);
636
- return () => clearTimeout(tid);
637
359
  }
638
360
  }, [selectedIndex, range]);
639
361
  // 是否处于归中状态
640
362
  const isCenterTimerId = React.useRef(null);
641
- // 滚动静止后归中并同步选中
363
+ // 简化为直接在滚动结束时通知父组件
642
364
  const handleScrollEnd = () => {
643
365
  if (!scrollViewRef.current) return;
644
366
  if (isCenterTimerId.current) {
645
367
  clearTimeout(isCenterTimerId.current);
646
368
  isCenterTimerId.current = null;
647
369
  }
648
- // 100ms 内无新滚动则归中并提交索引
370
+ // 做一个0.1s延时 0.1s之内没有新的滑动 则把选项归到中间 然后更新选中项
649
371
  isCenterTimerId.current = setTimeout(() => {
650
372
  if (!scrollViewRef.current) return;
651
373
  const scrollTop = scrollViewRef.current.scrollTop;
652
- const newIndex = getSelectedIndex(scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
653
- const allowA11yFocus = !syncScrollFromPropsRef.current;
654
- const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
655
- isTouchingRef.current = false;
374
+ const newIndex = getSelectedIndex(scrollTop);
375
+ setIsTouching(false);
656
376
  const baseValue = newIndex * itemHeightRef.current;
657
377
  const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
658
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset, lengthScaleRatioRef.current);
378
+ setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset);
659
379
  // 更新日期值
660
380
  if (updateDay) {
661
381
  // 解析文本中的数字(移除年、月、日等后缀)
@@ -663,15 +383,10 @@ function PickerGroupDate(props) {
663
383
  const numericValue = parseInt(valueText.replace(/[^0-9]/g, ''));
664
384
  updateDay(isNaN(numericValue) ? 0 : numericValue, parseInt(columnId));
665
385
  }
666
- pendingScrollSettleFocusRef.current = false;
667
- if (allowA11yFocus && shouldFocusOnScrollSettle) {
668
- requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
669
- }
670
- syncScrollFromPropsRef.current = false;
671
386
  isCenterTimerId.current = null;
672
387
  }, 100);
673
388
  };
674
- // 滚动中:按 scrollTop 更新高亮索引
389
+ // 滚动处理
675
390
  const handleScroll = () => {
676
391
  if (!scrollViewRef.current) return;
677
392
  if (isCenterTimerId.current) {
@@ -679,31 +394,17 @@ function PickerGroupDate(props) {
679
394
  isCenterTimerId.current = null;
680
395
  }
681
396
  const scrollTop = scrollViewRef.current.scrollTop;
682
- const ih = itemHeightRef.current;
683
- const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
684
- const spi = selectedIndexPropRef.current;
685
- if (newIndex !== spi) {
686
- if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
687
- pendingScrollSettleFocusRef.current = true;
688
- }
689
- if (isTouchingRef.current) {
690
- syncScrollFromPropsRef.current = false;
691
- }
692
- }
397
+ const newIndex = getSelectedIndex(scrollTop);
693
398
  if (newIndex !== currentIndex) {
694
399
  setCurrentIndex(newIndex);
695
400
  }
696
401
  };
697
402
  // 渲染选项
698
403
  const pickerItem = range.map((item, index) => {
699
- return createComponent(View, mergeProps({
404
+ return createComponent(View, {
700
405
  id: `picker-item-${columnId}-${index}`,
701
406
  key: index,
702
- ref: el => itemRefs.current[index] = el,
703
- className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
704
- }, enableClickItemScroll ? {
705
- onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
706
- } : {}, {
407
+ className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`,
707
408
  get style() {
708
409
  return {
709
410
  height: PICKER_LINE_HEIGHT,
@@ -711,7 +412,7 @@ function PickerGroupDate(props) {
711
412
  };
712
413
  },
713
414
  children: item
714
- }));
415
+ });
715
416
  });
716
417
  const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
717
418
  key: `blank-top-${idx}`,
@@ -726,10 +427,6 @@ function PickerGroupDate(props) {
726
427
  height: PICKER_LINE_HEIGHT
727
428
  }
728
429
  }))];
729
- const clickScrollViewProps = enableClickItemScroll ? {
730
- scrollIntoView,
731
- scrollIntoViewAlignment: 'center'
732
- } : {};
733
430
  return createComponent(View, {
734
431
  className: "taro-picker__group",
735
432
  get children() {
@@ -739,7 +436,7 @@ function PickerGroupDate(props) {
739
436
  className: "taro-picker__indicator"
740
437
  }, indicatorStyle ? {
741
438
  style: indicatorStyle
742
- } : {})), createComponent(ScrollView, mergeProps({
439
+ } : {})), createComponent(ScrollView, {
743
440
  ref: scrollViewRef,
744
441
  scrollY: true,
745
442
  showScrollbar: false,
@@ -747,16 +444,13 @@ function PickerGroupDate(props) {
747
444
  style: {
748
445
  height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
749
446
  },
750
- scrollTop: targetScrollTop
751
- }, clickScrollViewProps, {
447
+ scrollTop: targetScrollTop,
752
448
  onScroll: handleScroll,
753
- onTouchStart: () => {
754
- isTouchingRef.current = true;
755
- },
449
+ onTouchStart: () => setIsTouching(true),
756
450
  onScrollEnd: handleScrollEnd,
757
451
  scrollWithAnimation: true,
758
452
  children: realPickerItems
759
- }))];
453
+ })];
760
454
  }
761
455
  });
762
456
  }
@@ -769,54 +463,37 @@ function PickerGroupRegion(props) {
769
463
  updateIndex,
770
464
  selectedIndex = 0,
771
465
  // 使用selectedIndex参数,默认为0
772
- colors = {},
773
- enableClickItemScroll = true
466
+ colors = {}
774
467
  } = props;
775
468
  const indicatorStyle = colors.lineColor ? getIndicatorStyle(colors.lineColor) : null;
776
469
  const scrollViewRef = React.useRef(null);
777
470
  const [targetScrollTop, setTargetScrollTop] = React.useState(0);
778
- const [scrollIntoView, scrollToItemId] = usePickerItemScrollIntoView();
779
471
  const [currentIndex, setCurrentIndex] = React.useState(selectedIndex);
780
- const isTouchingRef = React.useRef(false);
781
- const lengthScaleRatioRef = React.useRef(1);
782
- const useMeasuredScaleRef = React.useRef(false);
472
+ const [isTouching, setIsTouching] = React.useState(false);
783
473
  const itemHeightRef = React.useRef(PICKER_LINE_HEIGHT);
784
- const itemRefs = React.useRef([]);
785
- const selectedIndexPropRef = React.useRef(selectedIndex);
786
- selectedIndexPropRef.current = selectedIndex;
787
- const syncScrollFromPropsRef = React.useRef(false);
788
- const pendingScrollSettleFocusRef = React.useRef(false);
789
- // 初始化时计算 lengthScaleRatio 并判定缩放模式
474
+ const isUserBeginScrollRef = React.useRef(false);
790
475
  React.useEffect(() => {
791
- Taro.getSystemInfo({
792
- success: res => {
793
- lengthScaleRatioRef.current = calculateLengthScaleRatio(res);
794
- useMeasuredScaleRef.current = resolveUseMeasuredScale(res);
795
- itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
796
- },
797
- fail: () => {
798
- lengthScaleRatioRef.current = 1;
799
- useMeasuredScaleRef.current = false;
476
+ var _a;
477
+ if (process.env.TARO_PLATFORM !== 'harmony') {
478
+ if (scrollViewRef.current && ((_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollHeight)) {
479
+ itemHeightRef.current = scrollViewRef.current.scrollHeight / scrollViewRef.current.childNodes.length;
480
+ } else {
481
+ console.warn('Height measurement anomaly');
800
482
  }
801
- });
802
- }, []);
803
- React.useEffect(() => {
804
- itemHeightRef.current = calculateItemHeight(scrollViewRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
483
+ }
805
484
  }, [range.length]); // 只在range长度变化时重新计算
806
- // props selectedIndex 变化:滚至对应项并短时标记程序化滚动(syncScrollFromPropsRef)
485
+ const getSelectedIndex = scrollTop => {
486
+ return Math.round(scrollTop / itemHeightRef.current);
487
+ };
488
+ // 当selectedIndex变化时,调整滚动位置
807
489
  React.useEffect(() => {
808
- if (scrollViewRef.current && range.length > 0 && !isTouchingRef.current) {
809
- syncScrollFromPropsRef.current = true;
490
+ if (scrollViewRef.current && range.length > 0 && !isTouching) {
810
491
  const baseValue = selectedIndex * itemHeightRef.current;
811
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, undefined, lengthScaleRatioRef.current);
492
+ setTargetScrollTopWithScale(setTargetScrollTop, baseValue);
812
493
  setCurrentIndex(selectedIndex);
813
- const tid = setTimeout(() => {
814
- syncScrollFromPropsRef.current = false;
815
- }, 400);
816
- return () => clearTimeout(tid);
817
494
  }
818
495
  }, [selectedIndex, range]);
819
- // 滚动静止后归中(debounce)
496
+ // 滚动结束处理
820
497
  const isCenterTimerId = React.useRef(null);
821
498
  const handleScrollEnd = () => {
822
499
  if (!scrollViewRef.current) return;
@@ -824,27 +501,19 @@ function PickerGroupRegion(props) {
824
501
  clearTimeout(isCenterTimerId.current);
825
502
  isCenterTimerId.current = null;
826
503
  }
827
- // 100ms 内无新滚动则归中并提交索引
504
+ // 做一个0.1s延时 0.1s之内没有新的滑动 则把选项归到中间 然后更新选中项
828
505
  isCenterTimerId.current = setTimeout(() => {
829
506
  if (!scrollViewRef.current) return;
830
507
  const scrollTop = scrollViewRef.current.scrollTop;
831
- const newIndex = getSelectedIndex(scrollTop, itemHeightRef.current, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
832
- const allowA11yFocus = !syncScrollFromPropsRef.current;
833
- const shouldFocusOnScrollSettle = pendingScrollSettleFocusRef.current;
834
- isTouchingRef.current = false;
508
+ const newIndex = getSelectedIndex(scrollTop);
509
+ setIsTouching(false);
835
510
  const baseValue = newIndex * itemHeightRef.current;
836
511
  const randomOffset = Math.random() * 0.001; // 随机数为了在一个项内滚动时强制刷新
837
- setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset, lengthScaleRatioRef.current);
838
- updateIndex(newIndex, columnId, false, allowA11yFocus);
839
- pendingScrollSettleFocusRef.current = false;
840
- if (allowA11yFocus && shouldFocusOnScrollSettle) {
841
- requestAccessibilityFocusOnView(itemRefs.current[newIndex]);
842
- }
843
- syncScrollFromPropsRef.current = false;
844
- isCenterTimerId.current = null;
512
+ setTargetScrollTopWithScale(setTargetScrollTop, baseValue, randomOffset);
513
+ updateIndex(newIndex, columnId, false, isUserBeginScrollRef.current);
845
514
  }, 100);
846
515
  };
847
- // 滚动中:按 scrollTop 更新高亮索引
516
+ // 滚动处理 - 在滚动时计算索引
848
517
  const handleScroll = () => {
849
518
  if (!scrollViewRef.current) return;
850
519
  if (isCenterTimerId.current) {
@@ -852,17 +521,7 @@ function PickerGroupRegion(props) {
852
521
  isCenterTimerId.current = null;
853
522
  }
854
523
  const scrollTop = scrollViewRef.current.scrollTop;
855
- const ih = itemHeightRef.current;
856
- const newIndex = getSelectedIndex(scrollTop, ih, lengthScaleRatioRef.current, useMeasuredScaleRef.current);
857
- const spi = selectedIndexPropRef.current;
858
- if (newIndex !== spi) {
859
- if (!syncScrollFromPropsRef.current || isTouchingRef.current) {
860
- pendingScrollSettleFocusRef.current = true;
861
- }
862
- if (isTouchingRef.current) {
863
- syncScrollFromPropsRef.current = false;
864
- }
865
- }
524
+ const newIndex = getSelectedIndex(scrollTop);
866
525
  if (newIndex !== currentIndex) {
867
526
  setCurrentIndex(newIndex);
868
527
  }
@@ -870,14 +529,10 @@ function PickerGroupRegion(props) {
870
529
  // 渲染选项
871
530
  const pickerItem = range.map((item, index) => {
872
531
  const content = rangeKey && item && typeof item === 'object' ? item[rangeKey] : item;
873
- return createComponent(View, mergeProps({
532
+ return createComponent(View, {
874
533
  id: `picker-item-${columnId}-${index}`,
875
534
  key: index,
876
- ref: el => itemRefs.current[index] = el,
877
- className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`
878
- }, enableClickItemScroll ? {
879
- onClick: () => scrollToItemId(`picker-item-${columnId}-${index}`)
880
- } : {}, {
535
+ className: `taro-picker__item${index === currentIndex ? ' taro-picker__item--selected' : ''}`,
881
536
  get style() {
882
537
  return {
883
538
  height: PICKER_LINE_HEIGHT,
@@ -885,7 +540,7 @@ function PickerGroupRegion(props) {
885
540
  };
886
541
  },
887
542
  children: content
888
- }));
543
+ });
889
544
  });
890
545
  const realPickerItems = [...new Array(PICKER_BLANK_ITEMS).fill(null).map((_, idx) => createComponent(View, {
891
546
  key: `blank-top-${idx}`,
@@ -900,10 +555,6 @@ function PickerGroupRegion(props) {
900
555
  height: PICKER_LINE_HEIGHT
901
556
  }
902
557
  }))];
903
- const clickScrollViewProps = enableClickItemScroll ? {
904
- scrollIntoView,
905
- scrollIntoViewAlignment: 'center'
906
- } : {};
907
558
  return createComponent(View, {
908
559
  className: "taro-picker__group",
909
560
  get children() {
@@ -913,7 +564,7 @@ function PickerGroupRegion(props) {
913
564
  className: "taro-picker__indicator"
914
565
  }, indicatorStyle ? {
915
566
  style: indicatorStyle
916
- } : {})), createComponent(ScrollView, mergeProps({
567
+ } : {})), createComponent(ScrollView, {
917
568
  ref: scrollViewRef,
918
569
  scrollY: true,
919
570
  showScrollbar: false,
@@ -921,16 +572,16 @@ function PickerGroupRegion(props) {
921
572
  style: {
922
573
  height: PICKER_LINE_HEIGHT * PICKER_VISIBLE_ITEMS
923
574
  },
924
- scrollTop: targetScrollTop
925
- }, clickScrollViewProps, {
575
+ scrollTop: targetScrollTop,
926
576
  onScroll: handleScroll,
927
577
  onTouchStart: () => {
928
- isTouchingRef.current = true;
578
+ setIsTouching(true);
579
+ isUserBeginScrollRef.current = true;
929
580
  },
930
581
  onScrollEnd: handleScrollEnd,
931
582
  scrollWithAnimation: true,
932
583
  children: realPickerItems
933
- }))];
584
+ })];
934
585
  }
935
586
  });
936
587
  }