├── .DS_Store ├── .babelrc ├── .flowconfig ├── .gitignore ├── .watchmanconfig ├── App.js ├── App.test.js ├── README.md ├── app.json ├── package.json ├── src ├── .DS_Store ├── Example │ ├── Animals.js │ ├── Router.js │ └── SocialTheme.js ├── assets │ ├── .DS_Store │ └── husna.gif └── components │ └── HelixScroll.js └── yarn.lock /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasbir23/HelixScrollView-React-Native/fccf082c5be7cb335735df875566a715d5cedba6/.DS_Store -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["babel-preset-expo"], 3 | "env": { 4 | "development": { 5 | "plugins": ["transform-react-jsx-source"] 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | .*/Libraries/react-native/ReactNative.js 16 | 17 | ; Additional create-react-native-app ignores 18 | 19 | ; Ignore duplicate module providers 20 | .*/node_modules/fbemitter/lib/* 21 | 22 | ; Ignore misbehaving dev-dependencies 23 | .*/node_modules/xdl/build/* 24 | .*/node_modules/reqwest/tests/* 25 | 26 | ; Ignore missing expo-sdk dependencies (temporarily) 27 | ; https://github.com/expo/expo/issues/162 28 | .*/node_modules/expo/src/* 29 | 30 | ; Ignore react-native-fbads dependency of the expo sdk 31 | .*/node_modules/react-native-fbads/* 32 | 33 | [include] 34 | 35 | [libs] 36 | node_modules/react-native/Libraries/react-native/react-native-interface.js 37 | node_modules/react-native/flow 38 | flow/ 39 | 40 | [options] 41 | module.system=haste 42 | 43 | emoji=true 44 | 45 | experimental.strict_type_args=true 46 | 47 | munge_underscores=true 48 | 49 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 50 | 51 | suppress_type=$FlowIssue 52 | suppress_type=$FlowFixMe 53 | suppress_type=$FixMe 54 | 55 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(4[0-9]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 56 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(4[0-9]\\|[1-3][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 57 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 58 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 59 | 60 | unsafe.enable_getters_and_setters=true 61 | 62 | [version] 63 | ^0.49.1 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .expo/ 3 | npm-debug.* 4 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} 2 | -------------------------------------------------------------------------------- /App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { StyleSheet, Text, View } from "react-native"; 3 | import Animals from "./src/Example/Animals"; 4 | import SocialTheme from "./src/Example/SocialTheme"; 5 | import Router from "./src/Example/Router"; 6 | 7 | import Expo from "expo"; 8 | 9 | export default class App extends React.Component { 10 | constructor() { 11 | super(); 12 | this.state = { 13 | isReady: false 14 | }; 15 | } 16 | async componentWillMount() { 17 | await Expo.Font.loadAsync({ 18 | Roboto: require("native-base/Fonts/Roboto.ttf"), 19 | Roboto_medium: require("native-base/Fonts/Roboto_medium.ttf"), 20 | Ionicons: require("native-base/Fonts/Ionicons.ttf") 21 | }); 22 | this.setState({ isReady: true }); 23 | } 24 | render() { 25 | if (!this.state.isReady) { 26 | return ; 27 | } 28 | return ; 29 | } 30 | } 31 | 32 | const styles = StyleSheet.create({ 33 | container: { 34 | flex: 1, 35 | backgroundColor: "#fff", 36 | alignItems: "center", 37 | justifyContent: "center" 38 | } 39 | }); 40 | -------------------------------------------------------------------------------- /App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import App from './App'; 3 | 4 | import renderer from 'react-test-renderer'; 5 | 6 | it('renders without crashing', () => { 7 | const rendered = renderer.create().toJSON(); 8 | expect(rendered).toBeTruthy(); 9 | }); 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Husna 2 | ![HelixScrollView](https://raw.githubusercontent.com/Jasbir23/HelixScrollView-React-Native/master/src/assets/husna.gif)
3 | 4 | ## Helical-ScrollView-React-Native(iOS + Android) 5 | ## Props 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 |
NameisRequiredTypeDescription
itemCountYESIntegerNo of card components to be rendered
renderCardYESFunction callbackRender card component callback. Carries index of card.
backgroundImageNOStringURI of background image of content container
contentContainerStyleNOstyleTo add style to the content container, **along with other ScrollView props**
43 | 44 | ## Usage 45 | Find detailed use case in **Examples** section
46 | ``` 47 | render(){ 48 | return( 49 | { 52 | return( 53 | // return your custom component here 54 | ) 55 | }} /> 56 | ); 57 | } 58 | ``` 59 | 60 | ## Check out [Sample Expo App](https://expo.io/@jaezzy23/husna) 61 | -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo": { 3 | "name": "Husna", 4 | "version": "1.0.0", 5 | "sdkVersion": "20.0.0", 6 | "android": { 7 | "package": "com.geekyants.Husna", 8 | }, 9 | "ios": { 10 | "bundleIdentifier": "com.geekyants.Husna", 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Helix", 3 | "version": "0.1.0", 4 | "private": true, 5 | "devDependencies": { 6 | "react-native-scripts": "1.3.1", 7 | "jest-expo": "~20.0.0", 8 | "react-test-renderer": "16.0.0-alpha.12" 9 | }, 10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js", 11 | "scripts": { 12 | "start": "react-native-scripts start", 13 | "eject": "react-native-scripts eject", 14 | "android": "react-native-scripts android", 15 | "ios": "react-native-scripts ios", 16 | "test": "node node_modules/jest/bin/jest.js --watch" 17 | }, 18 | "jest": { 19 | "preset": "jest-expo" 20 | }, 21 | "dependencies": { 22 | "expo": "^20.0.0", 23 | "native-base": "^2.3.1", 24 | "react": "16.0.0-alpha.12", 25 | "react-native": "^0.47.0", 26 | "react-navigation": "^1.0.0-beta.11" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasbir23/HelixScrollView-React-Native/fccf082c5be7cb335735df875566a715d5cedba6/src/.DS_Store -------------------------------------------------------------------------------- /src/Example/Animals.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import HelixScroll from "../components/HelixScroll"; 3 | import { 4 | View, 5 | Image, 6 | TouchableOpacity, 7 | Text, 8 | Dimensions, 9 | StatusBar 10 | } from "react-native"; 11 | import { Button } from "native-base"; 12 | const { width, height } = Dimensions.get("window"); 13 | export default class Animals extends Component { 14 | static navigationOptions = ({ navigation }) => ({ 15 | header: null 16 | }); 17 | constructor(props) { 18 | super(props); 19 | this.state = { 20 | dataArray: [ 21 | { 22 | name: "Sea Lions", 23 | url: "http://www.nationalgeographic.com/content/dam/photography/PROOF/2017/July/slowest-animals-gallery/01-slowest-animals-2852968_uploadsmember256581yourshot-256581-2852968jpg_jaup46uf5pq4jv7mlnrdgxwnh7p3eflutfvvbpyjwjhzlmh4iziq_1600x1060.jpg" 24 | }, 25 | { 26 | name: "Owl", 27 | url: "https://static.boredpanda.com/blog/wp-content/uploads/2014/10/animals-in-autumn-211__880.jpg" 28 | }, 29 | { 30 | name: "Tiger", 31 | url: "https://ak5.picdn.net/shutterstock/videos/7646350/thumb/1.jpg" 32 | }, 33 | { 34 | name: "Hero fennec", 35 | url: "http://animals.sandiegozoo.org/sites/default/files/2016-10/animals_hero_fennec2.jpg" 36 | }, 37 | { 38 | name: "Squirrel", 39 | url: "https://www.wpcc.org.uk/images/nature/mammals/header-squirrel.jpg" 40 | }, 41 | { 42 | name: "Eagle", 43 | url: "http://www.wallpaperawesome.com/wallpapers-awesome/wallpapers-animals-pets-puppies-awesome/wallpaper-animal-pet-puppy-awesome-315.jpg" 44 | }, 45 | { 46 | name: "Bad Boy", 47 | url: "https://i.pinimg.com/736x/65/34/49/653449c123e8187772acc852f3c691d7--extinct-animals-prehistoric-animals.jpg" 48 | } 49 | ] 50 | }; 51 | } 52 | render() { 53 | return ( 54 | 55 | 56 | { 65 | return ( 66 | 78 | 89 | 96 | {this.state.dataArray[index].name} 97 | 98 | 99 | 111 | 118 | 119 | 120 | ); 121 | }} 122 | /> 123 | 124 | 136 | 152 | 159 | Tap to change 160 | 161 | 162 | 163 | ); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/Example/Router.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Animals from "./Animals"; 3 | import SocialTheme from "./SocialTheme"; 4 | import { StackNavigator } from "react-navigation"; 5 | export default (DrawNav = StackNavigator({ 6 | Animals: { screen: Animals }, 7 | SocialTheme: { screen: SocialTheme } 8 | })); 9 | -------------------------------------------------------------------------------- /src/Example/SocialTheme.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import HelixScroll from "../components/HelixScroll"; 3 | import { 4 | View, 5 | Image, 6 | TouchableOpacity, 7 | Text, 8 | Dimensions, 9 | StatusBar, 10 | Platform, 11 | Animated 12 | } from "react-native"; 13 | import { Icon, Button } from "native-base"; 14 | const { width, height } = Dimensions.get("window"); 15 | export default class Animals extends Component { 16 | static navigationOptions = ({ navigation }) => ({ 17 | header: null 18 | }); 19 | constructor(props) { 20 | super(props); 21 | this.state = { 22 | dataArray: [ 23 | { 24 | name: "Jack Seen", 25 | username: "@jackseen", 26 | shots: 12, 27 | following: 314, 28 | followers: 487, 29 | image: "http://www.inspirefirst.com/wp-content/uploads/2012/09/0223.jpg" 30 | }, 31 | { 32 | name: "Spade Haan", 33 | username: "@spadeHaan", 34 | shots: 41, 35 | following: 122, 36 | followers: 865, 37 | image: "https://developers.google.com/web/images/contributors/ilyagrigorik.jpg" 38 | }, 39 | { 40 | name: "Julia Joe", 41 | username: "@juliajoe", 42 | shots: 61, 43 | following: 12, 44 | followers: 544, 45 | image: "https://i.pinimg.com/originals/6f/90/17/6f901708bf30f8a04bc04ce143fadade.jpg" 46 | }, 47 | { 48 | name: "Don Davy", 49 | username: "@dondavy", 50 | shots: 13, 51 | following: 114, 52 | followers: 233, 53 | image: "https://pp.userapi.com/c7007/v7007830/213eb/_G_cVZ5A_ag.jpg" 54 | }, 55 | { 56 | name: "Jaz Pen", 57 | username: "@jazpen", 58 | shots: 42, 59 | following: 13, 60 | followers: 561, 61 | image: "http://data.whicdn.com/images/20992812/original.jpg" 62 | }, 63 | { 64 | name: "Dan Bridge", 65 | username: "@danbridge", 66 | shots: 32, 67 | following: 115, 68 | followers: 421, 69 | image: "https://favim.com/orig/201106/24/black-and-white-boy-cute-hair-profile-Favim.com-81599.jpg" 70 | } 71 | ] 72 | }; 73 | } 74 | render() { 75 | return ( 76 | 77 | { 83 | return ( 84 | 93 | 94 | 109 | 122 | 123 | 135 | 144 | 147 | 169 | 170 | 171 | 179 | 187 | 188 | {this.state.dataArray[index].name} 189 | 190 | 191 | {this.state.dataArray[index].username} 192 | 193 | 194 | 195 | 203 | Shots 204 | 205 | following 206 | 207 | 208 | followers 209 | 210 | 211 | 219 | 220 | {this.state.dataArray[index].shots} 221 | 222 | 223 | {this.state.dataArray[index].followers} 224 | 225 | 226 | {this.state.dataArray[index].following} 227 | 228 | 229 | 230 | 237 | 257 | 258 | 259 | 260 | ); 261 | }} 262 | /> 263 | 276 | 296 | 303 | Tap to change 304 | 305 | 306 | 307 | ); 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /src/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasbir23/HelixScrollView-React-Native/fccf082c5be7cb335735df875566a715d5cedba6/src/assets/.DS_Store -------------------------------------------------------------------------------- /src/assets/husna.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Jasbir23/HelixScrollView-React-Native/fccf082c5be7cb335735df875566a715d5cedba6/src/assets/husna.gif -------------------------------------------------------------------------------- /src/components/HelixScroll.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | Animated, 4 | View, 5 | ListView, 6 | ScrollView, 7 | Dimensions, 8 | Text, 9 | ImageBackground 10 | } from "react-native"; 11 | 12 | const { width, height } = Dimensions.get("window"); 13 | 14 | export default class ScrollSwagger extends Component { 15 | constructor(props) { 16 | super(props); 17 | this.state = { 18 | scrollY: new Animated.Value(0), 19 | dataSource: undefined, 20 | maxScroll: 380 * 8 + 200, 21 | noOfItems: undefined, 22 | singleItemHeight: 320 23 | }; 24 | } 25 | componentWillReceiveProps(props) { 26 | let arr = []; 27 | let pin = props.itemCount; 28 | while (pin >= 0) { 29 | arr.push(pin); 30 | pin--; 31 | } 32 | arr.reverse(); 33 | this.setState({ 34 | noOfItems: props.itemCount + 1, 35 | dataSource: arr 36 | }); 37 | } 38 | componentWillMount() { 39 | let arr = []; 40 | let pin = this.props.itemCount; 41 | while (pin >= 0) { 42 | arr.push(pin); 43 | pin--; 44 | } 45 | arr.reverse(); 46 | this.setState({ 47 | noOfItems: this.props.itemCount + 1, 48 | dataSource: arr 49 | }); 50 | } 51 | renderRow(item, index) { 52 | let micro = this.state.singleItemHeight / 3; 53 | let can = 0; 54 | let scrollArray = []; 55 | const ind = index; 56 | let ang = -ind * 90; 57 | let rotArr = []; 58 | while (can < 4 * this.state.noOfItems) { 59 | scrollArray.push(can * micro); 60 | var temp = rotArr[rotArr.length]; 61 | rotArr.push(ang - 90 * can + "deg"); 62 | can++; 63 | } 64 | let loopArray = [1, 0.6, 0.4, 0.6]; 65 | let xArray = [0, -1000, 0, 1000]; 66 | let opArray = [1, 0.7, 0.5, 0.7]; 67 | let pointer = ind; 68 | while (pointer > 0) { 69 | pointer--; 70 | var temp = loopArray[0]; 71 | var temp2 = xArray[0]; 72 | var temp3 = opArray[0]; 73 | loopArray.splice(0, 1); 74 | xArray.splice(0, 1); 75 | opArray.splice(0, 1); 76 | opArray.push(temp3); 77 | xArray.push(temp2); 78 | loopArray.push(temp); 79 | } 80 | let finalZ = []; 81 | let finalX = []; 82 | let finalOp = []; 83 | let man = 0; 84 | while (man < this.state.noOfItems) { 85 | finalZ = finalZ.concat(loopArray); 86 | finalX = finalX.concat(xArray); 87 | finalOp = finalOp.concat(opArray); 88 | man++; 89 | } 90 | const helixZ = this.state.scrollY.interpolate({ 91 | inputRange: scrollArray, 92 | outputRange: finalZ 93 | }); 94 | const helixRot = this.state.scrollY.interpolate({ 95 | inputRange: scrollArray, 96 | outputRange: rotArr 97 | }); 98 | const helixX = this.state.scrollY.interpolate({ 99 | inputRange: scrollArray, 100 | outputRange: finalX 101 | }); 102 | const helixOp = this.state.scrollY.interpolate({ 103 | inputRange: scrollArray, 104 | outputRange: finalOp 105 | }); 106 | if (item === this.state.noOfItems - 1) { 107 | return ( 108 | 126 | ); 127 | } 128 | return ( 129 | 146 | {this.props.renderCard(index)} 147 | 148 | ); 149 | } 150 | render() { 151 | return ( 152 | 153 | 154 | 165 | 179 | onScroll={Animated.event( 180 | [ 181 | { 182 | nativeEvent: { contentOffset: { y: this.state.scrollY } } 183 | } 184 | ], 185 | { 186 | useNativeDriver: true // <- Native Driver used for animated events 187 | } 188 | )} 189 | > 190 | {this.state.dataSource.map((item, index) => { 191 | return this.renderRow(item, index); 192 | })} 193 | 194 | 195 | 196 | ); 197 | } 198 | } 199 | --------------------------------------------------------------------------------