├── README.md ├── assets └── dancingscript.ttf ├── demo.gif ├── exp.json ├── inspiration.gif ├── main.js ├── package.json └── src ├── components └── digital-magazine.js ├── img ├── page1.jpg ├── page2.jpg ├── page3.jpeg └── page3.jpg └── index.js /README.md: -------------------------------------------------------------------------------- 1 | # react-native-magazine-listview 2 | A pure javascript magazine listview for React Native framework. 3 | 4 | ###Inspiration 5 | ![alt tag](https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/master/inspiration.gif) 6 | 7 | By Frederik Røssell from dribbble.com 8 | https://dribbble.com/shots/3058788-Digital-magazine-mockup 9 | 10 | ###Demo 11 | ![alt tag](https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/master/demo.gif) 12 | 13 | ## Try it out 14 | 15 | Try it with Exponent: https://getexponent.com/@sungwoopark95/react-native-digital-magazine 16 | 17 | ## Run it locally 18 | 19 | To install, there are two steps: 20 | 21 | 1. Install Exponent XDE [following this 22 | guide](https://docs.getexponent.com/versions/latest/introduction/installation.html). 23 | Also install the Exponent app on your phone if you want to test it on 24 | your device, otherwise you don't need to do anything for the simulator. 25 | 2. Clone this repo and run `npm install` 26 | ```bash 27 | git clone https://github.com/ggomaeng/react-native-magazine-listview.git magazineListview 28 | 29 | cd magazineListview 30 | npm install 31 | ``` 32 | 3. Open the project with Exponent XDE and run it. 33 | -------------------------------------------------------------------------------- /assets/dancingscript.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/2ce20829950af757a37a5483c07a6b537486bda9/assets/dancingscript.ttf -------------------------------------------------------------------------------- /demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/2ce20829950af757a37a5483c07a6b537486bda9/demo.gif -------------------------------------------------------------------------------- /exp.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-digital-magazine", 3 | "description": "An empty new project", 4 | "slug": "react-native-digital-magazine", 5 | "sdkVersion": "11.0.0", 6 | "version": "1.0.0", 7 | "orientation": "portrait", 8 | "primaryColor": "#cccccc", 9 | "iconUrl": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png", 10 | "notification": { 11 | "iconUrl": "https://s3.amazonaws.com/exp-us-standard/placeholder-push-icon-blue-circle.png", 12 | "color": "#000000" 13 | }, 14 | "loading": { 15 | "iconUrl": "https://s3.amazonaws.com/exp-brand-assets/ExponentEmptyManifest_192.png", 16 | "hideExponentText": false 17 | }, 18 | "packagerOpts": { 19 | "assetExts": ["ttf", "mp4"] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /inspiration.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/2ce20829950af757a37a5483c07a6b537486bda9/inspiration.gif -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | import Exponent from 'exponent'; 2 | import Main from './src/index'; 3 | 4 | Exponent.registerRootComponent(Main); 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-digital-magazine", 3 | "version": "0.0.0", 4 | "description": "Hello Exponent!", 5 | "author": null, 6 | "private": true, 7 | "main": "main.js", 8 | "dependencies": { 9 | "exponent": "~11.0.2", 10 | "@exponent/vector-icons": "~2.0.3", 11 | "react": "~15.3.2", 12 | "react-native": "git+https://github.com/exponentjs/react-native#sdk-11.0.3" 13 | } 14 | } -------------------------------------------------------------------------------- /src/components/digital-magazine.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by ggoma on 2016. 11. 25.. 3 | */ 4 | import Exponent from 'exponent'; 5 | import React, {Component} from 'react'; 6 | import { 7 | Animated, 8 | View, 9 | Text, 10 | Image, 11 | Dimensions, 12 | ListView, 13 | StyleSheet 14 | } from 'react-native'; 15 | 16 | const {width, height} = Dimensions.get('window'); 17 | const midpoint = width / 2; 18 | 19 | export default class DigitalMagazine extends Component { 20 | constructor(props) { 21 | super(props); 22 | const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2}); 23 | 24 | this.state = { 25 | dataSource: ds.cloneWithRows(props.items), 26 | opacity: new Animated.Value(1), 27 | 28 | images: props.images.reverse(), //to stack the images in right order 29 | opacity_values : props.images.map(() => { 30 | return new Animated.Value(1) 31 | }), 32 | text_opacity : props.images.map(() => { 33 | return new Animated.Value(1) 34 | }), 35 | }; 36 | 37 | this.page = props.images.length - 1; //backwards 38 | this.offset = 0; 39 | this._renderRow = this._renderRow.bind(this); 40 | this.handleScroll = this.handleScroll.bind(this); 41 | 42 | } 43 | 44 | 45 | 46 | _renderRow(row) { 47 | var {page, publisher, title, author, highlight, color} = row; 48 | 49 | return ( 50 | 51 | 52 | 54 | 55 | 56 | 57 | {publisher} 58 | 59 | 60 | 61 | 62 | 63 | {highlight}{title} 64 | 65 | 66 | 67 | 68 | 69 | {author} 70 | 71 | 72 | 73 | 74 | 75 | 76 | 78 | 79 | 80 | 81 | 82 | 83 | ) 84 | } 85 | 86 | 87 | handleScroll (event) { 88 | var e = event.nativeEvent; 89 | 90 | var currentOffset = e.contentOffset.x; 91 | var offset_ratio = (currentOffset / width); 92 | if(currentOffset > this.offset) { 93 | if(!Number.isInteger(offset_ratio) && offset_ratio > 0 ) { 94 | var page = Math.floor(offset_ratio); 95 | // console.log('scrolling right on page', page); 96 | var stack = Math.abs(page - this.state.opacity_values.length + 1); 97 | // console.log('position on stack', stack); 98 | //make current slide fade to 0 99 | if(stack != 0) { //check last page 100 | this.state.opacity_values[stack].setValue(Math.abs((currentOffset - ( width * (page +1) )) / width)); 101 | //set current page to 0 102 | this.state.text_opacity[page].setValue(Math.abs((currentOffset - ( width * (page +1) )) / width)); 103 | //set next page to 1 from 0 104 | this.state.text_opacity[page + 1].setValue(Math.abs((currentOffset - ( width * (page) )) / width)); 105 | } 106 | } 107 | } else { 108 | if(!Number.isInteger(offset_ratio) && offset_ratio > 0) { 109 | var page = Math.ceil(offset_ratio); 110 | // console.log('scrolling left on page', page); 111 | var stack = Math.abs(page - this.state.opacity_values.length + 1); 112 | // console.log('position on stack', stack); 113 | if(this.state.opacity_values[stack + 1] != null && page < this.state.opacity_values.length) { 114 | //make previous slide fade to 1 -- remember, here uses math.ceil 115 | this.state.opacity_values[stack + 1].setValue(Math.abs(currentOffset - ( width * page )) / width); 116 | //set left page to 1 117 | this.state.text_opacity[page - 1].setValue(Math.abs((currentOffset - ( width * (page) )) / width)); 118 | //set current page from 1 to 0 119 | this.state.text_opacity[page].setValue(Math.abs(currentOffset - (width * (page-1) )) / width); 120 | 121 | } 122 | } 123 | } 124 | this.offset = currentOffset; 125 | 126 | } 127 | 128 | renderImages() { 129 | 130 | var {images, opacity_values} = this.state; 131 | 132 | return images.map((image, i) => { 133 | return 134 | }) 135 | 136 | 137 | } 138 | 139 | 140 | render() { 141 | 142 | var {images, page, opacity} = this.state; 143 | return ( 144 | 145 | {this.renderImages()} 146 | 147 | 155 | 156 | 157 | ) 158 | } 159 | } 160 | 161 | var styles = StyleSheet.create({ 162 | img: { 163 | width: width, 164 | height: height, 165 | position: 'absolute', 166 | } 167 | }) -------------------------------------------------------------------------------- /src/img/page1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/2ce20829950af757a37a5483c07a6b537486bda9/src/img/page1.jpg -------------------------------------------------------------------------------- /src/img/page2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/2ce20829950af757a37a5483c07a6b537486bda9/src/img/page2.jpg -------------------------------------------------------------------------------- /src/img/page3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/2ce20829950af757a37a5483c07a6b537486bda9/src/img/page3.jpeg -------------------------------------------------------------------------------- /src/img/page3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ggomaeng/react-native-magazine-listview/2ce20829950af757a37a5483c07a6b537486bda9/src/img/page3.jpg -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by ggoma on 2016. 11. 25.. 3 | */ 4 | import Exponent from 'exponent'; 5 | import React from 'react'; 6 | import { 7 | StyleSheet, 8 | Text, 9 | View, 10 | } from 'react-native'; 11 | 12 | import DigitalMagazine from './components/digital-magazine'; 13 | 14 | export default class App extends React.Component { 15 | 16 | 17 | state = { 18 | appIsReady: false, 19 | } 20 | 21 | async componentWillMount() { 22 | try { 23 | await Exponent.Font.loadAsync({cursive: require('../assets/dancingscript.ttf')}); 24 | this.setState({appIsReady: true}); 25 | } catch(e) { 26 | // console.warn( 27 | // 'There was an error caching assets (see: main.js), perhaps due to a ' + 28 | // 'network timeout, so we skipped caching. Reload the app to try again.' 29 | // ); 30 | // console.log(e.message); 31 | } 32 | } 33 | 34 | 35 | 36 | render() { 37 | if (!this.state.appIsReady) { 38 | return ( 39 | 40 | ); 41 | } 42 | 43 | return ( 44 | 45 | 59 | 60 | ); 61 | } 62 | } 63 | 64 | const styles = StyleSheet.create({ 65 | container: { 66 | flex: 1, 67 | backgroundColor: '#fff', 68 | }, 69 | }); --------------------------------------------------------------------------------