[improvement] Picker: refactor component with movable-view

This commit is contained in:
rex 2019-04-17 10:36:09 +08:00 committed by GitHub
parent b26cb5ba72
commit 9752c832d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 96 additions and 132 deletions

View File

@ -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;
} }
}); });

View File

@ -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;

View File

@ -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];
} }
} }
}); });

View File

@ -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>

View File

@ -100,7 +100,7 @@ Page({
```javascript ```javascript
const citys = { const citys = {
'浙江': ['杭州', '宁波', '温州', '嘉兴', '湖州'], '浙江': ['杭州', { text: '宁波', disabled: true }, '温州', '嘉兴', '湖州'],
'福建': ['福州', '厦门', '莆田', '三明', '泉州'] '福建': ['福州', '厦门', '莆田', '三明', '泉州']
}; };

View File

@ -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