Browse Source

finished first version

cyqresig 7 years ago
parent
commit
07a775abc9
6 changed files with 508 additions and 0 deletions
  1. 12
    0
      .gitignore
  2. 12
    0
      .npmignore
  3. 226
    0
      LoadingSpinnerOverlay.js
  4. 221
    0
      README.md
  5. 8
    0
      note.md
  6. 29
    0
      package.json

+ 12
- 0
.gitignore View File

@@ -0,0 +1,12 @@
1
+*.[aod]
2
+*.DS_Store
3
+.DS_Store
4
+*Thumbs.db
5
+*.iml
6
+.gradle
7
+.idea
8
+node_modules
9
+npm-debug.log
10
+/android/build
11
+/ios/**/*xcuserdata*
12
+/ios/**/*xcshareddata*

+ 12
- 0
.npmignore View File

@@ -0,0 +1,12 @@
1
+*.[aod]
2
+*.DS_Store
3
+.DS_Store
4
+*Thumbs.db
5
+*.iml
6
+.gradle
7
+.idea
8
+node_modules
9
+npm-debug.log
10
+/android/build
11
+/ios/**/*xcuserdata*
12
+/ios/**/*xcshareddata*

+ 226
- 0
LoadingSpinnerOverlay.js View File

@@ -0,0 +1,226 @@
1
+/*
2
+ * A smart loading spinner overlay for React Native apps, written in JS
3
+ * https://github.com/react-native-component/react-native-smart-loading-spinner-overlay/
4
+ * Released under the MIT license
5
+ * Copyright (c) 2016 react-native-component <moonsunfall@aliyun.com>
6
+ */
7
+
8
+import React, {
9
+    Component,
10
+    PropTypes,
11
+} from 'react'
12
+import {
13
+    View,
14
+    Modal,
15
+    StyleSheet,
16
+    Animated,
17
+    Easing,
18
+    Dimensions,
19
+    ActivityIndicator,
20
+    ActivityIndicatorIOS,
21
+    ProgressBarAndroid,
22
+} from 'react-native'
23
+
24
+import TimerEnhance from 'react-native-smart-timer-enhance'
25
+
26
+const {width: deviceWidth, height: deviceHeight,} = Dimensions.get('window')
27
+const styles = StyleSheet.create({
28
+    overlay: {
29
+        position: 'absolute',
30
+        left: 0,
31
+        top: 0,
32
+        zIndex: 998,
33
+    },
34
+    container: {
35
+        backgroundColor: 'rgba(0, 0, 0, 0.8)',
36
+        position: 'absolute',
37
+        borderRadius: 8,
38
+        padding: 18,
39
+        left: -9999,
40
+        top: -9999,
41
+        zIndex: 999,
42
+    },
43
+})
44
+const noop = () => {}
45
+
46
+class LoadingSpinnerOverlay extends Component {
47
+
48
+    static defaultProps = {
49
+        duration: 255,
50
+        delay: 0,
51
+        marginTop: 0,
52
+        modal: true,
53
+    }
54
+
55
+    static propTypes = {
56
+        overlayStyle: View.propTypes.style,
57
+        style: View.propTypes.style,
58
+        duration: PropTypes.number,
59
+        delay: PropTypes.number,
60
+        marginTop: PropTypes.number,
61
+        modal: PropTypes.bool,
62
+    }
63
+
64
+    constructor(props) {
65
+        super(props)
66
+        this.state = {
67
+            visible: false,
68
+            opacity: new Animated.Value(0),
69
+            children: props.children,
70
+            modal: props.modal,
71
+            marginTop: props.marginTop,
72
+        }
73
+        this._loadingSpinnerWidth = null
74
+        this._loadingSpinnerHeight = null
75
+        this._loadingSpinnerShowAnimation = null
76
+        this._loadingSpinnerHideAnimation = null
77
+        this._loadingSpinnerAnimationToggle = null
78
+    }
79
+
80
+    render() {
81
+        let loadingSpinner = this._renderLoadingSpinner()
82
+        return this._renderOverLay(loadingSpinner)
83
+    }
84
+    
85
+    _renderOverLay(loadingSpinner) {
86
+        return (
87
+            this.state.modal ?
88
+                (this.state.marginTop === 0 ?
89
+                    <Modal animationType={'none'} transparent={true} visible={this.state.visible} onRequestClose={noop}>
90
+                        {loadingSpinner}
91
+                    </Modal> :
92
+                    (this.state.visible ?
93
+                        <View
94
+                            style={[ styles.overlay, {marginTop: this.props.marginTop, width: deviceWidth, height: deviceHeight - this.props.marginTop,}, this.props.overlayStyle, ]}>
95
+                            {loadingSpinner}
96
+                        </View> : null))
97
+                : loadingSpinner
98
+        )
99
+    }
100
+    
101
+    _renderLoadingSpinner() {
102
+        let children
103
+        if(this.state.children == null) {
104
+            children = this._renderActivityIndicator()
105
+        }
106
+        else {
107
+            children = React.Children.map(this.state.children, (child) => {
108
+                if (!React.isValidElement(child)) {
109
+                    return null
110
+                }
111
+                return child
112
+            })
113
+        }
114
+        return (
115
+            this.state.visible ?
116
+                <Animated.View
117
+                    ref={ component => this._container = component }
118
+                    onLayout={this._onLoadingSpinnerLayout}
119
+                    style={[styles.container, this.props.style, {opacity:this.state.opacity, }]}>
120
+                    {children}
121
+                </Animated.View> : null
122
+        )
123
+    }
124
+
125
+    show({modal = this.state.modal, marginTop = this.state.marginTop, children = this.state.children, duration = this.props.duration, easing = Easing.linear, delay = this.props.delay, animationEnd,}
126
+        = {modal: this.state.modal, marginTop: this.state.marginTop, children: this.state.children, duration: this.props.duration, easing: Easing.linear, delay: this.props.delay,}) {
127
+
128
+        this._loadingSpinnerShowAnimation && this._loadingSpinnerShowAnimation.stop()
129
+        this._loadingSpinnerHideAnimation && this._loadingSpinnerHideAnimation.stop()
130
+        this._loadingSpinnerAnimationToggle && this.clearTimeout(this._loadingSpinnerAnimationToggle)
131
+
132
+        if(this.state.visible) {
133
+            this._setLoadingSpinnerOverlayPosition({modal, marginTop})
134
+        }
135
+
136
+        this.setState({
137
+            children,
138
+            modal,
139
+            marginTop,
140
+            visible: true,
141
+        })
142
+
143
+        this._loadingSpinnerShowAnimation = Animated.timing(
144
+            this.state.opacity,
145
+            {
146
+                toValue: 1,
147
+                duration,
148
+                easing,
149
+                delay,
150
+            }
151
+        )
152
+        this._loadingSpinnerShowAnimation.start(() => {
153
+            this._loadingSpinnerShowAnimation = null
154
+            animationEnd && animationEnd()
155
+        })
156
+    }
157
+
158
+    hide({duration = this.props.duration, easing = Easing.linear, delay = this.props.delay, animationEnd,}
159
+        = {duration: this.props.duration, easing: Easing.linear, delay: this.props.delay,}) {
160
+
161
+        this._loadingSpinnerShowAnimation && this._loadingSpinnerShowAnimation.stop()
162
+        this._loadingSpinnerHideAnimation && this._loadingSpinnerHideAnimation.stop()
163
+        this.clearTimeout(this._loadingSpinnerAnimationToggle)
164
+
165
+        this._loadingSpinnerHideAnimation = Animated.timing(
166
+            this.state.opacity,
167
+            {
168
+                toValue: 0,
169
+                duration,
170
+                easing,
171
+                delay,
172
+            }
173
+        )
174
+        this._loadingSpinnerHideAnimation.start( () => {
175
+            this._loadingSpinnerHideAnimation = null
176
+            this.setState({
177
+                visible: false,
178
+            })
179
+            animationEnd && animationEnd()
180
+        })
181
+    }
182
+
183
+    _onLoadingSpinnerLayout = (e) => {
184
+        this._loadingSpinnerWidth = e.nativeEvent.layout.width
185
+        this._loadingSpinnerHeight = e.nativeEvent.layout.height
186
+        this._setLoadingSpinnerOverlayPosition()
187
+    }
188
+
189
+    _setLoadingSpinnerOverlayPosition({modal, marginTop} = {modal: this.state.modal, marginTop: this.state.marginTop}) {
190
+        if(!this._loadingSpinnerWidth || !this._loadingSpinnerHeight) {
191
+            return
192
+        }
193
+        let left = (deviceWidth - this._loadingSpinnerWidth) / 2
194
+        let top =  (deviceHeight - this._loadingSpinnerHeight) / 2 - (modal && marginTop === 0 ? 0 : marginTop)
195
+        this._container.setNativeProps({
196
+            style: {
197
+                left,
198
+                top,
199
+            }
200
+        })
201
+    }
202
+
203
+    _renderActivityIndicator() {
204
+        return ActivityIndicator ? (
205
+            <ActivityIndicator
206
+                style={{position: 'relative', left: 1, top: 1,}}
207
+                animating={true}
208
+                color={'#fff'}
209
+                size={'large'}/>
210
+        ) : Platform.OS == 'android' ?
211
+            (
212
+                <ProgressBarAndroid
213
+                    color={'#fff'}
214
+                    styleAttr={'large'}/>
215
+
216
+            ) :  (
217
+            <ActivityIndicatorIOS
218
+                animating={true}
219
+                color={'#fff'}
220
+                size={'large'}/>
221
+        )
222
+    }
223
+
224
+}
225
+
226
+export default TimerEnhance(LoadingSpinnerOverlay)

