├── .gitignore
├── LICENSE
├── README.md
├── demo.gif
├── index.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # node-waf configuration
20 | .lock-wscript
21 |
22 | # Compiled binary addons (http://nodejs.org/api/addons.html)
23 | build/Release
24 |
25 | # Dependency directory
26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
27 | node_modules
28 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Olivier Lesnicki
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-native-switcher
2 |
3 | A dead simple zoomable-swiper made popular by the Kardashians' apps.
4 |
5 | ## Installation
6 |
7 | ```
8 | npm install react-native-switcher --save
9 | ```
10 |
11 | ## Demo
12 |
13 | 
14 |
15 | ## Usage
16 |
17 | ```js
18 | var Switcher = require('react-native-switcher');
19 | var App = React.createClass({
20 | render() {
21 | return (
22 |
23 |
24 |
25 |
26 |
27 | )
28 | }
29 | })
30 | ```
31 |
--------------------------------------------------------------------------------
/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/olivierlesnicki/react-native-switcher/c88033ec1777f8c2ced01a383fffa3598b2309fe/demo.gif
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | var React = require('react-native');
2 | var {
3 | Animated,
4 | Dimensions,
5 | PanResponder,
6 | ScrollView,
7 | View,
8 | } = React;
9 |
10 | var {
11 | width,
12 | height
13 | } = Dimensions.get('window');
14 |
15 | var Slider = React.createClass({
16 |
17 | getInitialState() {
18 | return {
19 | progress: new Animated.Value(0),
20 | value: 0,
21 | };
22 | },
23 |
24 | componentWillMount() {
25 | this.responder = PanResponder.create({
26 | onMoveShouldSetPanResponder: this.onMoveShouldSetPanResponder,
27 | onPanResponderMove: this.onPanResponderMove,
28 | onPanResponderRelease: this.onPanResponderRelease,
29 | onPanResponderTerminate: this.onPanResponderRelease,
30 | });
31 | },
32 |
33 | onMoveShouldSetPanResponder(e, gestureState) {
34 | return Math.abs(gestureState.dx) > Math.abs(gestureState.dy);
35 | },
36 |
37 | onPanResponderMove(e, gestureState) {
38 |
39 | var dx = gestureState.dx;
40 | var offsetX = dx - this.state.value * this.props.width;
41 | var toValue = -1 * offsetX / this.props.width;
42 |
43 | this.state.progress.setValue(toValue);
44 |
45 | this.props.onMove && this.props.onMove(toValue);
46 |
47 | },
48 |
49 | onPanResponderRelease(e, gestureState) {
50 |
51 | var rx = gestureState.dx / this.props.width,
52 | vx = gestureState.vx,
53 | toValue = this.state.value;
54 |
55 | if (rx < -0.5 || (rx < 0 && vx <=0.5)) {
56 | toValue = toValue + 1;
57 | } else if (rx >= 0.5 || (rx > 0 && vx >=0.5)) {
58 | toValue = toValue - 1;
59 | }
60 |
61 | toValue = Math.max(0, Math.min(toValue, this.props.size - 1));
62 |
63 | this.setState({
64 | value: toValue,
65 | });
66 |
67 | this.props.onRelease && this.props.onRelease(toValue);
68 |
69 | },
70 |
71 | render() {
72 | return (
73 |
82 | {this.props.children}
83 |
84 | );
85 | }
86 |
87 | });
88 |
89 | var Zoomer = React.createClass({
90 |
91 | getInitialState() {
92 | return {
93 | progress: new Animated.Value(0),
94 | value: 0,
95 | };
96 | },
97 |
98 | componentWillMount() {
99 | this.responder = PanResponder.create({
100 | onMoveShouldSetPanResponder: this.onMoveShouldSetPanResponder,
101 | onPanResponderMove: this.onPanResponderMove,
102 | onPanResponderRelease: this.onPanResponderRelease,
103 | onPanResponderTerminate: this.onPanResponderRelease,
104 | });
105 | },
106 |
107 | onMoveShouldSetPanResponder(e, gestureState) {
108 | if (Math.abs(gestureState.dy) > Math.abs(gestureState.dx)) {
109 | if (gestureState.dy > 0 && this.state.value === 0 || this.state.value === 1 && gestureState.dy < 0) {
110 | return true;
111 | }
112 | }
113 | },
114 |
115 | onPanResponderMove(e, gestureState) {
116 |
117 | var dy = gestureState.dy;
118 | var offsetY = dy + this.state.value * (this.props.height / 2);
119 | var toValue = offsetY / (this.props.height / 2);
120 |
121 | this.state.progress.setValue(toValue);
122 |
123 | this.props.onMove && this.props.onMove(toValue);
124 |
125 | },
126 |
127 | onPanResponderRelease(e, gestureState) {
128 |
129 | var ry = gestureState.dy / (this.props.height / 2),
130 | vy = gestureState.vy,
131 | toValue = this.state.value;
132 |
133 | if (ry < -0.5 || (ry < 0 && vy <=0.5)) {
134 | toValue = 0;
135 | } else if (ry >= 0.5 || (ry > 0 && vy >=0.5)) {
136 | toValue = 1;
137 | }
138 |
139 | this.setState({
140 | value: toValue,
141 | });
142 |
143 | this.props.onRelease && this.props.onRelease(toValue);
144 |
145 | },
146 |
147 | render() {
148 | return (
149 |
158 | {this.props.children}
159 |
160 | );
161 | },
162 |
163 | });
164 |
165 | var Switcher = React.createClass({
166 |
167 | getInitialState() {
168 | return {
169 | height: 0,
170 | width: 0,
171 | zoom: new Animated.Value(0),
172 | slide: new Animated.Value(0),
173 | };
174 | },
175 |
176 | onZoomMove(toValue) {
177 | this.state.zoom.setValue(toValue);
178 | },
179 |
180 | onZoomRelease(toValue) {
181 | Animated.spring(this.state.zoom, {
182 | toValue: toValue,
183 | friction: 7,
184 | tension: 70,
185 | }).start();
186 | },
187 |
188 | onSlideMove(toValue) {
189 | this.state.slide.setValue(toValue);
190 | },
191 |
192 | onSlideRelease(toValue) {
193 | Animated.spring(this.state.slide, {
194 | toValue: toValue,
195 | friction: 10,
196 | tension: 50,
197 | }).start();
198 | },
199 |
200 | onLayout(e) {
201 | var {height, width} = e.nativeEvent.layout;
202 | this.setState({height, width});
203 | },
204 |
205 | render() {
206 | return (
207 |
213 |
223 |
233 |
240 | {this.props.children}
241 |
242 |
243 |
244 |
245 | );
246 | },
247 |
248 | });
249 |
250 | module.exports = Switcher;
251 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-switcher",
3 | "version": "0.1.0",
4 | "description": "A dead simple zoomable-swiper made popular by the Kardashians' apps",
5 | "main": "index.js",
6 | "repository": {
7 | "type": "git",
8 | "url": "git+https://github.com/olivierlesnicki/react-native-switcher.git"
9 | },
10 | "author": "",
11 | "license": "MIT",
12 | "bugs": {
13 | "url": "https://github.com/olivierlesnicki/react-native-switcher/issues"
14 | },
15 | "homepage": "https://github.com/olivierlesnicki/react-native-switcher#readme"
16 | }
17 |
--------------------------------------------------------------------------------