Skip to content

Commit 7f8e732

Browse files
authored
feat(Picker): support visibleItemCount prop (#4052)
* feat(Picker): support visibleItemCount prop * test: update snapshots
1 parent 88b45e9 commit 7f8e732

File tree

12 files changed

+93
-72
lines changed

12 files changed

+93
-72
lines changed

packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,15 +61,15 @@ exports[`date-time-picker :base 1`] = `
6161
>
6262
<wx-view
6363
class="t-picker-item__group class t-class"
64-
style=""
64+
style="height:200px;"
6565
bind:touchcancel="onTouchEnd"
6666
bind:touchend="onTouchEnd"
6767
catch:touchmove="onTouchMove"
6868
bind:touchstart="onTouchStart"
6969
>
7070
<wx-view
7171
class="t-picker-item__wrapper"
72-
style="transition: transform 0ms cubic-bezier(0.215, 0.61, 0.355, 1); transform: translate3d(0, -240px, 0)"
72+
style="transition: transform 0ms cubic-bezier(0.215, 0.61, 0.355, 1); transform: translate3d(0, -240px, 0); padding: 80px 0"
7373
>
7474
<wx-view
7575
class="t-picker-item__item"
@@ -332,15 +332,15 @@ exports[`date-time-picker :base 1`] = `
332332
>
333333
<wx-view
334334
class="t-picker-item__group class t-class"
335-
style=""
335+
style="height:200px;"
336336
bind:touchcancel="onTouchEnd"
337337
bind:touchend="onTouchEnd"
338338
catch:touchmove="onTouchMove"
339339
bind:touchstart="onTouchStart"
340340
>
341341
<wx-view
342342
class="t-picker-item__wrapper"
343-
style="transition: transform 0ms cubic-bezier(0.215, 0.61, 0.355, 1); transform: translate3d(0, -320px, 0)"
343+
style="transition: transform 0ms cubic-bezier(0.215, 0.61, 0.355, 1); transform: translate3d(0, -320px, 0); padding: 80px 0"
344344
>
345345
<wx-view
346346
class="t-picker-item__item"
@@ -497,7 +497,7 @@ exports[`date-time-picker :base 1`] = `
497497
/>
498498
<wx-view
499499
class="t-picker__indicator"
500-
style="height: 40px"
500+
style="height: 40px; top: 80px"
501501
/>
502502
</wx-view>
503503
</wx-view>

packages/components/picker-item/picker-item.less

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
@picker: ~'@{prefix}-picker';
44
@item: ~'@{picker}-item';
5-
@picker-group-height: var(--td-picker-group-height, 400rpx);
65
@picker-item-color: var(--td-picker-item-color, @text-color-secondary);
76
@picker-item-active-color: var(--td-picker-item-active-color, @text-color-primary);
87
@picker-item-font-size: var(--td-picker-item-font-size, @font-size-m);
@@ -14,14 +13,12 @@
1413

1514
.@{item} {
1615
&__group {
17-
height: @picker-group-height;
1816
overflow: hidden;
1917
flex: 1;
2018
z-index: 1;
2119
}
2220

2321
&__wrapper {
24-
padding: 144rpx 0;
2522
// 虚拟滚动性能优化:使用 will-change 提示浏览器
2623
will-change: transform;
2724
}

packages/components/picker-item/picker-item.ts

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const INERTIA_DISTANCE = 15;
1616
// 虚拟滚动配置
1717
const VIRTUAL_SCROLL_CONFIG = {
1818
ENABLE_THRESHOLD: 100, // 超过100个选项启用虚拟滚动
19-
VISIBLE_COUNT: 5, // 可见区域显示5个选项
19+
// VISIBLE_COUNT: 5, // 可见区域显示5个选项,使用 visibleItemCount 属性代替
2020
BUFFER_COUNT: 8, // 上下各缓冲8个选项(增加缓冲区,防止快速滑动时空白)
2121
THROTTLE_TIME: 16, // 节流时间(60fps,提高更新频率)
2222
FAST_SCROLL_BUFFER: 12, // 快速滑动时的额外缓冲区
@@ -86,6 +86,11 @@ export default class PickerItem extends SuperComponent {
8686
virtualStartIndex: 0, // 虚拟滚动起始索引
8787
virtualOffsetY: 0, // 虚拟滚动偏移量
8888
totalHeight: 0, // 总高度(用于占位)
89+
90+
// 动态属性(由父组件传递)
91+
itemHeight: 40, // 单个选项高度
92+
visibleItemCount: 5, // 可视区域内的选项个数
93+
wrapperPaddingY: 72, // wrapper的上下padding
8994
};
9095

9196
lifetimes = {
@@ -115,11 +120,11 @@ export default class PickerItem extends SuperComponent {
115120
methods = {
116121
onClickItem(event: WechatMiniprogram.TouchEvent) {
117122
const { index: clickIndex } = event.currentTarget.dataset;
118-
const { pickItemHeight } = this.data;
123+
const { itemHeight } = this.data;
119124
const index = range(clickIndex, 0, this.getCount() - 1);
120125

121126
if (index !== this._selectedIndex) {
122-
this.setData({ offset: -index * pickItemHeight, curIndex: index, duration: 200 });
127+
this.setData({ offset: -index * itemHeight, curIndex: index, duration: 200 });
123128
}
124129

125130
this.updateSelected(index, true);
@@ -136,12 +141,12 @@ export default class PickerItem extends SuperComponent {
136141

137142
onTouchMove(event) {
138143
const { StartY, StartOffset } = this;
139-
const { pickItemHeight, enableVirtualScroll } = this.data;
144+
const { itemHeight, enableVirtualScroll } = this.data;
140145
const currentTime = Date.now();
141146

142147
// 偏移增量
143148
const deltaY = event.touches[0].clientY - StartY;
144-
const newOffset = range(StartOffset + deltaY, -(this.getCount() * pickItemHeight), 0);
149+
const newOffset = range(StartOffset + deltaY, -(this.getCount() * itemHeight), 0);
145150

146151
// 计算滑动速度和方向
147152
const offsetDelta = newOffset - this._lastOffset;
@@ -190,7 +195,7 @@ export default class PickerItem extends SuperComponent {
190195
this._moveTimer = null;
191196
}
192197

193-
const { offset, pickItemHeight } = this.data;
198+
const { offset, itemHeight } = this.data;
194199
const { startTime } = this;
195200
if (offset === this.StartOffset) {
196201
return;
@@ -205,15 +210,15 @@ export default class PickerItem extends SuperComponent {
205210
}
206211

207212
// 调整偏移量
208-
const newOffset = range(offset + distance, -this.getCount() * pickItemHeight, 0);
209-
const index = range(Math.round(-newOffset / pickItemHeight), 0, this.getCount() - 1);
213+
const newOffset = range(offset + distance, -this.getCount() * itemHeight, 0);
214+
const index = range(Math.round(-newOffset / itemHeight), 0, this.getCount() - 1);
210215

211216
// 判断是否为快速惯性滚动
212-
const isFastInertia = Math.abs(distance) > pickItemHeight * 3;
217+
const isFastInertia = Math.abs(distance) > itemHeight * 3;
213218

214219
// 立即更新虚拟滚动视图(修复惯性滚动后空白问题,快速滚动时使用更大缓冲区)
215220
if (this.data.enableVirtualScroll) {
216-
this.updateVisibleOptions(-index * pickItemHeight, isFastInertia);
221+
this.updateVisibleOptions(-index * itemHeight, isFastInertia);
217222
}
218223

219224
// 清除之前的动画更新定时器
@@ -225,7 +230,7 @@ export default class PickerItem extends SuperComponent {
225230
// 在动画执行期间定期更新虚拟滚动视图(确保动画过程流畅)
226231
if (this.data.enableVirtualScroll && Math.abs(distance) > 0) {
227232
const startOffset = offset;
228-
const endOffset = -index * pickItemHeight;
233+
const endOffset = -index * itemHeight;
229234
const startTime = Date.now();
230235

231236
this._animationTimer = setInterval(() => {
@@ -248,7 +253,7 @@ export default class PickerItem extends SuperComponent {
248253

249254
this.setData(
250255
{
251-
offset: -index * pickItemHeight,
256+
offset: -index * itemHeight,
252257
duration: ANIMATION_DURATION,
253258
curIndex: index,
254259
},
@@ -260,7 +265,7 @@ export default class PickerItem extends SuperComponent {
260265
}
261266
if (this.data.enableVirtualScroll) {
262267
// 动画结束后使用正常缓冲区(不再是快速滚动状态)
263-
this.updateVisibleOptions(-index * pickItemHeight, false);
268+
this.updateVisibleOptions(-index * itemHeight, false);
264269
}
265270
},
266271
);
@@ -293,7 +298,7 @@ export default class PickerItem extends SuperComponent {
293298

294299
// 刷新选中状态
295300
update() {
296-
const { options, value, pickerKeys, pickItemHeight, format, columnIndex } = this.data;
301+
const { options, value, pickerKeys, format, columnIndex, itemHeight, visibleItemCount } = this.data;
297302

298303
const formatOptions = this.formatOption(options, columnIndex, format);
299304
const optionsCount = formatOptions.length;
@@ -312,20 +317,24 @@ export default class PickerItem extends SuperComponent {
312317
}
313318
const selectedIndex = index > 0 ? index : 0;
314319

320+
// 计算wrapper的padding,确保选中项居中显示
321+
const wrapperPaddingY = ((visibleItemCount - 1) / 2) * itemHeight;
322+
315323
const updateData: any = {
316324
formatOptions,
317-
offset: -selectedIndex * pickItemHeight,
325+
offset: -selectedIndex * itemHeight,
318326
curIndex: selectedIndex,
319327
enableVirtualScroll,
320-
totalHeight: optionsCount * pickItemHeight,
328+
totalHeight: optionsCount * itemHeight,
329+
wrapperPaddingY,
321330
};
322331

323332
// 如果启用虚拟滚动,计算可见选项
324333
if (enableVirtualScroll) {
325-
const visibleRange = this.computeVirtualRange(-selectedIndex * pickItemHeight, optionsCount, pickItemHeight);
334+
const visibleRange = this.computeVirtualRange(-selectedIndex * itemHeight, optionsCount, itemHeight);
326335
updateData.visibleOptions = formatOptions.slice(visibleRange.startIndex, visibleRange.endIndex);
327336
updateData.virtualStartIndex = visibleRange.startIndex;
328-
updateData.virtualOffsetY = visibleRange.startIndex * pickItemHeight;
337+
updateData.virtualOffsetY = visibleRange.startIndex * itemHeight;
329338
} else {
330339
// 不启用虚拟滚动时,visibleOptions 等于 formatOptions
331340
updateData.visibleOptions = formatOptions;
@@ -347,7 +356,8 @@ export default class PickerItem extends SuperComponent {
347356
*/
348357
computeVirtualRange(offset: number, totalCount: number, itemHeight: number, isFastScroll = false) {
349358
const scrollTop = Math.abs(offset);
350-
const { VISIBLE_COUNT, BUFFER_COUNT, FAST_SCROLL_BUFFER } = VIRTUAL_SCROLL_CONFIG;
359+
const { BUFFER_COUNT, FAST_SCROLL_BUFFER } = VIRTUAL_SCROLL_CONFIG;
360+
const { visibleItemCount } = this.data;
351361

352362
// 根据滑动速度动态调整缓冲区大小
353363
const dynamicBuffer = isFastScroll ? FAST_SCROLL_BUFFER : BUFFER_COUNT;
@@ -364,7 +374,7 @@ export default class PickerItem extends SuperComponent {
364374
// 计算起始索引(减去顶部缓冲区)
365375
const startIndex = Math.max(0, centerIndex - topBuffer);
366376
// 计算结束索引(加上可见数量和底部缓冲区)
367-
const endIndex = Math.min(totalCount, centerIndex + VISIBLE_COUNT + bottomBuffer);
377+
const endIndex = Math.min(totalCount, centerIndex + visibleItemCount + bottomBuffer);
368378

369379
return { startIndex, endIndex };
370380
},
@@ -375,12 +385,12 @@ export default class PickerItem extends SuperComponent {
375385
* @param isFastScroll 是否为快速滑动
376386
*/
377387
updateVisibleOptions(offset?: number, isFastScroll = false) {
378-
const { formatOptions, pickItemHeight, enableVirtualScroll } = this.data;
388+
const { formatOptions, itemHeight, enableVirtualScroll } = this.data;
379389

380390
if (!enableVirtualScroll) return;
381391

382392
const currentOffset = offset !== undefined ? offset : this.data.offset;
383-
const visibleRange = this.computeVirtualRange(currentOffset, formatOptions.length, pickItemHeight, isFastScroll);
393+
const visibleRange = this.computeVirtualRange(currentOffset, formatOptions.length, itemHeight, isFastScroll);
384394

385395
// 只有当可见范围发生变化时才更新
386396
if (
@@ -390,7 +400,7 @@ export default class PickerItem extends SuperComponent {
390400
this.setData({
391401
visibleOptions: formatOptions.slice(visibleRange.startIndex, visibleRange.endIndex),
392402
virtualStartIndex: visibleRange.startIndex,
393-
virtualOffsetY: visibleRange.startIndex * pickItemHeight,
403+
virtualOffsetY: visibleRange.startIndex * itemHeight,
394404
});
395405
}
396406
},
@@ -400,8 +410,8 @@ export default class PickerItem extends SuperComponent {
400410
},
401411

402412
getCurrentSelected() {
403-
const { offset, pickItemHeight, formatOptions, pickerKeys } = this.data;
404-
const currentIndex = Math.max(0, Math.min(Math.round(-offset / pickItemHeight), this.getCount() - 1));
413+
const { offset, itemHeight, formatOptions, pickerKeys } = this.data;
414+
const currentIndex = Math.max(0, Math.min(Math.round(-offset / itemHeight), this.getCount() - 1));
405415

406416
return {
407417
index: currentIndex,

packages/components/picker-item/picker-item.wxml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<wxs src="../common/utils.wxs" module="_" />
22

33
<view
4-
style="{{_._style([style, customStyle])}}"
4+
style="{{_._style([style, customStyle, 'height:' + itemHeight * visibleItemCount + 'px'])}}"
55
class="{{_.cls(classPrefix + '__group', [])}} class {{prefix}}-class"
66
bind:touchstart="onTouchStart"
77
catch:touchmove="onTouchMove"
@@ -10,15 +10,15 @@
1010
>
1111
<view
1212
class="{{classPrefix}}__wrapper"
13-
style="transition: transform {{ duration }}ms cubic-bezier(0.215, 0.61, 0.355, 1); transform: translate3d(0, {{ offset }}px, 0)"
13+
style="transition: transform {{ duration }}ms cubic-bezier(0.215, 0.61, 0.355, 1); transform: translate3d(0, {{ offset }}px, 0); padding: {{ wrapperPaddingY }}px 0"
1414
>
1515
<!-- 虚拟滚动:占位容器(撑开总高度) -->
1616
<view wx:if="{{enableVirtualScroll}}" style="height: {{totalHeight}}px; position: relative;">
1717
<!-- 可见区域(绝对定位) -->
1818
<view style="position: absolute; top: {{virtualOffsetY}}px; left: 0; right: 0;">
1919
<view
2020
class="{{_.cls(classPrefix + '__item', [['active', curIndex == (virtualStartIndex + index)]])}}"
21-
style="height: {{pickItemHeight}}px"
21+
style="height: {{itemHeight}}px"
2222
wx:for="{{visibleOptions}}"
2323
wx:key="value"
2424
wx:for-item="option"
@@ -40,7 +40,7 @@
4040
<block wx:else>
4141
<view
4242
class="{{_.cls(classPrefix + '__item', [['active', curIndex == index]])}}"
43-
style="height: {{pickItemHeight}}px"
43+
style="height: {{itemHeight}}px"
4444
wx:for="{{visibleOptions}}"
4545
wx:key="value"
4646
wx:for-item="option"

packages/components/picker/README.en-US.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,19 @@ name | type | default | description | required
99
style | Object | - | CSS(Cascading Style Sheets) | N
1010
custom-style | Object | - | CSS(Cascading Style Sheets),used to set style on virtual component | N
1111
auto-close | Boolean | true | \- | N
12-
cancel-btn | String / Boolean | true | Typescript`boolean \| string` | N
13-
confirm-btn | String / Boolean | true | Typescript`boolean \| string` | N
12+
cancel-btn | String / Boolean | true | Typescript: `boolean \| string` | N
13+
confirm-btn | String / Boolean | true | Typescript: `boolean \| string` | N
1414
header | Boolean | true | \- | N
15-
item-height | Number | 80 | \- | N
16-
keys | Object | - | Typescript`KeysType`[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts) | N
17-
popup-props | Object | {} | popup properties。Typescript`PopupProps`[Popup API Documents](./popup?tab=api)[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N
15+
item-height | Number | 40 | \- | N
16+
keys | Object | - | Typescript: `KeysType`[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts) | N
17+
popup-props | Object | {} | popup properties。Typescript: `PopupProps`[Popup API Documents](./popup?tab=api)[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N
1818
title | String | '' | \- | N
1919
use-popup | Boolean | true | \- | N
2020
using-custom-navbar | Boolean | false | \- | N
21-
value | Array | - | Typescript`Array<PickerValue>` `type PickerValue = string \| number`[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N
22-
default-value | Array | undefined | uncontrolled property。Typescript`Array<PickerValue>` `type PickerValue = string \| number`[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N
21+
value | Array | - | Typescript: `Array<PickerValue>` `type PickerValue = string \| number`[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N
22+
default-value | Array | undefined | uncontrolled property。Typescript: `Array<PickerValue>` `type PickerValue = string \| number`[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N
2323
visible | Boolean | false | \- | N
24+
visible-item-count | Number | 5 | \- | N
2425

2526
### Picker Events
2627

@@ -48,8 +49,8 @@ name | type | default | description | required
4849
-- | -- | -- | -- | --
4950
style | Object | - | CSS(Cascading Style Sheets) | N
5051
custom-style | Object | - | CSS(Cascading Style Sheets),used to set style on virtual component | N
51-
format | Function | - | Typescript`(option: PickerItemOption, columnIndex: number) => PickerItemOption` | N
52-
options | Array | [] | Typescript`PickerItemOption[]` `interface PickerItemOption { label: string; value: string \| number; icon?: string }`[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker-item/type.ts) | N
52+
format | Function | - | Typescript: `(option: PickerItemOption, columnIndex: number) => PickerItemOption` | N
53+
options | Array | [] | Typescript: `PickerItemOption[]` `interface PickerItemOption { label: string; value: string \| number; icon?: string }`[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker-item/type.ts) | N
5354

5455
### PickerItem Slots
5556

@@ -75,7 +76,6 @@ Name | Default Value | Description
7576
--td-picker-title-line-height | 52rpx | -
7677
--td-picker-toolbar-height | 116rpx | -
7778
--td-picker-transparent-color | --td-picker-transparent-color | -
78-
--td-picker-group-height | 400rpx | -
7979
--td-picker-item-active-color | @text-color-primary | -
8080
--td-picker-item-color | @text-color-secondary | -
8181
--td-picker-item-font-size | @font-size-m | -

0 commit comments

Comments
 (0)