React Native 滚动页面置顶

来源:互联网 发布:腾讯网络加速器独立版 编辑:程序博客网 时间:2024/06/14 20:00

封装:

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

效果如图:
这里写图片描述

原创粉丝点击