mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
feat: support picker can scroll on desktop browser (#9713)
* feat: support picker can scroll on desktop browser fix: change mousewheel event and fix pr review opt: optimize wheel on first item * chore: use deltaY directly * fix: direction and unit test Co-authored-by: genffy <genffyl@gmail.com>
This commit is contained in:
parent
999f465c97
commit
14303f4ccb
@ -1,7 +1,7 @@
|
||||
import { deepClone } from '../utils/deep-clone';
|
||||
import { createNamespace, isObject } from '../utils';
|
||||
import { range } from '../utils/format/number';
|
||||
import { preventDefault } from '../utils/dom/event';
|
||||
import { preventDefault, on, off } from '../utils/dom/event';
|
||||
import { TouchMixin } from '../mixins/touch';
|
||||
|
||||
const DEFAULT_DURATION = 200;
|
||||
@ -9,8 +9,8 @@ const DEFAULT_DURATION = 200;
|
||||
// 惯性滑动思路:
|
||||
// 在手指离开屏幕时,如果和上一次 move 时的间隔小于 `MOMENTUM_LIMIT_TIME` 且 move
|
||||
// 距离大于 `MOMENTUM_LIMIT_DISTANCE` 时,执行惯性滑动
|
||||
const MOMENTUM_LIMIT_TIME = 300;
|
||||
const MOMENTUM_LIMIT_DISTANCE = 15;
|
||||
export const MOMENTUM_LIMIT_TIME = 300;
|
||||
export const MOMENTUM_LIMIT_DISTANCE = 15;
|
||||
|
||||
const [createComponent, bem] = createNamespace('picker-column');
|
||||
|
||||
@ -25,6 +25,10 @@ function getElementTranslateY(element) {
|
||||
function isOptionDisabled(option) {
|
||||
return isObject(option) && option.disabled;
|
||||
}
|
||||
// use standard WheelEvent:
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/WheelEvent
|
||||
const supportMousewheel = 'onwheel' in window;
|
||||
let mousewheelTimer = null;
|
||||
|
||||
export default createComponent({
|
||||
mixins: [TouchMixin],
|
||||
@ -63,6 +67,9 @@ export default createComponent({
|
||||
|
||||
mounted() {
|
||||
this.bindTouchEvent(this.$el);
|
||||
if (supportMousewheel) {
|
||||
on(this.$el, 'wheel', this.onMouseWheel, false);
|
||||
}
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
@ -71,6 +78,10 @@ export default createComponent({
|
||||
if (children) {
|
||||
children.splice(children.indexOf(this), 1);
|
||||
}
|
||||
|
||||
if (supportMousewheel) {
|
||||
off(this.$el, 'wheel');
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
@ -172,6 +183,44 @@ export default createComponent({
|
||||
}, 0);
|
||||
},
|
||||
|
||||
onMouseWheel(event) {
|
||||
if (this.readonly) {
|
||||
return;
|
||||
}
|
||||
preventDefault(event, true);
|
||||
// simply combine touchstart and touchmove
|
||||
const translateY = getElementTranslateY(this.$refs.wrapper);
|
||||
this.startOffset = Math.min(0, translateY - this.baseOffset);
|
||||
this.momentumOffset = this.startOffset;
|
||||
this.transitionEndTrigger = null;
|
||||
|
||||
// directly use deltaY, see https://caniuse.com/?search=deltaY
|
||||
// use deltaY to detect direction for not special setting device
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
|
||||
const { deltaY } = event;
|
||||
if (this.startOffset === 0 && deltaY < 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// get offset
|
||||
// if necessary, can adjust distance value to make scrolling smoother
|
||||
const distance = -deltaY;
|
||||
this.offset = range(
|
||||
this.startOffset + distance,
|
||||
-(this.count * this.itemHeight),
|
||||
this.itemHeight
|
||||
);
|
||||
|
||||
if (mousewheelTimer) {
|
||||
clearTimeout(mousewheelTimer);
|
||||
}
|
||||
|
||||
mousewheelTimer = setTimeout(() => {
|
||||
this.onTouchEnd();
|
||||
this.touchStartTime = 0;
|
||||
}, MOMENTUM_LIMIT_TIME);
|
||||
},
|
||||
|
||||
onTransitionEnd() {
|
||||
this.stopMomentum();
|
||||
},
|
||||
|
@ -1,6 +1,7 @@
|
||||
import Picker from '..';
|
||||
import PickerColumn from '../PickerColumn';
|
||||
import PickerColumn, { MOMENTUM_LIMIT_TIME, MOMENTUM_LIMIT_DISTANCE} from '../PickerColumn';
|
||||
import { mount, triggerDrag, later } from '../../../test';
|
||||
import { DEFAULT_ITEM_HEIGHT } from '../shared';
|
||||
|
||||
const simpleColumn = ['1990', '1991', '1992', '1993', '1994', '1995'];
|
||||
const columns = [
|
||||
@ -336,3 +337,66 @@ test('readonly prop', () => {
|
||||
|
||||
expect(wrapper.emitted('change')).toBeFalsy();
|
||||
});
|
||||
|
||||
test('wheel event on columns is detected', async () => {
|
||||
const onMouseWheel = jest.spyOn(PickerColumn.methods, 'onMouseWheel');
|
||||
|
||||
const wrapper = mount(PickerColumn, {
|
||||
propsData: {
|
||||
initialOptions: [...simpleColumn],
|
||||
},
|
||||
});
|
||||
|
||||
await wrapper.trigger('wheel');
|
||||
|
||||
expect(onMouseWheel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('wheel scroll on columns', async () => {
|
||||
const fakeScroll = (translateY, deltaY)=> {
|
||||
// mock getComputedStyle
|
||||
// see: https://github.com/jsdom/jsdom/issues/2588
|
||||
const originGetComputedStyle = window.getComputedStyle;
|
||||
window.getComputedStyle = (ele) => {
|
||||
const style = originGetComputedStyle(ele);
|
||||
return {
|
||||
...style,
|
||||
transform: `matrix(1, 0, 0, 1, 0, ${translateY})`,
|
||||
};
|
||||
};
|
||||
return new Promise(resolve => {
|
||||
const wrapper = mount(Picker, {
|
||||
propsData: {
|
||||
columns: simpleColumn,
|
||||
},
|
||||
});
|
||||
|
||||
wrapper.find('.van-picker-column').trigger('wheel', {
|
||||
deltaY,
|
||||
});
|
||||
|
||||
return later(MOMENTUM_LIMIT_TIME + 10).then(()=>{
|
||||
wrapper.find('.van-picker-column ul').trigger('transitionend');
|
||||
resolve(wrapper.emitted('change'));
|
||||
}).finally(()=>{
|
||||
window.getComputedStyle = originGetComputedStyle;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const topToDown = await fakeScroll(110, -MOMENTUM_LIMIT_DISTANCE);
|
||||
expect(topToDown).toEqual(undefined);
|
||||
|
||||
const topToUp = await fakeScroll(110, MOMENTUM_LIMIT_DISTANCE + 10);
|
||||
expect(topToUp[0][1]).toEqual('1991');
|
||||
|
||||
const bottomToUp = await fakeScroll(-110, MOMENTUM_LIMIT_DISTANCE + 5);
|
||||
expect(bottomToUp[0][1]).toEqual('1995');
|
||||
|
||||
const bottomToDown = await fakeScroll(-110, -(MOMENTUM_LIMIT_DISTANCE - 5));
|
||||
expect(bottomToDown[0][1]).toEqual('1995');
|
||||
|
||||
const pos1992 = simpleColumn.indexOf('1992')
|
||||
const momentum = await fakeScroll(-110 + (pos1992 + 1) * DEFAULT_ITEM_HEIGHT, MOMENTUM_LIMIT_DISTANCE + 10);
|
||||
expect(momentum[0][1]).toEqual(simpleColumn[pos1992+1]);
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user