├── README.md ├── helper.js ├── index.js └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # react-native-bottom-drawer 2 | 3 | 4 | ## Install 5 | ```bat 6 | npm install react-native-bottom-drawer --save 7 | ``` 8 | 9 | ## Demo 10 | ![](https://media.giphy.com/media/xUA7bcy8vCb27N67N6/giphy.gif) 11 | 12 | ## Basic example 13 | ``` 14 | } 18 | renderHandle={()=>
} 19 | renderDrawerView={()=>( 20 | 21 | 22 | 23 | 24 | {data} 25 | 26 | } /> 27 | 28 | )} 29 | /> 30 | 31 | ``` 32 | 33 | Based on [react-native-draggable-drawer](https://www.npmjs.com/package/react-native-draggable-drawer) 34 | -------------------------------------------------------------------------------- /helper.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | var React = require('react-native'); 3 | 4 | var { 5 | Animated, 6 | Easing 7 | } = React; 8 | 9 | 10 | module.exports = function (screen_height ) { 11 | var module = {}; 12 | var initialUsedSpace; 13 | var tension; 14 | var friction; 15 | var initialPosition; 16 | var callbackPositionUpdated; 17 | 18 | module.calculateInitialPosition = function ( initial_used_space ){ 19 | initialUsedSpace = Math.abs(initial_used_space); 20 | initialPosition = (screen_height * ( 1 - initialUsedSpace )); 21 | return initialPosition; 22 | }; 23 | 24 | module.getInitialUsedSpace = function(){ 25 | return initialUsedSpace; 26 | }; 27 | 28 | module.getInitialPosition = function(){ 29 | return initialPosition; 30 | }; 31 | 32 | 33 | module.setupAnimation = function ( higher_tension, friction, callbackPositionUpdated ){ 34 | this.tension = higher_tension; 35 | this.friction = friction; 36 | this.callbackPositionUpdated = callbackPositionUpdated; 37 | 38 | }; 39 | 40 | 41 | module.isAValidMovement = function(distanceX, distanceY){ 42 | var moveTravelledFarEnough = Math.abs(distanceY) > Math.abs(distanceX) && Math.abs(distanceY) > 2; 43 | return moveTravelledFarEnough; 44 | }; 45 | 46 | 47 | module.startAnimation = function (velocityY, positionY,initialPositon,id ){ 48 | console.log('creating animation '); 49 | var isGoingToUp = ( velocityY < 0 )? true : false; 50 | var speed = Math.abs(velocityY); 51 | var currentPosition = Math.abs(positionY / screen_height); 52 | var endPosition = isGoingToUp? 0 : initialPositon; 53 | 54 | var position = new Animated.Value(positionY); 55 | position.removeAllListeners(); 56 | 57 | console.log('configuration : '+endPosition) 58 | 59 | Animated.timing(position, { 60 | toValue: endPosition, 61 | tension: 30, 62 | friction: 1, 63 | //easing:Easing.elastic, 64 | velocity: velocityY 65 | }).start(); 66 | 67 | position.addListener((position)=>{console.log('position by',position,endPosition);}); 68 | position.addListener(this.callbackPositionUpdated); 69 | }; 70 | 71 | return module; 72 | }; 73 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var TENSION = 800; 2 | var FRICTION = 90; 3 | 4 | import React, { Component } from 'react' 5 | import { 6 | AppRegistry, 7 | StyleSheet, 8 | TouchableWithoutFeedback, 9 | Text, 10 | Image, 11 | View, 12 | Animated, 13 | AlertIOS, 14 | PanResponder, 15 | Dimensions 16 | } from 'react-native'; 17 | 18 | var SCREEN_HEIGHT = Dimensions.get('window').height; 19 | var DraggableDrawerHelper = require('./helper')(SCREEN_HEIGHT); 20 | 21 | var component = React.createClass({ 22 | getInitialState: function() { 23 | // naming it initialX clearly indicates that the only purpose 24 | // of the passed down prop is to initialize something internally 25 | var initialDrawerSize = DraggableDrawerHelper.calculateInitialPosition(this.props.initialDrawerSize); 26 | console.log(initialDrawerSize,'Initila size'); 27 | return { 28 | touched:'FALSE', 29 | position: new Animated.Value(initialDrawerSize), 30 | initialPositon:initialDrawerSize 31 | }; 32 | }, 33 | 34 | onUpdatePosition: function (position){ 35 | this.state.position.setValue(position); 36 | this._previousTop = position; 37 | console.log('Position ',position); 38 | var initialPosition = DraggableDrawerHelper.getInitialPosition(); 39 | 40 | if(initialPosition === position){ 41 | this.props.onInitialPositionReached && this.props.onInitialPositionReached(); 42 | } 43 | }, 44 | 45 | componentWillMount: function() { 46 | // Initialize the DraggableDrawerHelper that will drive animations 47 | DraggableDrawerHelper.setupAnimation(TENSION,FRICTION, 48 | (position) => { 49 | if (!this.center) return; 50 | this.onUpdatePosition(position.value); 51 | } 52 | ); 53 | 54 | this._panGesture = PanResponder.create({ 55 | onMoveShouldSetPanResponder: (evt, gestureState) => { 56 | return DraggableDrawerHelper.isAValidMovement(gestureState.dx,gestureState.dy) && this.state.touched=='TRUE'; 57 | }, 58 | onPanResponderMove: (evt, gestureState) => { 59 | this.moveDrawerView(gestureState) 60 | }, 61 | onPanResponderRelease: (evt, gestureState) => { 62 | this.moveFinished(gestureState) 63 | }, 64 | }) 65 | }, 66 | 67 | 68 | moveDrawerView: function(gestureState) { 69 | console.log(gestureState.vy,'GESTURE'); 70 | if (!this.center) return; 71 | var currentValue = Math.abs(gestureState.moveY / SCREEN_HEIGHT); 72 | var isGoingToUp = ( gestureState.vy < 0 ); 73 | //Here, I'm subtracting %5 of screen size from edge drawer position to be closer as possible to finger location when dragging the drawer view 74 | var position = gestureState.moveY - SCREEN_HEIGHT * 0.05; 75 | //Send to callback function the current drawer position when drag down the drawer view component 76 | if(!isGoingToUp) this.props.onDragDown(1-currentValue); 77 | this.onUpdatePosition(position); 78 | }, 79 | 80 | moveFinished: function(gestureState) { 81 | var isGoingToUp = ( gestureState.vy < 0 ); 82 | if (!this.center) return; 83 | DraggableDrawerHelper.startAnimation(gestureState.vy,gestureState.moveY,this.state.initialPositon,gestureState.stateId); 84 | this.props.onRelease && this.props.onRelease(isGoingToUp); 85 | }, 86 | 87 | render: function() { 88 | var containerView = this.props.renderContainerView ? this.props.renderContainerView() : null; 89 | var drawerView = this.props.renderDrawerView ? this.props.renderDrawerView() : null; 90 | 91 | var drawerPosition = { 92 | top: this.state.position 93 | }; 94 | 95 | return ( 96 | 97 | this.center = center} 100 | {...this._panGesture.panHandlers}> 101 | {this.setState({touched:'TRUE'})}} 103 | onPressOut={()=>{this.setState({touched:'FALSE'})}}> 104 | 105 | 106 | 107 | 108 | {drawerView} 109 | 110 | 111 | ) 112 | }, 113 | }); 114 | 115 | 116 | var styles = StyleSheet.create({ 117 | viewport: { 118 | flex: 1, 119 | }, 120 | drawer: { 121 | flex:1, 122 | }, 123 | container: { 124 | position: 'absolute', 125 | top:0, 126 | left:0, 127 | bottom: 0, 128 | right: 0, 129 | }, 130 | }); 131 | 132 | module.exports = component; 133 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-bottom-drawer", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC" 11 | } 12 | --------------------------------------------------------------------------------