├── .gitignore ├── .npmignore ├── README.md ├── Swiper.js ├── package.json └── src └── Swiper.jsx /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .module-cache/ 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .module-cache/ 2 | src/ 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | react-swiper 2 | ================ 3 | 4 | > Detects and triggers touch events for swiping such as onSwipeLeft, onSwipeDown, etc. with ReactJS 5 | 6 | ## Installation 7 | 8 | ```bash 9 | npm install --save react react-swiper 10 | ``` 11 | 12 | ## Usage 13 | 14 | ### Example with defaults 15 | 16 | Creating an example component: 17 | 18 | ```javascript 19 | var React = require('react'); 20 | var Swiper = require('react-swiper'); 21 | 22 | React.initializeTouchEvents(true); 23 | 24 | var Example = React.createClass({ 25 | 26 | render: function() { 27 | return ( 28 | 29 | Hello world! 30 | 31 | ); 32 | }, 33 | 34 | handleLeftSwipe: function (e) { 35 | console.log(e); 36 | } 37 | 38 | }); 39 | 40 | module.exports = Example; 41 | ``` 42 | 43 | The `Swiper` component will render a `
` element by default, this can be changed either by providing the `tagName` property or the `component` property. 44 | 45 | ### Example with custom element 46 | 47 | Creating a Swiper link (i.e. a swipeable `` element): 48 | 49 | ```javascript 50 | var React = require('react'); 51 | var Swiper = require('react-swiper'); 52 | 53 | React.initializeTouchEvents(true); 54 | 55 | var Example = React.createClass({ 56 | 57 | render: function() { 58 | return ( 59 | 60 | Swipe or click me... 61 | 62 | ); 63 | }, 64 | 65 | handleSwipe: function (e) { 66 | console.log(e); 67 | } 68 | 69 | }); 70 | 71 | module.exports = Example; 72 | ``` 73 | 74 | ### Example with custom component 75 | 76 | Creating a Swiper from another component: 77 | 78 | ```javascript 79 | var React = require('react'); 80 | var Swiper = require('react-swiper'); 81 | var MyComponent = require('./my-component'); 82 | 83 | React.initializeTouchEvents(true); 84 | 85 | var Example = React.createClass({ 86 | 87 | render: function() { 88 | return ( 89 | 90 | ); 91 | }, 92 | 93 | handleSwipe: function (e) { 94 | console.log(e); 95 | } 96 | 97 | }); 98 | 99 | module.exports = Example; 100 | ``` 101 | 102 | ## Properties 103 | 104 | ### `tagName` 105 | 106 | **Type** `String` 107 | 108 | **Default** `"div"` 109 | 110 | 111 | Specifies what type of element the `Swiper` component should be rendered as. See `component` below as well. 112 | 113 | ### `component` 114 | 115 | **Type** `ReactComponent` 116 | 117 | **Default** `undefined` 118 | 119 | 120 | Specifies what component `Swiper` should be rendered as. See `tagName` above as well. If both `tagName` and `component` are specified the later takes precedence. 121 | 122 | ### `onSwipe` 123 | 124 | **Type** `Function(event)` 125 | 126 | **Default** `undefined` 127 | 128 | 129 | If provided it's called on all swipes. 130 | 131 | Example `event`: 132 | 133 | ```javascript 134 | { 135 | type: String, // The type of swipe, e.g. "swipeLeft", "swipeUp" or "swipeDownRight" 136 | timeStampStart: Date, // Timestamp for when the swipe was initiated 137 | timeStampEnd: Date, // Timestamp for when the swipe was finished, 138 | initialTouch: Touch, // A Touch object for the initial touch position - https://developer.mozilla.org/en-US/docs/Web/API/Touch 139 | finalTouch: Touch, // A Touch object for the final touch position 140 | } 141 | ``` 142 | 143 | ### `onSwipe` 144 | 145 | **Direction** `Up`, `UpRight`, `Right`, `DownRight`, `Down`, `DownLeft`, `Left` and `UpLeft` 146 | 147 | **Type** `Function(event)` 148 | 149 | **Default** `undefined` 150 | 151 | 152 | If provided it's called with a swipe event (see example in `onSwipe` above) for a swipe in the wanted direction. 153 | E.g. `onSwipeUp` is called for swipes in the up direction. 154 | 155 | ## `minSwipeLength` 156 | 157 | **Type** `Number` 158 | 159 | **Default** `75` 160 | 161 | 162 | The minimum swipe length that's required for a swipe event to be triggered. 163 | 164 | ## `moveThreshold` 165 | 166 | **Type** `Number` 167 | 168 | **Default** `10` 169 | 170 | 171 | The minimum move length in one direction to be considered as the swipe direction, this also affects the required velocity in which the swipe must occur. 172 | 173 | 174 | ## License 175 | 176 | MIT 177 | -------------------------------------------------------------------------------- /Swiper.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @jsx React.DOM 3 | */ 4 | 5 | var React = require('react'), 6 | objectAssign = require('object-assign'); 7 | 8 | var Swiper = React.createClass({displayName: 'Swiper', 9 | propTypes: { 10 | tagName: React.PropTypes.string, 11 | component: React.PropTypes.element, 12 | minSwipeLength: React.PropTypes.number, 13 | moveThreshold: React.PropTypes.number, 14 | onSwipe: React.PropTypes.func, 15 | onSwipeLeft: React.PropTypes.func, 16 | onSwipeUpLeft: React.PropTypes.func, 17 | onSwipeUp: React.PropTypes.func, 18 | onSwipeUpRight: React.PropTypes.func, 19 | onSwipeRight: React.PropTypes.func, 20 | onSwipeDownRight: React.PropTypes.func, 21 | onSwipeDown: React.PropTypes.func, 22 | onSwipeDownLeft: React.PropTypes.func 23 | }, 24 | 25 | getDefaultProps: function() { 26 | return { 27 | tagName: 'div', 28 | minSwipeLength: 75, 29 | moveThreshold: 10 30 | }; 31 | }, 32 | 33 | getInitialState: function () { 34 | return { 35 | direction: null, 36 | initialTouch: null, 37 | touch: null, 38 | swipeStart: null 39 | }; 40 | }, 41 | 42 | render: function() { 43 | var Component = this.props.component || this.props.tagName; 44 | return ( 45 | Component(Object.assign({}, this.props, {onTouchStart: this.handleTouchStart, 46 | onTouchEnd: this.handleTouchEnd, 47 | onTouchCancel: this.handleTouchEnd, 48 | onTouchMove: this.handleTouchMove}), 49 | this.props.children 50 | ) 51 | ); 52 | }, 53 | 54 | handleTouchStart: function (e) { 55 | if (e.touches.length !== 1) { 56 | return; 57 | } 58 | this._initiateSwipe(e.touches[0]); 59 | }, 60 | 61 | handleTouchEnd: function (e) { 62 | if (!this.state.direction) { 63 | return; 64 | } 65 | if (this._getSwipeLength(this.state.initialTouch) > this.props.minSwipeLength) { 66 | var method = this._getEventMethodName(); 67 | var evt = { 68 | type: this._getEventTypeName(), 69 | timeStampStart: this.state.swipeStart, 70 | timeStampEnd: new Date(), 71 | initialTouch: this.state.initialTouch, 72 | finalTouch: this.state.touch 73 | }; 74 | this.props.onSwipe && this.props.onSwipe(evt); 75 | this.props[method] && this.props[method](evt); 76 | e.preventDefault(); 77 | } 78 | this._resetSwipe(); 79 | }, 80 | 81 | handleTouchMove: function (e) { 82 | if (e.touches.length !== 1 || !this.state.direction) { 83 | return; 84 | } 85 | var touch = e.touches[0]; 86 | var direction = this._getSwipeDirection(touch); 87 | if (this._isSwipeDirectionUnchanged(direction)) { 88 | this._updateSwipe(direction, touch); 89 | e.preventDefault(); 90 | return; 91 | } 92 | this._resetSwipe(); 93 | }, 94 | 95 | _initiateSwipe: function (touch) { 96 | this.setState({direction: {x: null, y: null}, initialTouch: touch, touch: touch, swipeStart: new Date()}); 97 | }, 98 | 99 | _resetSwipe: function () { 100 | this.setState(this.getInitialState()); 101 | }, 102 | 103 | _updateSwipe: function (direction, touch) { 104 | this.setState({direction:direction, touch:touch}); 105 | }, 106 | 107 | _getSwipeLength: function (touch) { 108 | return this._getSwipeLengthX(touch) + this._getSwipeLengthY(touch); 109 | }, 110 | 111 | _getSwipeLengthX: function (touch) { 112 | return Math.abs(touch.pageX - this.state.touch.pageX); 113 | }, 114 | 115 | _getSwipeLengthY: function (touch) { 116 | return Math.abs(touch.pageY - this.state.touch.pageY); 117 | }, 118 | 119 | _getSwipeDirection: function (touch) { 120 | var dir = objectAssign({x: null, y: null}, this.state.direction); 121 | if (this._getSwipeLengthY(touch) > this.props.moveThreshold) { 122 | dir.y = this._getSwipeDirectionY(touch); 123 | } 124 | if (this._getSwipeLengthX(touch) > this.props.moveThreshold) { 125 | dir.x = this._getSwipeDirectionX(touch); 126 | } 127 | return dir; 128 | }, 129 | 130 | _getSwipeDirectionX: function (touch) { 131 | return touch.pageX < this.state.touch.pageX ? 'Left' : 'Right'; 132 | }, 133 | 134 | _getSwipeDirectionY: function (touch) { 135 | return touch.pageY < this.state.touch.pageY ? 'Up' : 'Down'; 136 | }, 137 | 138 | _getSwipeDirectionName: function () { 139 | return (this.state.direction.y || '') + (this.state.direction.x || ''); 140 | }, 141 | 142 | _isSwipeDirectionUnchanged: function (direction) { 143 | return (!this.state.direction.x || this.state.direction.x === direction.x) && 144 | (!this.state.direction.y || this.state.direction.y === direction.y); 145 | }, 146 | 147 | _getEventMethodName: function () { 148 | return 'onSwipe' + this._getSwipeDirectionName(); 149 | }, 150 | 151 | _getEventTypeName: function () { 152 | return 'swipe' + this._getSwipeDirectionName(); 153 | } 154 | }); 155 | 156 | module.exports = Swiper; 157 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-swiper", 3 | "version": "0.1.5", 4 | "description": "Detects and triggers touch events for swiping such as onSwipeLeft, onSwipeDown, etc. with ReactJS", 5 | "main": "Swiper.js", 6 | "scripts": { 7 | "build": "jsx --harmony -x jsx src/ ./" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/joakimbeng/react-swiper" 12 | }, 13 | "keywords": [ 14 | "react", 15 | "swipe", 16 | "touch", 17 | "event" 18 | ], 19 | "author": "Joakim Carlstein ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/joakimbeng/react-swiper/issues" 23 | }, 24 | "homepage": "https://github.com/joakimbeng/react-swiper", 25 | "devDependencies": { 26 | "react-tools": "~0.11.2" 27 | }, 28 | "dependencies": { 29 | "object-assign": "^2.0.0" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Swiper.jsx: -------------------------------------------------------------------------------- 1 | /** 2 | * @jsx React.DOM 3 | */ 4 | 5 | var React = require('react'), 6 | objectAssign = require('object-assign'); 7 | 8 | var Swiper = React.createClass({ 9 | propTypes: { 10 | tagName: React.PropTypes.string, 11 | component: React.PropTypes.element, 12 | minSwipeLength: React.PropTypes.number, 13 | moveThreshold: React.PropTypes.number, 14 | onSwipe: React.PropTypes.func, 15 | onSwipeLeft: React.PropTypes.func, 16 | onSwipeUpLeft: React.PropTypes.func, 17 | onSwipeUp: React.PropTypes.func, 18 | onSwipeUpRight: React.PropTypes.func, 19 | onSwipeRight: React.PropTypes.func, 20 | onSwipeDownRight: React.PropTypes.func, 21 | onSwipeDown: React.PropTypes.func, 22 | onSwipeDownLeft: React.PropTypes.func 23 | }, 24 | 25 | getDefaultProps: function() { 26 | return { 27 | tagName: 'div', 28 | minSwipeLength: 75, 29 | moveThreshold: 10 30 | }; 31 | }, 32 | 33 | getInitialState: function () { 34 | return { 35 | direction: null, 36 | initialTouch: null, 37 | touch: null, 38 | swipeStart: null 39 | }; 40 | }, 41 | 42 | render: function() { 43 | var Component = this.props.component || this.props.tagName; 44 | return ( 45 | 49 | {this.props.children} 50 | 51 | ); 52 | }, 53 | 54 | handleTouchStart: function (e) { 55 | if (e.touches.length !== 1) { 56 | return; 57 | } 58 | this._initiateSwipe(e.touches[0]); 59 | }, 60 | 61 | handleTouchEnd: function (e) { 62 | if (!this.state.direction) { 63 | return; 64 | } 65 | if (this._getSwipeLength(this.state.initialTouch) > this.props.minSwipeLength) { 66 | var method = this._getEventMethodName(); 67 | var evt = { 68 | type: this._getEventTypeName(), 69 | timeStampStart: this.state.swipeStart, 70 | timeStampEnd: new Date(), 71 | initialTouch: this.state.initialTouch, 72 | finalTouch: this.state.touch 73 | }; 74 | this.props.onSwipe && this.props.onSwipe(evt); 75 | this.props[method] && this.props[method](evt); 76 | e.preventDefault(); 77 | } 78 | this._resetSwipe(); 79 | }, 80 | 81 | handleTouchMove: function (e) { 82 | if (e.touches.length !== 1 || !this.state.direction) { 83 | return; 84 | } 85 | var touch = e.touches[0]; 86 | var direction = this._getSwipeDirection(touch); 87 | if (this._isSwipeDirectionUnchanged(direction)) { 88 | this._updateSwipe(direction, touch); 89 | e.preventDefault(); 90 | return; 91 | } 92 | this._resetSwipe(); 93 | }, 94 | 95 | _initiateSwipe: function (touch) { 96 | this.setState({direction: {x: null, y: null}, initialTouch: touch, touch: touch, swipeStart: new Date()}); 97 | }, 98 | 99 | _resetSwipe: function () { 100 | this.setState(this.getInitialState()); 101 | }, 102 | 103 | _updateSwipe: function (direction, touch) { 104 | this.setState({direction, touch}); 105 | }, 106 | 107 | _getSwipeLength: function (touch) { 108 | return this._getSwipeLengthX(touch) + this._getSwipeLengthY(touch); 109 | }, 110 | 111 | _getSwipeLengthX: function (touch) { 112 | return Math.abs(touch.pageX - this.state.touch.pageX); 113 | }, 114 | 115 | _getSwipeLengthY: function (touch) { 116 | return Math.abs(touch.pageY - this.state.touch.pageY); 117 | }, 118 | 119 | _getSwipeDirection: function (touch) { 120 | var dir = objectAssign({x: null, y: null}, this.state.direction); 121 | if (this._getSwipeLengthY(touch) > this.props.moveThreshold) { 122 | dir.y = this._getSwipeDirectionY(touch); 123 | } 124 | if (this._getSwipeLengthX(touch) > this.props.moveThreshold) { 125 | dir.x = this._getSwipeDirectionX(touch); 126 | } 127 | return dir; 128 | }, 129 | 130 | _getSwipeDirectionX: function (touch) { 131 | return touch.pageX < this.state.touch.pageX ? 'Left' : 'Right'; 132 | }, 133 | 134 | _getSwipeDirectionY: function (touch) { 135 | return touch.pageY < this.state.touch.pageY ? 'Up' : 'Down'; 136 | }, 137 | 138 | _getSwipeDirectionName: function () { 139 | return (this.state.direction.y || '') + (this.state.direction.x || ''); 140 | }, 141 | 142 | _isSwipeDirectionUnchanged: function (direction) { 143 | return (!this.state.direction.x || this.state.direction.x === direction.x) && 144 | (!this.state.direction.y || this.state.direction.y === direction.y); 145 | }, 146 | 147 | _getEventMethodName: function () { 148 | return 'onSwipe' + this._getSwipeDirectionName(); 149 | }, 150 | 151 | _getEventTypeName: function () { 152 | return 'swipe' + this._getSwipeDirectionName(); 153 | } 154 | }); 155 | 156 | module.exports = Swiper; 157 | --------------------------------------------------------------------------------