├── .npmignore ├── .gitignore ├── .gitattributes ├── .editorconfig ├── package.json ├── ImagePlaceholder.js └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | img 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Don't export images 2 | /img export-ignore 3 | # Gif are binaries 4 | *.gif binary 5 | # Don't conflict with package-lock file 6 | package-lock.json -diff 7 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | 3 | [*] 4 | charset=utf-8 5 | end_of_line=lf 6 | insert_final_newline=true 7 | trim_trailing_whitespace=true 8 | 9 | [*.js] 10 | indent_style=space 11 | indent_size=2 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-image-with-placeholder", 3 | "version": "0.1.1", 4 | "description": "Load images incrementally to provide a better UX", 5 | "main": "ImagePlaceholder.js", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/mitogh/react-native-image-placeholder.git" 9 | }, 10 | "scripts": { 11 | "test": "echo \"Error: no test specified\" && exit 1" 12 | }, 13 | "keywords": [ 14 | "react", 15 | "react-native", 16 | "react-component", 17 | "image", 18 | "ios", 19 | "loader" 20 | ], 21 | "bugs": { 22 | "url": "https://github.com/mitogh/react-native-image-placeholder/issues" 23 | }, 24 | "peerDependencies": { 25 | "react-native": ">=0.20.0" 26 | }, 27 | "author": "Crisoforo Gaspar Hernandez (http://crisoforo.com/)", 28 | "license": "MIT", 29 | "dependencies": { 30 | "npm": "^6.0.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ImagePlaceholder.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { 3 | ActivityIndicator, 4 | Image, 5 | StyleSheet, 6 | Text, 7 | View, 8 | Animated 9 | } from "react-native"; 10 | 11 | export default class ImagePlaceholder extends Component { 12 | static defaultProps = { 13 | duration: 750, 14 | showActivityIndicator: true, 15 | resizeMode: "cover" 16 | }; 17 | 18 | constructor(props) { 19 | super(props); 20 | this.state = { 21 | isLoading: true, 22 | fadeAnim: new Animated.Value(1) 23 | }; 24 | } 25 | 26 | render() { 27 | return ( 28 | 29 | {this._renderPlaceholder.bind(this)()} 30 | 36 | 37 | ); 38 | } 39 | 40 | _onProgress(event) { 41 | const progress = event.nativeEvent.loaded / event.nativeEvent.total; 42 | this.setState({ isLoading: progress < 1 }); 43 | } 44 | 45 | _renderPlaceholder() { 46 | return ( 47 | 48 | 53 | {this._renderActivityIndicator()} 54 | 55 | ); 56 | } 57 | 58 | _getPlaceholderStyles() { 59 | let container = [styles.placeholderContainer]; 60 | if (!this.state.isLoading) { 61 | Animated.timing(this.state.fadeAnim, { 62 | toValue: 0, 63 | duration: this.props.duration 64 | }).start(); 65 | container.push({ opacity: this.state.fadeAnim }); 66 | } 67 | container.push(this.props.placeholderContainerStyle); 68 | return container; 69 | } 70 | 71 | _renderActivityIndicator() { 72 | if (this.props.showActivityIndicator) { 73 | if (this.props.ActivityIndicator) { 74 | return this.props.ActivityIndicator; 75 | } else { 76 | return ( 77 | 81 | ); 82 | } 83 | } else { 84 | return null; 85 | } 86 | } 87 | } 88 | 89 | const styles = StyleSheet.create({ 90 | container: { 91 | flex: 1, 92 | alignSelf: "stretch" 93 | }, 94 | placeholderContainer: { 95 | flex: 1, 96 | alignSelf: "stretch", 97 | zIndex: 2, 98 | position: "absolute", 99 | top: 0, 100 | left: 0, 101 | right: 0, 102 | bottom: 0, 103 | alignItems: "center", 104 | justifyContent: "center" 105 | }, 106 | placeholder: { 107 | flex: 1, 108 | alignSelf: "stretch", 109 | zIndex: 2, 110 | position: "absolute", 111 | top: 0, 112 | left: 0, 113 | right: 0, 114 | bottom: 0 115 | }, 116 | image: { 117 | flex: 1 118 | } 119 | }); 120 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native Image Placeholder 2 | 3 | > Load images incrementally to provide a better User Experience. 4 | 5 | If you want to download larger images but provide with a nice transition while the users are waiting 6 | for the image you can provide with a smaller version of the image (as [you can see on the 7 | examples](#examples)). 8 | 9 | This is inspired on how [medium](https://medium.com) loads the articles and the `iron-image` component 10 | from the [polymer](https://www.webcomponents.org/element/PolymerElements/iron-image) library works 11 | by providing the option to present the larger image and give to the user a hint of the presence of 12 | an image, specially on slower connections. 13 | 14 | It basically fetchs an image an tracks the progress of the image once is ready it shows the user the 15 | requeested image but during the period of loading the user can show a spinner or at least a hint of 16 | the image in a smaller version. 17 | 18 | # Examples 19 | 20 | ## Multiple Images. 21 | 22 | ![](https://raw.githubusercontent.com/mitogh/react-native-image-placeholder/master/img/multiple.gif) 23 | 24 | ## Single Image. 25 | 26 | ![](https://raw.githubusercontent.com/mitogh/react-native-image-placeholder/master/img/single.gif) 27 | 28 | # Installation 29 | 30 | ```bash 31 | npm install --save react-native-image-with-placeholder 32 | ``` 33 | 34 | # Getting started 35 | 36 | Import the file as specified on the [installation steps](#installation). Then import the Component where you have 37 | plans to use it, for example: 38 | 39 | ```js 40 | import ImagePlaceholder from 'react-native-image-with-placeholder' 41 | ``` 42 | 43 | And inside of the `render` method of your component add the component with the properties you 44 | require. 45 | 46 | ## Basic usage 47 | 48 | ```js 49 | 59 | ); 60 | ``` 61 | 62 | ## Multiple images with different transition speed 63 | 64 | ```js 65 | render() { 66 | return ( 67 | 68 | 73 | 79 | 85 | 86 | ) 87 | } 88 | 89 | const styles = StyleSheet.create({ 90 | container: { 91 | flex: 1, 92 | justifyContent: 'center', 93 | alignItems: 'center', 94 | flexWrap: 'wrap', 95 | }, 96 | item: { 97 | flex: 1, 98 | } 99 | }); 100 | ``` 101 | 102 | # API 103 | 104 | Props | Type | Optional | Default | Description 105 | ------------------|---------------|---------------|-------------|------------ 106 | src | String | false | null | The url of the main image to be fetched. 107 | placeholder | String | true | null | The smaller image present before the main image is ready if is not present it will show an [`ActivityIndicator`](https://facebook.github.io/react-native/docs/activityindicator.html) component instead. 108 | style | View.propTypes.style | true | [Container Style](https://github.com/mitogh/react-native-image-placeholder/blob/master/ImagePlaceholder.js#L90-L93) | Style applied to the image container 109 | imageStyle | Object | true | null | The styles appliead to the main image. 110 | placeholderStyle | Object | true | null | The styles applied to the placeholder image. 111 | placeholderContainerStyle | Object | true | null | The styles applied to the container `View` of the placeholder image. 112 | duration | Integer | true | 750 | Time in miliseconds used to transition to the original image once is ready. 113 | showActivityIndicator | Boolean | true | true | If `true` an [`ActivityIndicator`](https://facebook.github.io/react-native/docs/activityindicator.html) should be displayed while the placeholder image is being fetch or if is not present. 114 | activityIndicatorProps | Object | true | null | Options to pass to the [`ActivityIndicator`](https://facebook.github.io/react-native/docs/activityindicator.html) component such as `size`, `color` or `style`. 115 | [`ActivityIndicator`](https://facebook.github.io/react-native/docs/activityindicator.html) | Component | true | null | If present it will render this instead of the [`ActivityIndicator`](https://facebook.github.io/react-native/docs/activityindicator.html) component and `activityIndicatorProps` is no longer used and valid. 116 | --------------------------------------------------------------------------------