+ 221
- 0
README.md View File

@@ -1,6 +1,227 @@
1 1
 # react-native-smart-loading-spinner-overlay
2 2
 
3
+[![npm](https://img.shields.io/npm/v/react-native-smart-loading-spinner-overlay.svg)](https://www.npmjs.com/package/react-native-smart-loading-spinner-overlay)
4
+[![npm](https://img.shields.io/npm/dm/react-native-smart-loading-spinner-overlay.svg)](https://www.npmjs.com/package/react-native-smart-loading-spinner-overlay)
5
+[![npm](https://img.shields.io/npm/dt/react-native-smart-loading-spinner-overlay.svg)](https://www.npmjs.com/package/react-native-smart-loading-spinner-overlay)
6
+[![npm](https://img.shields.io/npm/l/react-native-smart-loading-spinner-overlay.svg)](https://github.com/react-native-component/react-native-smart-loading-spinner-overlay/blob/master/LICENSE)
7
+
3 8
 A smart loading spinner overlay for React Native apps, written in JS for cross-platform support.
4 9
 It works on iOS and Android.
5 10
 
6 11
 This component is compatible with React Native 0.25 and newer.
12
+
13
+## Preview
14
+
15
+![react-native-smart-loading-spinner-overlay-preview-ios][1]
16
+![react-native-smart-loading-spinner-overlay-preview-android][2]
17
+
18
+## Installation
19
+
20
+```
21
+npm install react-native-smart-loading-spinner-overlay --save
22
+```
23
+
24
+## Full Demo
25
+
26
+see [ReactNativeComponentDemos][0]
27
+
28
+## Usage
29
+
30
+Install the loading-spinner-overlay from npm with `npm install react-native-smart-loading-spinner-overlay --save`.
31
+Then, require it from your app's JavaScript files with `import loading-spinner-overlay from 'react-native-smart-loading-spinner-overlay'`.
32
+
33
+```js
34
+
35
+import React, {
36
+    Component,
37
+} from 'react'
38
+import {
39
+    View,
40
+} from 'react-native'
41
+
42
+import Button from 'react-native-smart-button'
43
+import TimerEnhance from 'react-native-smart-timer-enhance'
44
+import Toast from 'react-native-smart-loading-spinner-overlay'
45
+
46
+class LoadingSpinnerOverLayDemo extends Component {
47
+
48
+    constructor(props) {
49
+        super(props)
50
+        this.state = {}
51
+    }
52
+
53
+    render() {
54
+        return (
55
+            <View style={{ paddingTop: 64, flex: 1, backgroundColor: '#fff',}}>
56
+                <Button
57
+                    onPress={this._showModalLoadingSpinnerOverLay}
58
+                    touchableType={Button.constants.touchableTypes.fadeContent}
59
+                    style={{margin: 10, height: 40, backgroundColor: 'red', borderRadius: 3, borderWidth: StyleSheet.hairlineWidth, borderColor: 'red', justifyContent: 'center',}}
60
+                    textStyle={{fontSize: 17, color: 'white'}}
61
+                >
62
+                    show modal loading spinner (模态)
63
+                </Button>
64
+                <Button
65
+                    onPress={this._showPartModalLoadingSpinnerOverLay}
66
+                    touchableType={Button.constants.touchableTypes.highlight}
67
+                    underlayColor={'#C90000'}
68
+                    style={{margin: 10, justifyContent: 'center', height: 40, backgroundColor: 'red', borderRadius: 3, borderWidth: StyleSheet.hairlineWidth, borderColor: 'red', justifyContent: 'center',}}
69
+                    textStyle={{fontSize: 17, color: 'white'}}
70
+                >
71
+                    [part modal]can click header (导航栏允许点击)
72
+                </Button>
73
+                <Button
74
+                    onPress={this._showNoModalLoadingSpinnerOverLay}
75
+                    touchableType={Button.constants.touchableTypes.blur}
76
+                    style={{margin: 10, justifyContent: 'center', height: 40, backgroundColor: 'red', borderRadius: 3, borderWidth: StyleSheet.hairlineWidth, borderColor: 'red', justifyContent: 'center',}}
77
+                    textStyle={{fontSize: 17,  color: 'white'}}
78
+                >
79
+                    show no modal loading spinner (非模态)
80
+                </Button>
81
+
82
+                <Button
83
+                    onPress={this._showImmediateLoadingSpinnerOverLayAndImmediateHide}
84
+                    touchableType={Button.constants.touchableTypes.fade}
85
+                    style={{margin: 10, justifyContent: 'center', height: 40, backgroundColor: 'red', borderRadius: 3, borderWidth: StyleSheet.hairlineWidth, borderColor: 'red', justifyContent: 'center',}}
86
+                    textStyle={{fontSize: 17,  color: 'white'}}
87
+                >
88
+                    show and hide immediately (无渐变)
89
+                </Button>
90
+
91
+                <Button
92
+                    onPress={this._showModal_2_LoadingSpinnerOverLay}
93
+                    touchableType={Button.constants.touchableTypes.fadeContent}
94
+                    style={{margin: 10, height: 40, backgroundColor: 'red', borderRadius: 3, borderWidth: StyleSheet.hairlineWidth, borderColor: 'red', justifyContent: 'center',}}
95
+                    textStyle={{fontSize: 17, color: 'white'}}
96
+                >
97
+                    custom content (自定义内容)
98
+                </Button>
99
+
100
+                <LoadingSpinnerOverlay
101
+                    ref={ component => this._modalLoadingSpinnerOverLay = component }/>
102
+                <LoadingSpinnerOverlay
103
+                    ref={ component => this._partModalLoadingSpinnerOverLay = component }
104
+                    modal={true}
105
+                    marginTop={64}/>
106
+                <LoadingSpinnerOverlay
107
+                    ref={ component => this._LoadingSpinnerOverLay = component }
108
+                    modal={false}/>
109
+                <LoadingSpinnerOverlay
110
+                    ref={ component => this._modalImmediateLoadingSpinnerOverLay = component }/>
111
+                <LoadingSpinnerOverlay
112
+                    ref={ component => this._modal_2_LoadingSpinnerOverLay = component }>
113
+                    {this._renderActivityIndicator()}
114
+                </LoadingSpinnerOverlay>
115
+            </View>
116
+        )
117
+    }
118
+
119
+
120
+
121
+
122
+    _showModalLoadingSpinnerOverLay = () => {
123
+        this._modalLoadingSpinnerOverLay.show()
124
+        //simulate http request
125
+        this.setTimeout( () => {
126
+            this._modalLoadingSpinnerOverLay.hide()
127
+        }, 3000)
128
+    }
129
+
130
+    _showPartModalLoadingSpinnerOverLay = () => {
131
+        this._partModalLoadingSpinnerOverLay.show()
132
+        //simulate http request
133
+        this.setTimeout( () => {
134
+            this._partModalLoadingSpinnerOverLay.hide()
135
+        }, 3000)
136
+    }
137
+
138
+    _showNoModalLoadingSpinnerOverLay = () => {
139
+        this._LoadingSpinnerOverLay.show()
140
+        //simulate http request
141
+        this.setTimeout( () => {
142
+            this._LoadingSpinnerOverLay.hide()
143
+        }, 3000)
144
+    }
145
+
146
+    _showImmediateLoadingSpinnerOverLayAndImmediateHide = () => {
147
+        this._modalImmediateLoadingSpinnerOverLay.show({
148
+            duration: 0,
149
+            children: this._renderActivityIndicator(),
150
+        })
151
+        //simulate http request
152
+        this.setTimeout( () => {
153
+            this._modalImmediateLoadingSpinnerOverLay.hide({
154
+                duration: 0,
155
+            })
156
+        }, 3000)
157
+    }
158
+
159
+    _showModal_2_LoadingSpinnerOverLay = () => {
160
+        this._modal_2_LoadingSpinnerOverLay.show()
161
+        //simulate http request
162
+        this.setTimeout( () => {
163
+            this._modal_2_LoadingSpinnerOverLay.hide()
164
+        }, 3000)
165
+    }
166
+
167
+    _renderActivityIndicator() {
168
+        return ActivityIndicator ? (
169
+            <ActivityIndicator
170
+                animating={true}
171
+                color={'#ff0000'}
172
+                size={'small'}/>
173
+        ) : Platform.OS == 'android' ?
174
+            (
175
+                <ProgressBarAndroid
176
+                    color={'#ff0000'}
177
+                    styleAttr={'small'}/>
178
+
179
+            ) :  (
180
+            <ActivityIndicatorIOS
181
+                animating={true}
182
+                color={'#ff0000'}
183
+                size={'small'}/>
184
+        )
185
+    }
186
+
187
+}
188
+
189
+
190
+export default TimerEnhance(LoadingSpinnerOverLayDemo)
191
+```
192
+
193
+## Props
194
+
195
+Prop             | Type   | Optional | Default          | Description
196
+---------------- | ------ | -------- | ---------------- | -----------
197
+style            | style  | Yes      |                  | see [react-native documents][3]
198
+overlayStyle     | style  | Yes      |                  | see [react-native documents][3]
199
+duration         | number | Yes      | 255              | determine the duration of loading-spinner-overlay animation
200
+delay            | number | Yes      | 0                | determine the delay of loading-spinner-overlay animation
201
+marginTop        | number | Yes      | 0                | determine the marginTop of the root container view, it is used when the modal prop is false
202
+modal            | bool   | Yes      | true             | determine if the modal will be used
203
+
204
+## Method
205
+
206
+* show({modal, marginTop, children, duration, easing, delay, animationEnd,})
207
+
208
+    * modal: determine if the modal will be used
209
+    * marginTop: determine the marginTop of the root container view, it is used when the modal prop is false
210
+    * children: determine the children of loading-spinner-overlay
211
+    * duration: determine the duration of animation
212
+    * easing: determine the easing of animation
213
+    * delay: determine the delay of animation
214
+    * animationEnd: determine the callback when animation is end
215
+
216
+* hide({duration, easing, delay, animationEnd,})
217
+
218
+    * duration: determine the duration of animation
219
+    * easing: determine the easing of animation
220
+    * delay: determine the delay of animation
221
+    * animationEnd: determine the callback when animation is end
222
+
223
+[0]: https://github.com/cyqresig/ReactNativeComponentDemos
224
+[1]: http://cyqresig.github.io/img/react-native-smart-loading-spinner-overlay-preview-ios-v1.0.0.gif
225
+[2]: http://cyqresig.github.io/img/react-native-smart-loading-spinner-overlay-preview-android-v1.0.0.gif
226
+[3]: https://facebook.github.io/react-native/docs/style.html
227
+[4]: http://facebook.github.io/react-native/docs/text.html#style

+ 8
- 0
note.md View File

@@ -0,0 +1,8 @@
1
+
2
+* 需要保证组件为单例, 在显示期间再次调用显示时, 不会产生冲突
3
+* 需要支持完全模态(屏幕可视区域无法点击),
4
+  部分模态(部分区域比如导航栏可以点击),
5
+  非模态(除了加载指示器区域外都可以点击)
6
+* 需要保证上述三种类型, 加载指示器区域显示时相对屏幕可视区域水平及垂直居中
7
+* 需要支持加载指示器区域及内容可自定义样式和内容
8
+

+ 29
- 0
package.json View File

@@ -0,0 +1,29 @@
1
+{
2
+  "name": "react-native-smart-loading-spinner-overlay",
3
+  "version": "1.0.0",
4
+  "description": "A smart loading spinner overlay for React Native apps, written in JS for cross-platform support. It works on iOS and Android.",
5
+  "main": "LoadingSpinnerOverlay.js",
6
+  "scripts": {
7
+    "test": "echo \"Error: no test specified\" && exit 1"
8
+  },
9
+  "repository": {
10
+    "type": "git",
11
+    "url": "git+https://github.com/react-native-component/react-native-smart-loading-spinner-overlay.git"
12
+  },
13
+  "keywords": [
14
+    "react-native",
15
+    "smart",
16
+    "loading",
17
+    "spinner",
18
+    "overlay"
19
+  ],
20
+  "author": "HISAME SHIZUMARU",
21
+  "license": "MIT",
22
+  "bugs": {
23
+    "url": "https://github.com/react-native-component/react-native-smart-loading-spinner-overlay/issues"
24
+  },
25
+  "homepage": "https://github.com/react-native-component/react-native-smart-loading-spinner-overlay#readme",
26
+  "dependencies": {
27
+    "react-native-smart-timer-enhance": "^1.0.2"
28
+  }
29
+}