此功能是在 react native 第三方组件 react-native-scrollable-tab-view 使用自定义 tabBar 做出的效果
效果展示
代码
1.自定义的 ScrollableTabBar
新建文件 SongCustomTabBar.js
const React = require('react');
const { ViewPropTypes } = ReactNative = require('react-native');
const PropTypes = require('prop-types');
const createReactClass = require('create-react-class');
const {
View,
Animated,
StyleSheet,
ScrollView,
Text,
Platform,
Dimensions,
} = ReactNative;
const Button = require('./Button');
const WINDOW_WIDTH = Dimensions.get('window').width;
let beforePageOffset = 0
const ScrollableTabBar = createReactClass({
propTypes: {
goToPage: PropTypes.func,
activeTab: PropTypes.number,
tabs: PropTypes.array,
backgroundColor: PropTypes.string,
activeTextColor: PropTypes.string,
inactiveTextColor: PropTypes.string,
scrollOffset: PropTypes.number,
style: ViewPropTypes.style,
tabStyle: ViewPropTypes.style,
tabsContainerStyle: ViewPropTypes.style,
textStyle: Text.propTypes.style,
renderTab: PropTypes.func,
underlineStyle: ViewPropTypes.style,
onScroll: PropTypes.func,
},
getDefaultProps() {
return {
scrollOffset: 52,
activeTextColor: 'navy',
inactiveTextColor: 'black',
backgroundColor: null,
style: {},
tabStyle: {},
tabsContainerStyle: {},
underlineStyle: {},
};
},
getInitialState() {
this._tabsMeasurements = [];
return {
_leftTabUnderline: new Animated.Value(0),
_widthTabUnderline: new Animated.Value(0),
_containerWidth: null,
};
},
componentDidMount() {
this.props.scrollValue.addListener(this.updateView);
},
updateView(offset) {
const position = Math.floor(offset.value);
const pageOffset = offset.value % 1;
const tabCount = this.props.tabs.length;
const lastTabPosition = tabCount - 1;
if (tabCount === 0 || offset.value < 0 || offset.value > lastTabPosition) {
return;
}
if (this.necessarilyMeasurementsCompleted(position, position === lastTabPosition)) {
this.updateTabPanel(position, pageOffset);
this.updateTabUnderline(position, pageOffset, tabCount);
}
},
necessarilyMeasurementsCompleted(position, isLastTab) {
return this._tabsMeasurements[position] &&
(isLastTab || this._tabsMeasurements[position + 1]) &&
this._tabContainerMeasurements &&
this._containerMeasurements;
},
updateTabPanel(position, pageOffset) {
const containerWidth = this._containerMeasurements.width;
const tabWidth = this._tabsMeasurements[position].width;
const nextTabMeasurements = this._tabsMeasurements[position + 1];
const nextTabWidth = nextTabMeasurements && nextTabMeasurements.width || 0;
const tabOffset = this._tabsMeasurements[position].left;
const absolutePageOffset = pageOffset * tabWidth;
let newScrollX = tabOffset + absolutePageOffset;
// center tab and smooth tab change (for when tabWidth changes a lot between two tabs)
newScrollX -= (containerWidth - (1 - pageOffset) * tabWidth - pageOffset * nextTabWidth) / 2;
newScrollX = newScrollX >= 0 ? newScrollX : 0;
if (Platform.OS === 'android') {
this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false, });
} else {
const rightBoundScroll = this._tabContainerMeasurements.width - (this._containerMeasurements.width);
newScrollX = newScrollX > rightBoundScroll ? rightBoundScroll : newScrollX;
this._scrollView.scrollTo({x: newScrollX, y: 0, animated: false, });
}
},
updateTabUnderline(position, pageOffset, tabCount) {
const lineLeft = this._tabsMeasurements[position].left;
const lineRight = this._tabsMeasurements[position].right;
if (position < tabCount - 1) {
const nextTabLeft = this._tabsMeasurements[position + 1].left;
const nextTabRight = this._tabsMeasurements[position + 1].right;
const newLineLeft = (pageOffset * nextTabLeft + (1 - pageOffset) * lineLeft);
const newLineRight = (pageOffset * nextTabRight + (1 - pageOffset) * lineRight);
let width = nextTabLeft-lineLeft
let rate = pageOffset/1
let addW = 0
if(width*rate < width*(1-rate)){
addW = width*rate
}else{
addW =width*(1-rate)
}
if(pageOffset onPressHandler(page)}
onLayout={onLayoutHandler}
>
{name}
;
},
measureTab(page, event) {
const { x, width, height, } = event.nativeEvent.layout;
this._tabsMeasurements[page] = {left: x, right: x + width, width, height, };
this.updateView({value: this.props.scrollValue.__getValue(), });
},
render() {
const tabUnderlineStyle = {
position: 'absolute',
height: 4,
backgroundColor: 'navy',
bottom: 0,
};
const dynamicTabUnderline = {
left: this.state._leftTabUnderline,
width: this.state._widthTabUnderline,
};
return
{ this._scrollView = scrollView; }}
horizontal={true}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
directionalLockEnabled={true}
bounces={false}
scrollsToTop={false}
>
{this.props.tabs.map((name, page) => {
const isTabActive = this.props.activeTab === page;
const renderTab = this.props.renderTab || this.renderTab;
return renderTab(name, page, isTabActive, this.props.goToPage, this.measureTab.bind(this, page));
})}
;
},
componentWillReceiveProps(nextProps) {
// If the tabs change, force the width of the tabs container to be recalculated
if (JSON.stringify(this.props.tabs) !== JSON.stringify(nextProps.tabs) && this.state._containerWidth) {
this.setState({ _containerWidth: null, });
}
},
onTabContainerLayout(e) {
this._tabContainerMeasurements = e.nativeEvent.layout;
let width = this._tabContainerMeasurements.width;
if (width < WINDOW_WIDTH) {
width = WINDOW_WIDTH;
}
this.setState({ _containerWidth: width, });
this.updateView({value: this.props.scrollValue.__getValue(), });
},
onContainerLayout(e) {
this._containerMeasurements = e.nativeEvent.layout;
this.updateView({value: this.props.scrollValue.__getValue(), });
},
});
module.exports = ScrollableTabBar;
const styles = StyleSheet.create({
tab: {
height: 49,
alignItems: 'center',
justifyContent: 'center',
paddingLeft: 20,
paddingRight: 20,
},
container: {
height: 50,
borderWidth: 1,
borderTopWidth: 0,
borderLeftWidth: 0,
borderRightWidth: 0,
borderColor: '#ccc',
},
tabs: {
flexDirection: 'row',
justifyContent: 'space-around',
},
});
- 调用方式
import CustomTabBar from '../Component/SongCustomTabBar'
//其它代码省略
}>
{
this.state.catList.map((item, index)=>{
return(
)
})
}