mirror of
https://gitee.com/vant-contrib/vant-weapp.git
synced 2025-04-26 19:36:39 +08:00
[improvement] Picker: refactor component with movable-view
This commit is contained in:
parent
b26cb5ba72
commit
9752c832d2
@ -1,25 +1,22 @@
|
|||||||
import Page from '../../common/page';
|
import Page from '../../common/page';
|
||||||
import Toast from '../../dist/toast/toast';
|
import Toast from '../../dist/toast/toast';
|
||||||
|
|
||||||
|
const citys = {
|
||||||
|
'浙江': ['杭州', { text: '宁波', disabled: true }, '温州', '嘉兴', '湖州'],
|
||||||
|
'福建': ['福州', '厦门', '莆田', '三明', '泉州']
|
||||||
|
};
|
||||||
|
|
||||||
Page({
|
Page({
|
||||||
data: {
|
data: {
|
||||||
column1: ['杭州', '宁波', '温州', '嘉兴', '湖州'],
|
column1: ['杭州', '宁波', '温州', '嘉兴', '湖州'],
|
||||||
column2: [
|
column2: citys['浙江'],
|
||||||
{ text: '杭州', disabled: true },
|
|
||||||
{ text: '宁波' },
|
|
||||||
{ text: '温州' }
|
|
||||||
],
|
|
||||||
column3: {
|
|
||||||
浙江: ['杭州', '宁波', '温州', '嘉兴', '湖州'],
|
|
||||||
福建: ['福州', '厦门', '莆田', '三明', '泉州']
|
|
||||||
},
|
|
||||||
column4: [
|
column4: [
|
||||||
{
|
{
|
||||||
values: ['浙江', '福建'],
|
values: Object.keys(citys),
|
||||||
className: 'column1'
|
className: 'column1'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
values: ['杭州', '宁波', '温州', '嘉兴', '湖州'],
|
values: citys['浙江'],
|
||||||
className: 'column2',
|
className: 'column2',
|
||||||
defaultIndex: 2
|
defaultIndex: 2
|
||||||
}
|
}
|
||||||
@ -42,7 +39,6 @@ Page({
|
|||||||
|
|
||||||
onChange2(event) {
|
onChange2(event) {
|
||||||
const { picker, value } = event.detail;
|
const { picker, value } = event.detail;
|
||||||
picker.setColumnValues(1, this.data.column3[value[0]]);
|
picker.setColumnValues(1, citys[value[0]]);
|
||||||
getApp().picker = picker;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
@import '../common/style/var';
|
@import '../common/style/var';
|
||||||
|
|
||||||
.van-picker-column {
|
:host {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.van-picker-column {
|
||||||
|
width: 100%;
|
||||||
|
overflow: visible;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
import { VantComponent } from '../common/component';
|
import { VantComponent } from '../common/component';
|
||||||
import { isObj, range } from '../common/utils';
|
import { isObj, range } from '../common/utils';
|
||||||
|
|
||||||
const DEFAULT_DURATION = 200;
|
function isDisabled(option: any) {
|
||||||
|
return isObj(option) && option.disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOptionText(option: any, valueKey: string) {
|
||||||
|
return isObj(option) && valueKey in option ? option[valueKey] : option;
|
||||||
|
}
|
||||||
|
|
||||||
VantComponent({
|
VantComponent({
|
||||||
classes: ['active-class'],
|
classes: ['active-class'],
|
||||||
@ -17,17 +23,18 @@ VantComponent({
|
|||||||
},
|
},
|
||||||
defaultIndex: {
|
defaultIndex: {
|
||||||
type: Number,
|
type: Number,
|
||||||
value: 0
|
value: 0,
|
||||||
|
observer(value) {
|
||||||
|
this.setIndex(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
data: {
|
data: {
|
||||||
startY: 0,
|
|
||||||
offset: 0,
|
offset: 0,
|
||||||
duration: 0,
|
|
||||||
startOffset: 0,
|
|
||||||
options: [],
|
options: [],
|
||||||
currentIndex: 0
|
currentIndex: 0,
|
||||||
|
animation: false
|
||||||
},
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
@ -36,71 +43,38 @@ VantComponent({
|
|||||||
this.set({
|
this.set({
|
||||||
currentIndex: defaultIndex,
|
currentIndex: defaultIndex,
|
||||||
options: initialOptions
|
options: initialOptions
|
||||||
}).then(() => {
|
}).then(() => this.setIndex(defaultIndex));
|
||||||
this.setIndex(defaultIndex);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
count() {
|
|
||||||
return this.data.options.length;
|
|
||||||
},
|
|
||||||
|
|
||||||
baseOffset() {
|
|
||||||
const { data } = this;
|
|
||||||
return (data.itemHeight * (data.visibleItemCount - 1)) / 2;
|
|
||||||
},
|
|
||||||
|
|
||||||
wrapperStyle() {
|
|
||||||
const { data } = this;
|
|
||||||
return [
|
|
||||||
`transition: ${data.duration}ms`,
|
|
||||||
`transform: translate3d(0, ${data.offset + data.baseOffset}px, 0)`,
|
|
||||||
`line-height: ${data.itemHeight}px`
|
|
||||||
].join('; ');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
defaultIndex(value: number) {
|
|
||||||
this.setIndex(value);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onTouchStart(event: Weapp.TouchEvent) {
|
onChange(event) {
|
||||||
this.set({
|
if (!event.detail.source) {
|
||||||
startY: event.touches[0].clientY,
|
return;
|
||||||
startOffset: this.data.offset,
|
}
|
||||||
duration: 0
|
|
||||||
});
|
this.offset = event.detail.y;
|
||||||
},
|
},
|
||||||
|
|
||||||
onTouchMove(event: Weapp.TouchEvent) {
|
onTouchStart() {
|
||||||
const { data } = this;
|
// open animate at first touch
|
||||||
const deltaY = event.touches[0].clientY - data.startY;
|
if (!this.data.animation) {
|
||||||
this.set({
|
this.set({ animation: true });
|
||||||
offset: range(
|
}
|
||||||
data.startOffset + deltaY,
|
|
||||||
-(data.count * data.itemHeight),
|
|
||||||
data.itemHeight
|
|
||||||
)
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onTouchEnd() {
|
onTouchEnd() {
|
||||||
const { data } = this;
|
const { options = [], itemHeight, offset } = this.data;
|
||||||
if (data.offset !== data.startOffset) {
|
|
||||||
this.set({
|
if (this.offset === offset) {
|
||||||
duration: DEFAULT_DURATION
|
return;
|
||||||
});
|
}
|
||||||
|
|
||||||
const index = range(
|
const index = range(
|
||||||
Math.round(-data.offset / data.itemHeight),
|
Math.round(-this.offset / itemHeight),
|
||||||
0,
|
0,
|
||||||
data.count - 1
|
options.length - 1
|
||||||
);
|
);
|
||||||
this.setIndex(index, true);
|
this.setIndex(index, true);
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onClickItem(event: Weapp.Event) {
|
onClickItem(event: Weapp.Event) {
|
||||||
@ -109,33 +83,27 @@ VantComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
adjustIndex(index: number) {
|
adjustIndex(index: number) {
|
||||||
const { data } = this;
|
const { options = [] } = this.data;
|
||||||
index = range(index, 0, data.count);
|
const count = options.length;
|
||||||
for (let i = index; i < data.count; i++) {
|
index = range(index, 0, count);
|
||||||
if (!this.isDisabled(data.options[i])) return i;
|
|
||||||
|
for (let i = index; i < count; i++) {
|
||||||
|
if (!isDisabled(options[i])) return i;
|
||||||
}
|
}
|
||||||
for (let i = index - 1; i >= 0; i--) {
|
for (let i = index - 1; i >= 0; i--) {
|
||||||
if (!this.isDisabled(data.options[i])) return i;
|
if (!isDisabled(options[i])) return i;
|
||||||
}
|
}
|
||||||
},
|
return 0;
|
||||||
|
|
||||||
isDisabled(option: any) {
|
|
||||||
return isObj(option) && option.disabled;
|
|
||||||
},
|
|
||||||
|
|
||||||
getOptionText(option: any) {
|
|
||||||
const { data } = this;
|
|
||||||
return isObj(option) && data.valueKey in option
|
|
||||||
? option[data.valueKey]
|
|
||||||
: option;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setIndex(index: number, userAction: boolean) {
|
setIndex(index: number, userAction: boolean) {
|
||||||
const { data } = this;
|
const { itemHeight, currentIndex } = this.data;
|
||||||
index = this.adjustIndex(index) || 0;
|
index = this.adjustIndex(index);
|
||||||
const offset = -index * data.itemHeight;
|
const offset = -index * itemHeight;
|
||||||
|
|
||||||
if (index !== data.currentIndex) {
|
this.offset = offset;
|
||||||
|
|
||||||
|
if (index !== currentIndex) {
|
||||||
return this.set({ offset, currentIndex: index }).then(() => {
|
return this.set({ offset, currentIndex: index }).then(() => {
|
||||||
userAction && this.$emit('change', index);
|
userAction && this.$emit('change', index);
|
||||||
});
|
});
|
||||||
@ -145,18 +113,17 @@ VantComponent({
|
|||||||
},
|
},
|
||||||
|
|
||||||
setValue(value: string) {
|
setValue(value: string) {
|
||||||
const { options } = this.data;
|
const { options = [], valueKey } = this.data;
|
||||||
for (let i = 0; i < options.length; i++) {
|
|
||||||
if (this.getOptionText(options[i]) === value) {
|
const index = options.findIndex(
|
||||||
return this.setIndex(i);
|
item => getOptionText(item, valueKey) === value
|
||||||
}
|
);
|
||||||
}
|
return index !== -1 ? this.setIndex(index) : Promise.resolve();
|
||||||
return Promise.resolve();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getValue() {
|
getValue() {
|
||||||
const { data } = this;
|
const { options = [], currentIndex } = this.data;
|
||||||
return data.options[data.currentIndex];
|
return options[currentIndex];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,31 +1,27 @@
|
|||||||
<view
|
<wxs src="../wxs/utils.wxs" module="utils" />
|
||||||
|
|
||||||
|
<movable-area
|
||||||
class="van-picker-column custom-class"
|
class="van-picker-column custom-class"
|
||||||
style="height: {{ itemHeight * visibleItemCount }}px"
|
style="height: {{ itemHeight }}px; top: {{ itemHeight * (visibleItemCount - 1) / 2 }}px;"
|
||||||
bind:touchstart="onTouchStart"
|
>
|
||||||
catch:touchmove="onTouchMove"
|
<movable-view
|
||||||
bind:touchend="onTouchEnd"
|
direction="vertical"
|
||||||
bind:touchcancel="onTouchEnd"
|
out-of-bounds
|
||||||
|
damping="{{ 50 }}"
|
||||||
|
y="{{ offset }}"
|
||||||
|
animation="{{ animation }}"
|
||||||
|
style="line-height: {{ itemHeight }}px; width: 100%; height: {{ itemHeight * options.length }}px;"
|
||||||
|
bindchange="onChange"
|
||||||
|
bindtouchstart="onTouchStart"
|
||||||
|
bindtouchend="onTouchEnd"
|
||||||
>
|
>
|
||||||
<view style="{{ wrapperStyle }}">
|
|
||||||
<view
|
<view
|
||||||
wx:for="{{ options }}"
|
wx:for="{{ options }}"
|
||||||
wx:for-item="option"
|
|
||||||
wx:key="index"
|
wx:key="index"
|
||||||
data-index="{{ index }}"
|
data-index="{{ index }}"
|
||||||
style="height: {{ itemHeight }}px"
|
style="height: {{ itemHeight }}px"
|
||||||
class="van-ellipsis van-picker-column__item {{ option && option.disabled ? 'van-picker-column__item--disabled' : '' }} {{ index === currentIndex ? 'van-picker-column__item--selected active-class' : '' }}"
|
class="van-ellipsis {{ utils.bem('picker-column__item', { disabled: item && item.disabled, selected: index === currentIndex }) }} {{ index === currentIndex ? 'active-class' : '' }}"
|
||||||
bindtap="onClickItem"
|
bindtap="onClickItem"
|
||||||
>{{ getOptionText(option, valueKey) }}</view>
|
>{{ item && item[valueKey] ? item[valueKey] : item }}</view>
|
||||||
</view>
|
</movable-view>
|
||||||
</view>
|
</movable-area>
|
||||||
|
|
||||||
<wxs module="getOptionText">
|
|
||||||
function isObj(x) {
|
|
||||||
var type = typeof x;
|
|
||||||
return x !== null && (type === 'object' || type === 'function');
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = function (option, valueKey) {
|
|
||||||
return isObj(option) && option[valueKey] ? option[valueKey] : option;
|
|
||||||
}
|
|
||||||
</wxs>
|
|
||||||
|
@ -100,7 +100,7 @@ Page({
|
|||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const citys = {
|
const citys = {
|
||||||
'浙江': ['杭州', '宁波', '温州', '嘉兴', '湖州'],
|
'浙江': ['杭州', { text: '宁波', disabled: true }, '温州', '嘉兴', '湖州'],
|
||||||
'福建': ['福州', '厦门', '莆田', '三明', '泉州']
|
'福建': ['福州', '厦门', '莆田', '三明', '泉州']
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -32,17 +32,17 @@
|
|||||||
catch:touchmove="noop"
|
catch:touchmove="noop"
|
||||||
>
|
>
|
||||||
<picker-column
|
<picker-column
|
||||||
class="van-picker__column"
|
|
||||||
wx:for="{{ isSimple(columns) ? [columns] : columns }}"
|
wx:for="{{ isSimple(columns) ? [columns] : columns }}"
|
||||||
wx:key="{{ index }}"
|
wx:key="{{ index }}"
|
||||||
data-index="{{ index }}"
|
class="van-picker__column"
|
||||||
custom-class="column-class"
|
|
||||||
value-key="{{ valueKey }}"
|
value-key="{{ valueKey }}"
|
||||||
initial-options="{{ isSimple(columns) ? item : item.values }}"
|
initial-options="{{ isSimple(columns) ? item : item.values }}"
|
||||||
default-index="{{ item.defaultIndex }}"
|
default-index="{{ item.defaultIndex }}"
|
||||||
item-height="{{ itemHeight }}"
|
item-height="{{ itemHeight }}"
|
||||||
visible-item-count="{{ visibleItemCount }}"
|
visible-item-count="{{ visibleItemCount }}"
|
||||||
|
custom-class="column-class"
|
||||||
active-class="active-class"
|
active-class="active-class"
|
||||||
|
data-index="{{ index }}"
|
||||||
bind:change="onChange"
|
bind:change="onChange"
|
||||||
/>
|
/>
|
||||||
<view
|
<view
|
||||||
|
Loading…
x
Reference in New Issue
Block a user