272 lines
11 KiB
JavaScript
272 lines
11 KiB
JavaScript
import React from 'react';
|
|
import { View, Text, ScrollView, PixelRatio } from 'react-native';
|
|
import variables from '../../common/styles/variables';
|
|
import { range } from '../../common/utils';
|
|
import scrollpickerStyles from './styles';
|
|
export { scrollpickerStyles };
|
|
const px = 1 / PixelRatio.get();
|
|
const DEFAULT_CONTAINER_HEIGHT = 1;
|
|
export class Scrollpicker extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.containerRef = null;
|
|
this.scrollers = [];
|
|
this.targetItemHeight = null;
|
|
this.containerHeight = null;
|
|
this.containerRef = null;
|
|
const data = this.initialize(props);
|
|
this.state = {
|
|
...data,
|
|
targetItemHeight: null,
|
|
containerHeight: null
|
|
};
|
|
this.scrollers = [];
|
|
}
|
|
initialize(props) {
|
|
const data = this.initData(props);
|
|
return data;
|
|
}
|
|
initData(props) {
|
|
let { list, proportion, value } = props;
|
|
if (!list || !list.length) {
|
|
throw TypeError('提供有效的 list 参数');
|
|
}
|
|
const { offsetCount } = this.props;
|
|
const placeholderList = range(offsetCount).map(() => {
|
|
return '';
|
|
});
|
|
list = list.map(scrollItem => {
|
|
const tmp = scrollItem.concat();
|
|
[].push.apply(tmp, placeholderList);
|
|
[].unshift.apply(tmp, placeholderList);
|
|
return tmp;
|
|
});
|
|
const length = list.length;
|
|
if (!proportion ||
|
|
!proportion.length ||
|
|
(proportion && proportion.length && proportion.length !== length)) {
|
|
proportion = range(length).map(() => {
|
|
return 1;
|
|
});
|
|
}
|
|
if (!value ||
|
|
!value.length ||
|
|
(value && value.length && value.length !== length)) {
|
|
value = range(length).map(() => {
|
|
return 0;
|
|
});
|
|
}
|
|
return {
|
|
list,
|
|
value,
|
|
proportion
|
|
};
|
|
}
|
|
componentDidMount() {
|
|
this.getUIData(this.containerRef, DEFAULT_CONTAINER_HEIGHT).then((data) => {
|
|
const { targetItemHeight } = data;
|
|
const containerHeight = this.resizeContainerHeight(targetItemHeight);
|
|
this.setState({
|
|
containerHeight,
|
|
targetItemHeight
|
|
}, () => {
|
|
this.getUIData(this.containerRef, this.state.containerHeight).then((uiData) => {
|
|
const { value } = this.state;
|
|
value.forEach((item, index) => {
|
|
this.scrollTo(index, item, false);
|
|
});
|
|
}).catch((e) => {
|
|
console.log(e);
|
|
});
|
|
});
|
|
}).catch((e) => {
|
|
console.log(e);
|
|
});
|
|
}
|
|
componentWillReceiveProps(nextProps) {
|
|
if (nextProps !== this.props) {
|
|
const data = this.initialize(nextProps);
|
|
this.setState({
|
|
...data
|
|
}, () => {
|
|
setTimeout(() => {
|
|
const { value } = this.state;
|
|
value.forEach((item, index) => {
|
|
this.scrollTo(index, item);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
getUIData(element, accurateHeight, maxCount) {
|
|
let count = 0;
|
|
maxCount = maxCount == null ? 20 : maxCount;
|
|
return new Promise((resolve, reject) => {
|
|
let toCheck = null;
|
|
let measure = () => {
|
|
let ret = null;
|
|
element.measure((x, y, width, height, left, top) => {
|
|
// console.log(
|
|
// `Get container height: ${height}, accurate height: ${accurateHeight} and target item height: ${
|
|
// this.targetItemHeight
|
|
// } for ${++count}th.`
|
|
// )
|
|
if (height) {
|
|
// 安卓机器获取高度不精确
|
|
const needToReset = height % 1 === 0 ? false : true;
|
|
let minHeight;
|
|
let maxHeight;
|
|
if (needToReset) {
|
|
minHeight = Math.floor(height);
|
|
maxHeight = minHeight + 1;
|
|
}
|
|
else {
|
|
minHeight = maxHeight = height;
|
|
}
|
|
if ((minHeight === accurateHeight || maxHeight === accurateHeight) &&
|
|
this.targetItemHeight) {
|
|
ret = {
|
|
rect: {
|
|
x,
|
|
y,
|
|
width,
|
|
height
|
|
},
|
|
targetItemHeight: this.targetItemHeight
|
|
};
|
|
}
|
|
}
|
|
toCheck(ret);
|
|
});
|
|
};
|
|
toCheck = (ret) => {
|
|
if (ret) {
|
|
return resolve(ret);
|
|
}
|
|
else {
|
|
if (count < maxCount) {
|
|
setTimeout(() => {
|
|
measure();
|
|
}, 20);
|
|
}
|
|
else {
|
|
return reject('获取元素高度失败!');
|
|
}
|
|
}
|
|
};
|
|
measure();
|
|
});
|
|
}
|
|
resizeContainerHeight(targetItemHeight) {
|
|
const { offsetCount } = this.props;
|
|
const ret = targetItemHeight + 2 * (targetItemHeight * offsetCount);
|
|
return ret;
|
|
}
|
|
locateIndicator(targetItemHeight) {
|
|
const styles = scrollpickerStyles;
|
|
const { offsetCount } = this.props;
|
|
return (React.createElement(View, { style: [styles.indicator], pointerEvents: 'none' },
|
|
React.createElement(View, { style: [
|
|
styles.indicator,
|
|
styles.indicatorMask,
|
|
{ bottom: targetItemHeight + offsetCount * targetItemHeight },
|
|
{ borderBottomWidth: 1 * px, borderBottomColor: variables.mtdBorderColorDark }
|
|
] }),
|
|
React.createElement(View, { style: [
|
|
styles.indicator,
|
|
styles.indicatorMask,
|
|
{ top: targetItemHeight + offsetCount * targetItemHeight },
|
|
{ borderTopWidth: 1 * px, borderTopColor: variables.mtdBorderColorDark }
|
|
] })));
|
|
}
|
|
scrollTo(scrollIndex, targetItemIndex, animated) {
|
|
const { targetItemHeight } = this.state;
|
|
// const { offsetCount } = this.props
|
|
this.scrollProper(scrollIndex, targetItemHeight * targetItemIndex, animated);
|
|
}
|
|
onScroll(scrollIndex, scrollHeight) {
|
|
const targetItemIndex = this.scrollProper(scrollIndex, scrollHeight);
|
|
this.props.onChange && this.props.onChange(scrollIndex, targetItemIndex);
|
|
}
|
|
scrollProper(scrollIndex, scrollHeight, animated) {
|
|
const { targetItemHeight, list } = this.state;
|
|
const { offsetCount } = this.props;
|
|
const scrollListLength = list[scrollIndex].length;
|
|
let newScrollHeight;
|
|
const min = 0;
|
|
const max = (scrollListLength - 2 * offsetCount - 1) * targetItemHeight;
|
|
if (scrollHeight <= min) {
|
|
newScrollHeight = min;
|
|
}
|
|
else if (scrollHeight >= max) {
|
|
newScrollHeight = max;
|
|
}
|
|
else {
|
|
const quotient = parseInt(String(scrollHeight / targetItemHeight), 10);
|
|
newScrollHeight = quotient * targetItemHeight;
|
|
const halfHeight = targetItemHeight / 2;
|
|
if (scrollHeight - newScrollHeight > halfHeight) {
|
|
newScrollHeight += targetItemHeight;
|
|
}
|
|
}
|
|
this.scrollers[scrollIndex] && this.scrollers[scrollIndex].scrollTo && this.scrollers[scrollIndex].scrollTo({
|
|
x: 0,
|
|
y: newScrollHeight,
|
|
animated: animated === false ? false : true
|
|
});
|
|
const targetItemIndex = newScrollHeight / targetItemHeight;
|
|
return targetItemIndex;
|
|
}
|
|
render() {
|
|
const styles = scrollpickerStyles;
|
|
const { list, proportion, containerHeight, targetItemHeight } = this.state;
|
|
return (React.createElement(View, { ref: el => {
|
|
this.containerRef = el;
|
|
}, style: [
|
|
styles.container,
|
|
this.props.style,
|
|
{ height: containerHeight || DEFAULT_CONTAINER_HEIGHT }
|
|
] },
|
|
containerHeight && this.locateIndicator(targetItemHeight),
|
|
list.map((scrollItem, scrollIndex) => {
|
|
return (React.createElement(View, { key: scrollIndex, style: [
|
|
styles.proportionWrapper,
|
|
{ flex: Number(proportion[scrollIndex]) }
|
|
] },
|
|
React.createElement(ScrollView, { ref: c => {
|
|
this.scrollers[scrollIndex] = c;
|
|
}, style: styles.scroller, showsVerticalScrollIndicator: false, contentContainerStyle: [styles.scrollerContentContainer], onScrollEndDrag: (e) => {
|
|
this.onScroll(scrollIndex, e.nativeEvent.contentOffset.y);
|
|
} }, scrollItem.map((item, index) => {
|
|
return (React.createElement(View, { key: index, style: [styles.targetItem, { height: targetItemHeight }], onLayout: e => {
|
|
if (item &&
|
|
this.targetItemHeight == null &&
|
|
e.nativeEvent.layout.height) {
|
|
this.targetItemHeight = Math.ceil(e.nativeEvent.layout.height);
|
|
// console.log(
|
|
// 'OnLayout get target item height:',
|
|
// this.targetItemHeight
|
|
// )
|
|
}
|
|
} }, this.props.renderItem ? this.props.renderItem(item, index) :
|
|
React.createElement(Text, { style: [
|
|
styles.targetItemContent
|
|
], numberOfLines: 1 }, typeof item === 'object' ? item.label : item)));
|
|
}))));
|
|
})));
|
|
}
|
|
}
|
|
Scrollpicker.defaultProps = {
|
|
style: {},
|
|
list: [
|
|
['第一列第一项', '第一列第二项', '第一列第三项'],
|
|
['第二列第一项', '第二列第二项', '第二列第三项'],
|
|
['第三列第一项', '第三列第二项', '第三列第三项']
|
|
],
|
|
value: [],
|
|
proportion: [2, 1, 1],
|
|
offsetCount: 2,
|
|
onChange: null,
|
|
renderItem: null
|
|
};
|
|
//# sourceMappingURL=index.js.map
|