Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Picker): emit scrollInto when pass through each option #12621

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion packages/vant/src/picker/PickerColumn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import { getElementTranslateY, findIndexOfEnabledOption } from './utils';

// Composables
import { useEventListener, useParent } from '@vant/use';
import { useEventListener, useParent, useRaf } from '@vant/use';
import { useTouch } from '../composables/use-touch';
import { useExpose } from '../composables/use-expose';

Expand Down Expand Up @@ -111,6 +111,9 @@ export default defineComponent({

transitionEndTrigger = null;
currentDuration.value = DEFAULT_DURATION;
const distance = currentOffset.value - momentumOffset;
const duration = Date.now() - touchStartTime;
scrollInto(index, duration, Math.abs(distance / duration));
updateValueByIndex(index);
emit('clickOption', props.options[index]);
};
Expand All @@ -119,6 +122,7 @@ export default defineComponent({
clamp(Math.round(-offset / props.optionHeight), 0, count() - 1);

const currentIndex = computed(() => getIndexByOffset(currentOffset.value));
let stop: () => void;

const momentum = (distance: number, duration: number) => {
const speed = Math.abs(distance / duration);
Expand All @@ -129,9 +133,43 @@ export default defineComponent({
const index = getIndexByOffset(distance);

currentDuration.value = +props.swipeDuration;

scrollInto(index, duration, speed);

updateValueByIndex(index);
};

const scrollInto = (index: number, duration: number, speed: number) => {
// const duration = currentDuration.value;
const oldIndex = currentIndex.value;
const itemCount = Math.max(index, oldIndex) - Math.min(index, oldIndex);
const time = duration / speed;
let passedCount = 0;
const isDown = currentIndex.value < index;
let start: number;
if (stop) stop();
stop = useRaf(
(timestamp) => {
if (start === undefined) start = timestamp;
const elapsed = timestamp - start;
if (passedCount <= itemCount) {
const currentCount = Math.round(elapsed / time);
if (passedCount <= currentCount) {
const option =
props.options[oldIndex + (isDown ? passedCount : -passedCount)];
emit('scrollInto', option);
passedCount++;
}
} else {
stop();
}
},
{
isLoop: true,
},
);
};

const stopMomentum = () => {
moving = false;
currentDuration.value = 0;
Expand Down
32 changes: 32 additions & 0 deletions packages/vant/src/picker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,38 @@ export default {
};
```

### Scroll Into Event

Emitted when an option is scrolled into the middle selection area by clicking or dragging. It can used to get user's scroll change, e.g. implement a click sound of iOS picker.

```html
<van-picker title="标题" :columns="columns" @scroll-into="onScrollInto" />
```

```js
import { showToast } from 'vant';

export default {
setup() {
const columns = [
{ text: '杭州', value: 'Hangzhou' },
{ text: '宁波', value: 'Ningbo' },
{ text: '温州', value: 'Wenzhou' },
{ text: '绍兴', value: 'Shaoxing' },
{ text: '湖州', value: 'Huzhou' },
];
const onScrollInto = ({ currentOption }) => {
showToast(`当前值: ${currentOption.text}`);
};

return {
columns,
onScrollInto,
};
},
};
```

## API

### Props
Expand Down
32 changes: 32 additions & 0 deletions packages/vant/src/picker/README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,38 @@ export default {
};
```

### 监听 scroll-into 事件

当用户通过点击或拖拽让一个选项滚动到中间的选择区域时触发 scroll-into 事件。可用于监听用户滚动选项的变化,例如实现 iOS 选择器的咔哒声。

```html
<van-picker title="标题" :columns="columns" @scroll-into="onScrollInto" />
```

```js
import { showToast } from 'vant';

export default {
setup() {
const columns = [
{ text: '杭州', value: 'Hangzhou' },
{ text: '宁波', value: 'Ningbo' },
{ text: '温州', value: 'Wenzhou' },
{ text: '绍兴', value: 'Shaoxing' },
{ text: '湖州', value: 'Huzhou' },
];
const onScrollInto = ({ currentOption }) => {
showToast(`当前值: ${currentOption.text}`);
};

return {
columns,
onScrollInto,
};
},
};
```

## API

### Props
Expand Down
15 changes: 15 additions & 0 deletions packages/vant/src/picker/demo/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import WithPopup from './WithPopup.vue';
import VanPicker, {
PickerChangeEventParams,
PickerConfirmEventParams,
PickerOption,
} from '..';
import {
dateColumns,
Expand All @@ -29,6 +30,7 @@ const t = useTranslate({
multipleColumns: '多列选择',
customChildrenKey: '自定义 Columns 结构',
customChildrenColumns: customKeyColumns['zh-CN'],
scrollInto: '监听 scrollInto 事件',
toastContent: (value: string) => `当前值:${value}`,
},
'en-US': {
Expand All @@ -44,6 +46,7 @@ const t = useTranslate({
multipleColumns: 'Multiple Columns',
customChildrenKey: 'Custom Columns Fields',
customChildrenColumns: customKeyColumns['en-US'],
scrollInto: 'ScrollInto Event',
toastContent: (value: string) => `Value: ${value}`,
},
});
Expand All @@ -65,6 +68,10 @@ const onConfirm = ({ selectedValues }: PickerConfirmEventParams) => {
};

const onCancel = () => showToast(t('cancel'));

const onScrollInto = ({ currentOption }: { currentOption: PickerOption }) => {
showToast(t('toastContent', currentOption.text));
};
</script>

<template>
Expand Down Expand Up @@ -116,4 +123,12 @@ const onCancel = () => showToast(t('cancel'));
:columns-field-names="customFieldName"
/>
</demo-block>

<demo-block card :title="t('scrollInto')">
<van-picker
:title="t('title')"
:columns="t('basicColumns')"
@scroll-into="onScrollInto"
/>
</demo-block>
</template>
98 changes: 98 additions & 0 deletions packages/vant/src/picker/test/__snapshots__/demo-ssr.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -762,4 +762,102 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
</div>
<div>
<!--[-->
<div class="van-picker">
<div class="van-picker__toolbar">
<!--[-->
<button
type="button"
class="van-picker__cancel van-haptics-feedback"
>
Cancel
</button>
<div class="van-picker__title van-ellipsis">
Title
</div>
<button
type="button"
class="van-picker__confirm van-haptics-feedback"
>
Confirm
</button>
</div>
<div
class="van-picker__columns"
style="height:264px;"
>
<!--[-->
<div class="van-picker-column">
<ul
style="transform:translate3d(0, 110px, 0);transition-duration:0ms;transition-property:none;"
class="van-picker-column__wrapper"
>
<!--[-->
<li
role="button"
style="height:44px;"
tabindex="0"
class="van-picker-column__item van-picker-column__item--selected"
>
<div class="van-ellipsis">
Delaware
</div>
</li>
<li
role="button"
style="height:44px;"
tabindex="0"
class="van-picker-column__item"
>
<div class="van-ellipsis">
Florida
</div>
</li>
<li
role="button"
style="height:44px;"
tabindex="0"
class="van-picker-column__item"
>
<div class="van-ellipsis">
Wenzhou
</div>
</li>
<li
role="button"
style="height:44px;"
tabindex="0"
class="van-picker-column__item"
>
<div class="van-ellipsis">
Indiana
</div>
</li>
<li
role="button"
style="height:44px;"
tabindex="0"
class="van-picker-column__item"
>
<div class="van-ellipsis">
Maine
</div>
</li>
</ul>
</div>
<!--[-->
<div
class="van-picker__mask"
style="background-size:100% 110px;"
>
</div>
<div
class="van-hairline-unset--top-bottom van-picker__frame"
style="height:44px;"
>
</div>
</div>
</div>
</div>
`;
93 changes: 93 additions & 0 deletions packages/vant/src/picker/test/__snapshots__/demo.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -723,4 +723,97 @@ exports[`should render demo and match snapshot 1`] = `
</div>
</div>
</div>
<div>
<div class="van-picker">
<div class="van-picker__toolbar">
<button
type="button"
class="van-picker__cancel van-haptics-feedback"
>
Cancel
</button>
<div class="van-picker__title van-ellipsis">
Title
</div>
<button
type="button"
class="van-picker__confirm van-haptics-feedback"
>
Confirm
</button>
</div>
<div
class="van-picker__columns"
style="height: 264px;"
>
<div class="van-picker-column">
<ul
style="transform: translate3d(0, 110px, 0); transition-duration: 0ms; transition-property: none;"
class="van-picker-column__wrapper"
>
<li
role="button"
style="height: 44px;"
tabindex="0"
class="van-picker-column__item van-picker-column__item--selected"
>
<div class="van-ellipsis">
Delaware
</div>
</li>
<li
role="button"
style="height: 44px;"
tabindex="0"
class="van-picker-column__item"
>
<div class="van-ellipsis">
Florida
</div>
</li>
<li
role="button"
style="height: 44px;"
tabindex="0"
class="van-picker-column__item"
>
<div class="van-ellipsis">
Wenzhou
</div>
</li>
<li
role="button"
style="height: 44px;"
tabindex="0"
class="van-picker-column__item"
>
<div class="van-ellipsis">
Indiana
</div>
</li>
<li
role="button"
style="height: 44px;"
tabindex="0"
class="van-picker-column__item"
>
<div class="van-ellipsis">
Maine
</div>
</li>
</ul>
</div>
<div
class="van-picker__mask"
style="background-size: 100% 110px;"
>
</div>
<div
class="van-hairline-unset--top-bottom van-picker__frame"
style="height: 44px;"
>
</div>
</div>
</div>
</div>
`;
Loading