React Native 滚动页面置顶
2017-10-27 16:21
453 查看
封装:
实现:
效果如图:
import React, {Component} from 'react'; import PropTypes from 'prop-types'; import {StyleSheet, Animated, View, PanResponder, Dimensions} from 'react-native'; const screen = Dimensions.get("window"); class VerticalSwipe extends Component { _panResponder = null; _movable = null; _initialPositionTop = null; _hasActivatedThreshold = false; _hasMoveAction = false; stylesheets = null; static propTypes: { // Amount of pixel the user can use to swipe the window in swipeOffset: PropTypes.number.isRequired, // Threshold after which window is considered opened when moving openSwipeThreshold: PropTypes.number.isRequired, // Threshold after which window is considered closed when moving closeSwipeThreshold: PropTypes.number.isRequired, // The offset to stop when opening offsetTop: PropTypes.number.isRequired, //内容布局偏移 contentOffsetTop: PropTypes.number, //状态变化通知 onChange: PropTypes.func, //禁止拖动超过初始化swipe content ui高度(跟contentOffsetTop,offsetTop有关) lockContentTopOffset: PropTypes.bool, //拖放阈值 thresholdDrag: PropTypes.number, //Y轴变化通知 onTopChange: PropTypes.func, }; static defaultProps = { swipeOffset: 100, openSwipeThreshold: 100, closeSwipeThreshold: 50, offsetTop: 0, contentOffsetTop: 0, lockContentTopOffset: false, thresholdDrag: 8, }; initialize = () => { this._initialPositionTop = Math.floor(screen.height - this.props.contentOffsetTop - this.props.swipeOffset); let positionTopAnimatedValue = new Animated.Value(this._initialPositionTop); positionTopAnimatedValue.addListener(({value}) => { this.props.onTopChange && this.props.onTopChange(value) }); this.state = { isAnimating: false, isOpen: false, positionTop: positionTopAnimatedValue, }; const styles = { container: { flex: 1, }, swiper: { // We put a transparent background color as a hack because otherwise, moving doesn't work backgroundColor: "rgba(0, 0, 0, 0)", width: "100%", height: screen.height + 75 - this.props.offsetTop, // this.props.swipeOffset, position: "absolute", left: 0, top: this._initialPositionTop, }, innerBottom: { marginTop: this.props.swipeOffset, }, }; this.stylesheets = StyleSheet.create(styles); this.stylesheets.content = this.props.style; }; checkTopValue(top) { let result = top; if (this.props.lockContentTopOffset) { if (top < this.props.offsetTop) { result = this.props.offsetTop; } if (top > this.props.offsetTop && (top > (screen.height - this.props.contentOffsetTop - this.props.swipeOffset))) { result = this._initialPositionTop; } } return result; } onStartShouldSetPanResponder = (evt, gestureState) => { Log('onStartShouldSetPanResponder', gestureState.dy) if (this.state.isOpen) { if ((evt.nativeEvent.pageY < (this.props.openSwipeThreshold + this.props.offsetTop) && evt.nativeEvent.pageY > this.props.offsetTop)) { return false; } return true; } else { } return true; }; onStartShouldSetPanResponderCapture = (evt, gestureState) => { this._hasMoveAction = false; Log('onStartShouldSetPanResponderCapture', gestureState.dy) if (this.state.isAnimating === true) { return false; } if (Math.abs(gestureState.dy) < 5) { // Log('onStartShouldSetPanResponder false') return false; } else { return true; } if (this.state.isOpen) { if ((evt.nativeEvent.pageY < (this.props.openSwipeThreshold + this.props.offsetTop) && evt.nativeEvent.pageY > this.props.offsetTop)) { return false; } } return true; }; onPanResponderMove = (evt, gestureState) => { Log('onPanResponderMove', evt.nativeEvent.pageY, gestureState.dy) if (this.state.isAnimating === true) { return; } if(!this._hasMoveAction){ this._hasMoveAction = true; return ; } // Log(gestureState) let top; if (this.state.isOpen === false) { if (gestureState.dy < -(this.props.openSwipeThreshold) && this._hasActivatedThreshold === false) { this._hasActivatedThreshold = true; } else if (gestureState.dy >= -(this.props.openSwipeThreshold) && this._hasActivatedThreshold === true) { this._hasActivatedThreshold = false; } //增加禁止拖动超过初始化swipe content ui高度 top = screen.height - this.props.contentOffsetTop - this.props.swipeOffset + gestureState.dy; // Log('close state _hasActivatedThreshold',this._hasActivatedThreshold,gestureState.dy,top) top = this.checkTopValue(top); this._movable.setNativeProps({ style: [this.stylesheets.swiper, { top: top }] }); } else { if (gestureState.dy > this.props.closeSwipeThreshold && this._hasActivatedThreshold === false) { this._hasActivatedThreshold = true; } else if (gestureState.dy <= this.props.closeSwipeThreshold && this._hasActivatedThreshold === true) { this._hasActivatedThreshold = false; } top = -this.props.swipeOffset + this.props.offsetTop + gestureState.dy; // Log('open state _hasActivatedThreshold',this._hasActivatedThreshold,gestureState.dy,top); top = this.checkTopValue(top); //增加禁止拖动超过初始化swipe content ui高度 this._movable.setNativeProps({ style: [this.stylesheets.swiper, { top: top }] }); } this.props.onTopChange && this.props.onTopChange(top) }; onPanResponderRelease = (evt, gestureState) => { Log('onPanResponderRelease', gestureState.dy,this._hasMoveAction,this.state.isOpen) if (this._hasMoveAction) { if (this._hasActivatedThreshold) { if (this.state.isOpen === false) { let top = screen.height - this.props.contentOffsetTop - this.props.swipeOffset + gestureState.dy; top = this.checkTopValue(top); this.state.positionTop.setValue(top); this.open(); } else { let top = -this.props.swipeOffset + this.props.offsetTop + gestureState.dy; top = this.checkTopValue(top); this.state.positionTop.setValue(top); this.close(); } } else { let newTop=0; if (this.state.isOpen === false) { newTop=this._initialPositionTop; this._movable.setNativeProps({ style: [this.stylesheets.swiper, { top: newTop, }] }); } else { newTop= -this.props.swipeOffset + this.props.offsetTop; this._movable.setNativeProps({ style: [this.stylesheets.swiper, { top: newTop, }] }); } this.props.onTopChange && this.props.onTopChange(newTop) } } this._hasMoveAction = false; }; open = () => { if (this.state.isAnimating) { return; } this.setState({isAnimating: true, isOpen: true}); Animated.timing( this.state.positionTop, { toValue: -this.props.swipeOffset + this.props.offsetTop, duration: 300, } ).start(() => { if (this.props.onChange) { this.props.onChange(this.state.isOpen) } this.setState({isAnimating: false}); }) }; close = () => { if (this.state.isAnimating) { return; } this.setState({isAnimating: true, isOpen: false}); Animated.timing( this.state.positionTop, { toValue: this._initialPositionTop, duration: 300, } ).start(() => { if (this.props.onChange) { this.props.onChange(this.state.isOpen) } this.setState({isAnimating: false}); }) }; getMovableStyle = () => { return [this.stylesheets.swiper, { top: this.state.positionTop, }] }; constructor(props) { super(props); this.initialize(); } componentWillMount() { this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: this.onStartShouldSetPanResponder, onStartShouldSetPanResponderCapture: this.onStartShouldSetPanResponderCapture, onMoveShouldSetPanResponder: this.onStartShouldSetPanResponder, onMoveShouldSetPanResponderCapture: this.onStartShouldSetPanResponder, onPanResponderMove: this.onPanResponderMove, onPanResponderRelease: this.onPanResponderRelease, onPanResponderTerminate: this.onPanResponderRelease }) } render() { return ( <View style={this.stylesheets.container}> <View style={this.stylesheets.content}> {this.props.children} </View> <Animated.View style={this.getMovableStyle()} ref={(ref) => this._movable = ref} {...this._panResponder.panHandlers}> <View style={this.stylesheets.innerBottom}> {this.props.content} </View> </Animated.View> </View> ); } } export default VerticalSwipe;
实现:
import React, {Component} from 'react'; import { StyleSheet, View, TouchableOpacity, Alert, ScrollView, ART, TouchableHighlight, ListView, Dimensions, Text, Image } from 'react-native'; const {width, height} = Dimensions.get('window'); import VerticalSwipe from './VerticalSwipe' const TabState = [{title: '已发布'}, {title: '审核中'}, {title: '审核不通过'}]; export default class extends React.Component { constructor(props) { super(props); this.state = { topOffset: ContentTopOffset, }; } render() { return ( <View style={styles.container} > <VerticalSwipe swipeOffset={0} lockContentTopOffset closeSwipeThreshold={50} openSwipeThreshold={50} contentOffsetTop={ContentTopOffset} offsetTop={64} style={styles.dragContainer} onTopChange={(top) => { this.setState({ topOffset: top, }) }} onChange={(isOpen) => { }} content={( <View style={styles.innerContainer}> <View style={{flexDirection: 'row', height: TabHeight}}> { TabState.map((item, index) => { return this.renderTab(index) }) } </View> <View style={{backgroundColor:'pink', height:height, width: width}} > </View> </View> )}> {this.renderTopView()} </VerticalSwipe> </View> ); } renderTab(index) { return ( <TouchableOpacity style={{flex: 1, height: TabHeight}} onPress={() => {}}> <View style={{flex: 1, justifyContent: 'center', alignItems: "center"}}> <Text style={[styles.tabTxtStyle, {marginTop: 3}]}>0</Text> <Text style={[styles.tabTxtStyle, {marginTop: 3}]}>{TabState[index].title}</Text> </View> </TouchableOpacity> ) } renderTopView = () => { let maxOffsetTop = height - ContentTopOffset; let offsetPercent = ((this.state.topOffset - 64) / (maxOffsetTop - 64)); if(offsetPercent<0.3){ offsetPercent=0; } return ( <View style={{opacity: offsetPercent,width: width, height:TopHeight}}> <Image style={{width: width, height:TopHeight, alignItems:'center', justifyContent:'center'}} source={require('../img/cellar/bg_cellar_top@2x.png')}> <Image style={{width:60, height:60, borderRadius:30}} source={require('../img/mine/ic_default@2x.png')}/> </Image> </View> ) } }; const TabHeight = 56; const TopHeight = 268; const ContentTopOffset = height - TopHeight + TabHeight; const styles = StyleSheet.create({ container: { backgroundColor:'#350400', flex: 1, }, tabTxtStyle: { textAlign: 'center', color: '#c9a88d' }, tabHeightLineStyle: { width: 75, height: 1, backgroundColor: '#c9a88d', marginTop: 5 }, });
效果如图:
相关文章推荐
- react native 使webview里面的页面自动滚动
- react_native 项目实战 (3) 使用导航页面跳转 (ReactNaviation 完全自定义导航)
- react native scrollview深入详解触摸滚动事件
- 原生Android项目中集成React native页面
- react-native 页面切换的实现
- 从零学React Native之03页面导航
- [置顶] react-native-baidu-map在react-native中的使用
- React_Native 项目实战 (1) (首页,以及页面的切换)
- 《ReactNative实战讲义》react-native-router-flux框架篇---刷新当前页面的属性
- react-native最新的ES6基于页面跳转和传值
- [置顶] Android布局加载React Native视图
- react-native编辑的跑马等抽奖页面
- react-native 部分手机二维码扫面页面黑屏的问题(不能使用相机)
- 从零学React Native之03页面导航
- React Native之使用导航器跳转页面(react-navigation)
- React Native学习笔记之--向原生应用中集成RN页面
- [置顶] 关于React Native提示has been register的解决方法
- ReactJS 监听页面滚动事件
- react native 不同页面之间传参 传值 __listview列表跳转详情页 带参数 传值
- React-Native中navigator.pop()后如何更新前一个页面