mirror of
https://github.com/youzan/vant.git
synced 2025-10-18 17:51:54 +00:00
picker component
This commit is contained in:
51
packages/picker/src/draggable.js
Normal file
51
packages/picker/src/draggable.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import Vue from 'vue';
|
||||
|
||||
let isDragging = false;
|
||||
|
||||
const supportTouch = !Vue.prototype.$isServer && 'ontouchstart' in window;
|
||||
|
||||
export default function(element, options) {
|
||||
const moveFn = function(event) {
|
||||
if (options.drag) {
|
||||
options.drag(supportTouch ? event.changedTouches[0] || event.touches[0] : event);
|
||||
}
|
||||
};
|
||||
|
||||
const endFn = function(event) {
|
||||
if (!supportTouch) {
|
||||
document.removeEventListener('mousemove', moveFn);
|
||||
document.removeEventListener('mouseup', endFn);
|
||||
}
|
||||
document.onselectstart = null;
|
||||
document.ondragstart = null;
|
||||
|
||||
isDragging = false;
|
||||
|
||||
if (options.end) {
|
||||
options.end(supportTouch ? event.changedTouches[0] || event.touches[0] : event);
|
||||
}
|
||||
};
|
||||
|
||||
element.addEventListener(supportTouch ? 'touchstart' : 'mousedown', function(event) {
|
||||
if (isDragging) return;
|
||||
document.onselectstart = function() { return false; };
|
||||
document.ondragstart = function() { return false; };
|
||||
|
||||
if (!supportTouch) {
|
||||
document.addEventListener('mousemove', moveFn);
|
||||
document.addEventListener('mouseup', endFn);
|
||||
}
|
||||
isDragging = true;
|
||||
|
||||
if (options.start) {
|
||||
event.preventDefault();
|
||||
options.start(supportTouch ? event.changedTouches[0] || event.touches[0] : event);
|
||||
}
|
||||
});
|
||||
|
||||
if (supportTouch) {
|
||||
element.addEventListener('touchmove', moveFn);
|
||||
element.addEventListener('touchend', endFn);
|
||||
element.addEventListener('touchcancel', endFn);
|
||||
}
|
||||
};
|
@@ -1,14 +1,21 @@
|
||||
<template>
|
||||
<div class="z-picker-column">
|
||||
<div class="z-picker-column-wrapper">
|
||||
<div class="z-picker-item">
|
||||
|
||||
<div class="z-picker-column-wrapper" :class="{ dragging: isDragging }" ref="wrapper" :style="{ height: visibleContentHeight + 'px' }">
|
||||
<div
|
||||
v-for="item in currentValues"
|
||||
class="z-picker-column__item"
|
||||
:class="{ 'z-picker-column__item--selected': item === currentValue }"
|
||||
:style="{ height: itemHeight + 'px', lineHeight: itemHeight + 'px' }">
|
||||
{{item}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import translateUtil from 'src/utils/transition';
|
||||
import draggable from './draggable';
|
||||
|
||||
const DEFAULT_ITEM_HEIGHT = 44;
|
||||
|
||||
export default {
|
||||
@@ -40,7 +47,7 @@ export default {
|
||||
return {
|
||||
currentValue: this.value,
|
||||
currentValues: this.values,
|
||||
dragging: false
|
||||
isDragging: false
|
||||
};
|
||||
},
|
||||
|
||||
@@ -50,11 +57,16 @@ export default {
|
||||
},
|
||||
|
||||
currentValues(val) {
|
||||
|
||||
if (this.valueIndex === -1) {
|
||||
this.currentValue = (val || [])[0];
|
||||
}
|
||||
},
|
||||
|
||||
currentValue(val) {
|
||||
this.doOnValueChange();
|
||||
|
||||
this.$emit('change', this);
|
||||
this.$emit('input', val);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -64,11 +76,128 @@ export default {
|
||||
*/
|
||||
visibleContentHeight() {
|
||||
return this.itemHeight * this.visibileColumnCount;
|
||||
},
|
||||
|
||||
valueIndex() {
|
||||
return this.currentValues.indexOf(this.currentValue);
|
||||
},
|
||||
|
||||
dragRange() {
|
||||
var values = this.currentValues;
|
||||
var visibileColumnCount = this.visibileColumnCount;
|
||||
var itemHeight = this.itemHeight;
|
||||
|
||||
return [ -itemHeight * (values.length - Math.ceil(visibileColumnCount / 2)), itemHeight * Math.floor(visibileColumnCount / 2) ];
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
mounted() {
|
||||
this.ready = true;
|
||||
this.$emit('input', this.currentValue);
|
||||
|
||||
this.initEvents();
|
||||
this.doOnValueChange();
|
||||
},
|
||||
|
||||
methods: {
|
||||
value2Translate(value) {
|
||||
let values = this.currentValues;
|
||||
let valueIndex = values.indexOf(value);
|
||||
let offset = Math.floor(this.visibileColumnCount / 2);
|
||||
let itemHeight = this.itemHeight;
|
||||
|
||||
if (valueIndex !== -1) {
|
||||
return (valueIndex - offset) * -itemHeight;
|
||||
}
|
||||
},
|
||||
|
||||
translate2Value(translate) {
|
||||
let itemHeight = this.itemHeight;
|
||||
translate = Math.round(translate / itemHeight) * itemHeight;
|
||||
|
||||
let index = -(translate - Math.floor(this.visibileColumnCount / 2) * itemHeight) / itemHeight;
|
||||
|
||||
return this.currentValues[index];
|
||||
},
|
||||
|
||||
initEvents() {
|
||||
var el = this.$refs.wrapper;
|
||||
var dragState = {};
|
||||
|
||||
var velocityTranslate, prevTranslate, pickerItems;
|
||||
|
||||
draggable(el, {
|
||||
start: (event) => {
|
||||
dragState = {
|
||||
range: this.dragRange,
|
||||
start: new Date(),
|
||||
startLeft: event.pageX,
|
||||
startTop: event.pageY,
|
||||
startTranslateTop: translateUtil.getElementTranslate(el).top
|
||||
};
|
||||
pickerItems = el.querySelectorAll('.z-picker-item');
|
||||
},
|
||||
|
||||
drag: (event) => {
|
||||
this.isDragging = true;
|
||||
|
||||
dragState.left = event.pageX;
|
||||
dragState.top = event.pageY;
|
||||
|
||||
let deltaY = dragState.top - dragState.startTop;
|
||||
let translate = dragState.startTranslateTop + deltaY;
|
||||
|
||||
translateUtil.translateElement(el, null, translate);
|
||||
|
||||
velocityTranslate = translate - prevTranslate || translate;
|
||||
|
||||
prevTranslate = translate;
|
||||
},
|
||||
|
||||
end: () => {
|
||||
if (this.isDragging) {
|
||||
this.isDragging = false;
|
||||
|
||||
var momentumRatio = 7;
|
||||
var currentTranslate = translateUtil.getElementTranslate(el).top;
|
||||
var duration = new Date() - dragState.start;
|
||||
|
||||
var momentumTranslate;
|
||||
if (duration < 300) {
|
||||
momentumTranslate = currentTranslate + velocityTranslate * momentumRatio;
|
||||
}
|
||||
|
||||
var dragRange = dragState.range;
|
||||
|
||||
this.$nextTick(() => {
|
||||
var translate;
|
||||
var itemHeight = this.itemHeight;
|
||||
|
||||
if (momentumTranslate) {
|
||||
translate = Math.round(momentumTranslate / itemHeight) * itemHeight;
|
||||
} else {
|
||||
translate = Math.round(currentTranslate / itemHeight) * itemHeight;
|
||||
}
|
||||
|
||||
translate = Math.max(Math.min(translate, dragRange[1]), dragRange[0]);
|
||||
|
||||
translateUtil.translateElement(el, null, translate);
|
||||
|
||||
this.currentValue = this.translate2Value(translate);
|
||||
});
|
||||
}
|
||||
|
||||
dragState = {};
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
doOnValueChange() {
|
||||
let value = this.currentValue;
|
||||
let wrapper = this.$refs.wrapper;
|
||||
|
||||
translateUtil.translateElement(wrapper, null, this.value2Translate(value));
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
<div class="z-picker">
|
||||
<div class="z-picker-toolbar">
|
||||
<div class="z-picker__toolbar">
|
||||
<slot>
|
||||
</slot>
|
||||
</div>
|
||||
<div class="z-picker-columns">
|
||||
<div class="z-picker__columns" :class="['z-picker__columns--' + columns.length]">
|
||||
<picker-column
|
||||
v-for="(item, index) in columns"
|
||||
v-model="values[index]"
|
||||
@@ -69,7 +69,7 @@ export default {
|
||||
let values = [];
|
||||
|
||||
columns.forEach(column => {
|
||||
values.push(column.value || column[column.defaultIndex || 0]);
|
||||
values.push(column.value || column.values[column.defaultIndex || 0]);
|
||||
});
|
||||
|
||||
return values;
|
||||
|
Reference in New Issue
Block a user