提交自定义viewpager控件 review by 陈林
Showing
5 changed files
with
826 additions
and
3 deletions
js/common/components/YH_PtrRefresh.js
0 → 100644
1 | +/** | ||
2 | + * Description: | ||
3 | + * | ||
4 | + * Author: Bruce.Lu | ||
5 | + * Version: 1.0 | ||
6 | + * Created on 2017/2/23. | ||
7 | + */ | ||
8 | +import React from 'react'; | ||
9 | +import ReactNative from 'react-native'; | ||
10 | + | ||
11 | +let { | ||
12 | + requireNativeComponent, | ||
13 | + View | ||
14 | +} = ReactNative; | ||
15 | + | ||
16 | +let YH_PtrRefreshView = requireNativeComponent('YH_PtrRefresh', null); | ||
17 | + | ||
18 | +export default class YH_PtrRefresh extends React.Component { | ||
19 | + | ||
20 | + static propTypes = { | ||
21 | + ...View.propTypes // 包含默认的View的属性 | ||
22 | + }; | ||
23 | + | ||
24 | + constructor(props) { | ||
25 | + super(props); | ||
26 | + } | ||
27 | + | ||
28 | + render() { | ||
29 | + return <YH_PtrRefreshView {...this.props} data={this.props.data} />; | ||
30 | + } | ||
31 | +} |
js/common/components/YH_ViewPager.js
0 → 100644
1 | +/** | ||
2 | + * Description: | ||
3 | + * | ||
4 | + * Author: Bruce.Lu | ||
5 | + * Version: 1.0 | ||
6 | + * Created on 2017/2/23. | ||
7 | + */ | ||
8 | +import React from 'react'; | ||
9 | +import ReactNative from 'react-native'; | ||
10 | + | ||
11 | +let { | ||
12 | + requireNativeComponent, | ||
13 | + View, | ||
14 | +} = ReactNative; | ||
15 | +var dismissKeyboard = require('dismissKeyboard'); | ||
16 | + | ||
17 | +let YH_ViewPagerView = requireNativeComponent('YH_ViewPager', null); | ||
18 | + | ||
19 | +var UIManager = require('UIManager'); | ||
20 | + | ||
21 | +type Event = Object; | ||
22 | +var VIEWPAGER_REF = 'viewPager'; | ||
23 | + | ||
24 | +export type ViewPagerScrollState = $Enum<{ | ||
25 | + idle: string, | ||
26 | + dragging: string, | ||
27 | + settling: string, | ||
28 | +}>; | ||
29 | + | ||
30 | +export default class YH_ViewPager extends React.Component { | ||
31 | + props: { | ||
32 | + initialPage?: number, | ||
33 | + onPageScroll?: Function, | ||
34 | + onPageScrollStateChanged?: Function, | ||
35 | + onPageSelected?: Function, | ||
36 | + pageMargin?: number, | ||
37 | + keyboardDismissMode?: 'none' | 'on-drag', | ||
38 | + scrollEnabled?: boolean, | ||
39 | + }; | ||
40 | + | ||
41 | + static propTypes = { | ||
42 | + ...View.propTypes, | ||
43 | + initialPage: React.PropTypes.number, | ||
44 | + onPageScroll: React.PropTypes.func, | ||
45 | + onPageScrollStateChanged: React.PropTypes.func, | ||
46 | + onPageSelected: React.PropTypes.func, | ||
47 | + pageMargin: React.PropTypes.number, | ||
48 | + keyboardDismissMode: React.PropTypes.oneOf([ | ||
49 | + 'none', // default | ||
50 | + 'on-drag', | ||
51 | + ]), | ||
52 | + scrollEnabled: React.PropTypes.bool, | ||
53 | + }; | ||
54 | + | ||
55 | + constructor(props) { | ||
56 | + super(props); | ||
57 | + } | ||
58 | + | ||
59 | + componentDidMount() { | ||
60 | + if (this.props.initialPage != null) { | ||
61 | + this.setPageWithoutAnimation(this.props.initialPage); | ||
62 | + } | ||
63 | + } | ||
64 | + | ||
65 | + _childrenWithOverridenStyle = (): Array => { | ||
66 | + // Override styles so that each page will fill the parent. Native component | ||
67 | + // will handle positioning of elements, so it's not important to offset | ||
68 | + // them correctly. | ||
69 | + return React.Children.map(this.props.children, function(child) { | ||
70 | + if (!child) { | ||
71 | + return null; | ||
72 | + } | ||
73 | + var newProps = { | ||
74 | + ...child.props, | ||
75 | + style: [child.props.style, { | ||
76 | + position: 'absolute', | ||
77 | + left: 0, | ||
78 | + top: 0, | ||
79 | + right: 0, | ||
80 | + bottom: 0, | ||
81 | + width: undefined, | ||
82 | + height: undefined, | ||
83 | + }], | ||
84 | + collapsable: false, | ||
85 | + }; | ||
86 | + if (child.type && | ||
87 | + child.type.displayName && | ||
88 | + (child.type.displayName !== 'RCTView') && | ||
89 | + (child.type.displayName !== 'View')) { | ||
90 | + console.warn('Each ViewPager child must be a <View>. Was ' + child.type.displayName); | ||
91 | + } | ||
92 | + return React.createElement(child.type, newProps); | ||
93 | + }); | ||
94 | + }; | ||
95 | + | ||
96 | + _onPageScroll = (e: Event) => { | ||
97 | + if (this.props.onPageScroll) { | ||
98 | + this.props.onPageScroll(e); | ||
99 | + } | ||
100 | + if (this.props.keyboardDismissMode === 'on-drag') { | ||
101 | + dismissKeyboard(); | ||
102 | + } | ||
103 | + }; | ||
104 | + | ||
105 | + _onPageScrollStateChanged = (e: Event) => { | ||
106 | + if (this.props.onPageScrollStateChanged) { | ||
107 | + this.props.onPageScrollStateChanged(e.nativeEvent.pageScrollState); | ||
108 | + } | ||
109 | + }; | ||
110 | + | ||
111 | + _onPageSelected = (e: Event) => { | ||
112 | + if (this.props.onPageSelected) { | ||
113 | + this.props.onPageSelected(e); | ||
114 | + } | ||
115 | + }; | ||
116 | + | ||
117 | + setPage = (selectedPage: number) => { | ||
118 | + UIManager.dispatchViewManagerCommand( | ||
119 | + ReactNative.findNodeHandle(this), | ||
120 | + UIManager.AndroidViewPager.Commands.setPage, | ||
121 | + [selectedPage], | ||
122 | + ); | ||
123 | + }; | ||
124 | + | ||
125 | + setPageWithoutAnimation = (selectedPage: number) => { | ||
126 | + UIManager.dispatchViewManagerCommand( | ||
127 | + ReactNative.findNodeHandle(this), | ||
128 | + UIManager.AndroidViewPager.Commands.setPageWithoutAnimation, | ||
129 | + [selectedPage], | ||
130 | + ); | ||
131 | + }; | ||
132 | + | ||
133 | + | ||
134 | + render() { | ||
135 | + return (<YH_ViewPagerView | ||
136 | + {...this.props} | ||
137 | + ref={VIEWPAGER_REF} | ||
138 | + style={this.props.style} | ||
139 | + onPageScroll={this._onPageScroll} | ||
140 | + onPageScrollStateChanged={this._onPageScrollStateChanged} | ||
141 | + onPageSelected={this._onPageSelected} | ||
142 | + children={this._childrenWithOverridenStyle()} | ||
143 | + />); | ||
144 | + } | ||
145 | +} |
js/common/recycler-swiper/recyclerswiper.js
0 → 100644
js/common/recycler-swiper/src/index.js
0 → 100644
1 | +/** | ||
2 | + * react-native-swiper | ||
3 | + * @author leecade<leecade@163.com> | ||
4 | + */ | ||
5 | +import React, { Component, PropTypes } from 'react' | ||
6 | +import { | ||
7 | + Text, | ||
8 | + View, | ||
9 | + ScrollView, | ||
10 | + Dimensions, | ||
11 | + TouchableOpacity, | ||
12 | + ViewPagerAndroid, | ||
13 | + Platform, | ||
14 | + ActivityIndicator | ||
15 | +} from 'react-native' | ||
16 | + | ||
17 | +import YH_ViewPager from '../../components/YH_ViewPager' | ||
18 | + | ||
19 | +const { width, height } = Dimensions.get('window') | ||
20 | + | ||
21 | +/** | ||
22 | + * Default styles | ||
23 | + * @type {StyleSheetPropType} | ||
24 | + */ | ||
25 | +const styles = { | ||
26 | + container: { | ||
27 | + backgroundColor: 'transparent', | ||
28 | + position: 'relative' | ||
29 | + }, | ||
30 | + | ||
31 | + wrapper: { | ||
32 | + backgroundColor: 'transparent' | ||
33 | + }, | ||
34 | + | ||
35 | + slide: { | ||
36 | + backgroundColor: 'transparent' | ||
37 | + }, | ||
38 | + | ||
39 | + pagination_x: { | ||
40 | + position: 'absolute', | ||
41 | + bottom: 25, | ||
42 | + left: 0, | ||
43 | + right: 0, | ||
44 | + flexDirection: 'row', | ||
45 | + flex: 1, | ||
46 | + justifyContent: 'center', | ||
47 | + alignItems: 'center', | ||
48 | + backgroundColor: 'transparent' | ||
49 | + }, | ||
50 | + | ||
51 | + pagination_y: { | ||
52 | + position: 'absolute', | ||
53 | + right: 15, | ||
54 | + top: 0, | ||
55 | + bottom: 0, | ||
56 | + flexDirection: 'column', | ||
57 | + flex: 1, | ||
58 | + justifyContent: 'center', | ||
59 | + alignItems: 'center', | ||
60 | + backgroundColor: 'transparent' | ||
61 | + }, | ||
62 | + | ||
63 | + title: { | ||
64 | + height: 30, | ||
65 | + justifyContent: 'center', | ||
66 | + position: 'absolute', | ||
67 | + paddingLeft: 10, | ||
68 | + bottom: -30, | ||
69 | + left: 0, | ||
70 | + flexWrap: 'nowrap', | ||
71 | + width: 250, | ||
72 | + backgroundColor: 'transparent' | ||
73 | + }, | ||
74 | + | ||
75 | + buttonWrapper: { | ||
76 | + backgroundColor: 'transparent', | ||
77 | + flexDirection: 'row', | ||
78 | + position: 'absolute', | ||
79 | + top: 0, | ||
80 | + left: 0, | ||
81 | + flex: 1, | ||
82 | + paddingHorizontal: 10, | ||
83 | + paddingVertical: 10, | ||
84 | + justifyContent: 'space-between', | ||
85 | + alignItems: 'center' | ||
86 | + }, | ||
87 | + | ||
88 | + buttonText: { | ||
89 | + fontSize: 50, | ||
90 | + color: '#007aff', | ||
91 | + fontFamily: 'Arial' | ||
92 | + } | ||
93 | +} | ||
94 | + | ||
95 | +// missing `module.exports = exports['default'];` with babel6 | ||
96 | +// export default React.createClass({ | ||
97 | +export default class extends Component { | ||
98 | + /** | ||
99 | + * Props Validation | ||
100 | + * @type {Object} | ||
101 | + */ | ||
102 | + static propTypes = { | ||
103 | + horizontal: PropTypes.bool, | ||
104 | + children: PropTypes.node.isRequired, | ||
105 | + style: View.propTypes.style, | ||
106 | + pagingEnabled: PropTypes.bool, | ||
107 | + showsHorizontalScrollIndicator: PropTypes.bool, | ||
108 | + showsVerticalScrollIndicator: PropTypes.bool, | ||
109 | + bounces: PropTypes.bool, | ||
110 | + scrollsToTop: PropTypes.bool, | ||
111 | + removeClippedSubviews: PropTypes.bool, | ||
112 | + automaticallyAdjustContentInsets: PropTypes.bool, | ||
113 | + showsPagination: PropTypes.bool, | ||
114 | + showsButtons: PropTypes.bool, | ||
115 | + loadMinimal: PropTypes.bool, | ||
116 | + loadMinimalSize: PropTypes.number, | ||
117 | + loadMinimalLoader: PropTypes.element, | ||
118 | + loop: PropTypes.bool, | ||
119 | + autoplay: PropTypes.bool, | ||
120 | + autoplayTimeout: PropTypes.number, | ||
121 | + autoplayDirection: PropTypes.bool, | ||
122 | + index: PropTypes.number, | ||
123 | + renderPagination: PropTypes.func, | ||
124 | + dotStyle: PropTypes.object, | ||
125 | + activeDotStyle: PropTypes.object, | ||
126 | + dotColor: PropTypes.string, | ||
127 | + activeDotColor: PropTypes.string | ||
128 | + } | ||
129 | + | ||
130 | + /** | ||
131 | + * Default props | ||
132 | + * @return {object} props | ||
133 | + * @see http://facebook.github.io/react-native/docs/scrollview.html | ||
134 | + */ | ||
135 | + static defaultProps = { | ||
136 | + horizontal: true, | ||
137 | + pagingEnabled: true, | ||
138 | + showsHorizontalScrollIndicator: false, | ||
139 | + showsVerticalScrollIndicator: false, | ||
140 | + bounces: false, | ||
141 | + scrollsToTop: false, | ||
142 | + removeClippedSubviews: true, | ||
143 | + automaticallyAdjustContentInsets: false, | ||
144 | + showsPagination: true, | ||
145 | + showsButtons: false, | ||
146 | + loop: true, | ||
147 | + loadMinimal: false, | ||
148 | + loadMinimalSize: 1, | ||
149 | + autoplay: false, | ||
150 | + autoplayTimeout: 2.5, | ||
151 | + autoplayDirection: true, | ||
152 | + index: 0 | ||
153 | + } | ||
154 | + | ||
155 | + /** | ||
156 | + * Init states | ||
157 | + * @return {object} states | ||
158 | + */ | ||
159 | + state = this.initState(this.props, true) | ||
160 | + | ||
161 | + /** | ||
162 | + * autoplay timer | ||
163 | + * @type {null} | ||
164 | + */ | ||
165 | + autoplayTimer = null | ||
166 | + loopJumpTimer = null | ||
167 | + | ||
168 | + componentWillReceiveProps (nextProps) { | ||
169 | + const sizeChanged = (nextProps.width || width) !== this.state.width || | ||
170 | + (nextProps.height || height) !== this.state.height | ||
171 | + if (!nextProps.autoplay && this.autoplayTimer) clearTimeout(this.autoplayTimer) | ||
172 | + this.setState(this.initState(nextProps, sizeChanged)) | ||
173 | + } | ||
174 | + | ||
175 | + componentDidMount () { | ||
176 | + this.autoplay() | ||
177 | + } | ||
178 | + | ||
179 | + componentWillUnmount () { | ||
180 | + this.autoplayTimer && clearTimeout(this.autoplayTimer) | ||
181 | + this.loopJumpTimer && clearTimeout(this.loopJumpTimer) | ||
182 | + } | ||
183 | + | ||
184 | + initState (props, setOffsetInState) { | ||
185 | + // set the current state | ||
186 | + const state = this.state || {} | ||
187 | + | ||
188 | + const initState = { | ||
189 | + autoplayEnd: false, | ||
190 | + loopJump: false | ||
191 | + } | ||
192 | + | ||
193 | + const newInternals = { | ||
194 | + isScrolling: false | ||
195 | + } | ||
196 | + | ||
197 | + initState.total = props.children ? props.children.length || 1 : 0 | ||
198 | + if (state.total === initState.total) { | ||
199 | + // retain the index | ||
200 | + initState.index = state.index | ||
201 | + } else { | ||
202 | + // reset the index | ||
203 | + setOffsetInState = true // if the index is reset, go ahead and update the offset in state | ||
204 | + initState.index = initState.total > 1 ? Math.min(props.index, initState.total - 1) : 0 | ||
205 | + } | ||
206 | + | ||
207 | + // Default: horizontal | ||
208 | + initState.dir = props.horizontal === false ? 'y' : 'x' | ||
209 | + initState.width = props.width || width | ||
210 | + initState.height = props.height || height | ||
211 | + newInternals.offset = {} | ||
212 | + | ||
213 | + if (initState.total > 1) { | ||
214 | + let setup = initState.index | ||
215 | + if (props.loop) { | ||
216 | + setup++ | ||
217 | + } | ||
218 | + newInternals.offset[initState.dir] = initState.dir === 'y' | ||
219 | + ? initState.height * setup | ||
220 | + : initState.width * setup | ||
221 | + } | ||
222 | + | ||
223 | + // only update the offset in state if needed, updating offset while swiping | ||
224 | + // causes some bad jumping / stuttering | ||
225 | + if (setOffsetInState) { | ||
226 | + initState.offset = newInternals.offset | ||
227 | + } | ||
228 | + | ||
229 | + this.internals = newInternals | ||
230 | + return initState | ||
231 | + } | ||
232 | + | ||
233 | + // include internals with state | ||
234 | + fullState () { | ||
235 | + return Object.assign({}, this.state, this.internals) | ||
236 | + } | ||
237 | + | ||
238 | + loopJump = () => { | ||
239 | + if (!this.state.loopJump) return | ||
240 | + const i = this.state.index + (this.props.loop ? 1 : 0) | ||
241 | + const scrollView = this.refs.scrollView | ||
242 | + this.loopJumpTimer = setTimeout(() => scrollView.setPageWithoutAnimation && | ||
243 | + scrollView.setPageWithoutAnimation(i), 50) | ||
244 | + } | ||
245 | + | ||
246 | + /** | ||
247 | + * Automatic rolling | ||
248 | + */ | ||
249 | + autoplay = () => { | ||
250 | + if (!Array.isArray(this.props.children) || | ||
251 | + !this.props.autoplay || | ||
252 | + this.internals.isScrolling || | ||
253 | + this.state.autoplayEnd) return | ||
254 | + | ||
255 | + this.autoplayTimer && clearTimeout(this.autoplayTimer) | ||
256 | + this.autoplayTimer = setTimeout(() => { | ||
257 | + if (!this.props.loop && ( | ||
258 | + this.props.autoplayDirection | ||
259 | + ? this.state.index === this.state.total - 1 | ||
260 | + : this.state.index === 0 | ||
261 | + ) | ||
262 | + ) return this.setState({ autoplayEnd: true }) | ||
263 | + | ||
264 | + this.scrollBy(this.props.autoplayDirection ? 1 : -1) | ||
265 | + }, this.props.autoplayTimeout * 1000) | ||
266 | + } | ||
267 | + | ||
268 | + /** | ||
269 | + * Scroll begin handle | ||
270 | + * @param {object} e native event | ||
271 | + */ | ||
272 | + onScrollBegin = e => { | ||
273 | + // update scroll state | ||
274 | + this.internals.isScrolling = true | ||
275 | + this.props.onScrollBeginDrag && this.props.onScrollBeginDrag(e, this.fullState(), this) | ||
276 | + } | ||
277 | + | ||
278 | + /** | ||
279 | + * Scroll end handle | ||
280 | + * @param {object} e native event | ||
281 | + */ | ||
282 | + onScrollEnd = e => { | ||
283 | + console.log('onPageSelected'); | ||
284 | + // update scroll state | ||
285 | + this.internals.isScrolling = false | ||
286 | + | ||
287 | + // making our events coming from android compatible to updateIndex logic | ||
288 | + if (!e.nativeEvent.contentOffset) { | ||
289 | + if (this.state.dir === 'x') { | ||
290 | + e.nativeEvent.contentOffset = {x: e.nativeEvent.position * this.state.width} | ||
291 | + } else { | ||
292 | + e.nativeEvent.contentOffset = {y: e.nativeEvent.position * this.state.height} | ||
293 | + } | ||
294 | + } | ||
295 | + | ||
296 | + this.updateIndex(e.nativeEvent.contentOffset, this.state.dir, () => { | ||
297 | + this.autoplay() | ||
298 | + this.loopJump() | ||
299 | + | ||
300 | + // if `onMomentumScrollEnd` registered will be called here | ||
301 | + this.props.onMomentumScrollEnd && this.props.onMomentumScrollEnd(e, this.fullState(), this) | ||
302 | + }) | ||
303 | + } | ||
304 | + | ||
305 | + /* | ||
306 | + * Drag end handle | ||
307 | + * @param {object} e native event | ||
308 | + */ | ||
309 | + onScrollEndDrag = e => { | ||
310 | + const { contentOffset } = e.nativeEvent | ||
311 | + const { horizontal, children } = this.props | ||
312 | + const { index } = this.state | ||
313 | + const { offset } = this.internals | ||
314 | + const previousOffset = horizontal ? offset.x : offset.y | ||
315 | + const newOffset = horizontal ? contentOffset.x : contentOffset.y | ||
316 | + | ||
317 | + if (previousOffset === newOffset && | ||
318 | + (index === 0 || index === children.length - 1)) { | ||
319 | + this.internals.isScrolling = false | ||
320 | + } | ||
321 | + } | ||
322 | + | ||
323 | + /** | ||
324 | + * Update index after scroll | ||
325 | + * @param {object} offset content offset | ||
326 | + * @param {string} dir 'x' || 'y' | ||
327 | + */ | ||
328 | + updateIndex = (offset, dir, cb) => { | ||
329 | + const state = this.state | ||
330 | + let index = state.index | ||
331 | + const diff = offset[dir] - this.internals.offset[dir] | ||
332 | + const step = dir === 'x' ? state.width : state.height | ||
333 | + let loopJump = false | ||
334 | + | ||
335 | + // Do nothing if offset no change. | ||
336 | + if (!diff) return | ||
337 | + | ||
338 | + // Note: if touch very very quickly and continuous, | ||
339 | + // the variation of `index` more than 1. | ||
340 | + // parseInt() ensures it's always an integer | ||
341 | + index = parseInt(index + Math.round(diff / step)) | ||
342 | + | ||
343 | + if (this.props.loop) { | ||
344 | + if (index <= -1) { | ||
345 | + index = state.total - 1 | ||
346 | + offset[dir] = step * state.total | ||
347 | + loopJump = true | ||
348 | + } else if (index >= state.total) { | ||
349 | + index = 0 | ||
350 | + offset[dir] = step | ||
351 | + loopJump = true | ||
352 | + } | ||
353 | + } | ||
354 | + | ||
355 | + const newState = {} | ||
356 | + newState.index = index | ||
357 | + newState.loopJump = loopJump | ||
358 | + | ||
359 | + this.internals.offset = offset | ||
360 | + | ||
361 | + // only update offset in state if loopJump is true | ||
362 | + if (loopJump) { | ||
363 | + // when swiping to the beginning of a looping set for the third time, | ||
364 | + // the new offset will be the same as the last one set in state. | ||
365 | + // Setting the offset to the same thing will not do anything, | ||
366 | + // so we increment it by 1 then immediately set it to what it should be, | ||
367 | + // after render. | ||
368 | + if (offset[dir] === this.state.offset[dir]) { | ||
369 | + newState.offset = { x: 0, y: 0 } | ||
370 | + newState.offset[dir] = offset[dir] + 1 | ||
371 | + this.setState(newState, () => { | ||
372 | + this.setState({ offset: offset }, cb) | ||
373 | + }) | ||
374 | + } else { | ||
375 | + newState.offset = offset | ||
376 | + this.setState(newState, cb) | ||
377 | + } | ||
378 | + } else { | ||
379 | + this.setState(newState, cb) | ||
380 | + } | ||
381 | + } | ||
382 | + | ||
383 | + /** | ||
384 | + * Scroll by index | ||
385 | + * @param {number} index offset index | ||
386 | + * @param {bool} animated | ||
387 | + */ | ||
388 | + | ||
389 | + scrollBy = (index, animated = true) => { | ||
390 | + if (this.internals.isScrolling || this.state.total < 2) return | ||
391 | + const state = this.state | ||
392 | + const diff = (this.props.loop ? 1 : 0) + index + this.state.index | ||
393 | + let x = 0 | ||
394 | + let y = 0 | ||
395 | + if (state.dir === 'x') x = diff * state.width | ||
396 | + if (state.dir === 'y') y = diff * state.height | ||
397 | + | ||
398 | + console.log('xxxxx scrollBy diff=' + diff); | ||
399 | + if (Platform.OS === 'android') { | ||
400 | + this.refs.scrollView && this.refs.scrollView[animated ? 'setPage' : 'setPageWithoutAnimation'](diff) | ||
401 | + } else { | ||
402 | + this.refs.scrollView && this.refs.scrollView.scrollTo({ x, y, animated }) | ||
403 | + } | ||
404 | + | ||
405 | + // update scroll state | ||
406 | + this.internals.isScrolling = true | ||
407 | + this.setState({ | ||
408 | + autoplayEnd: false | ||
409 | + }) | ||
410 | + | ||
411 | + // trigger onScrollEnd manually in android | ||
412 | + if (!animated || Platform.OS === 'android') { | ||
413 | + setImmediate(() => { | ||
414 | + this.onScrollEnd({ | ||
415 | + nativeEvent: { | ||
416 | + position: diff | ||
417 | + } | ||
418 | + }) | ||
419 | + }) | ||
420 | + } | ||
421 | + } | ||
422 | + | ||
423 | + scrollViewPropOverrides = () => { | ||
424 | + const props = this.props | ||
425 | + let overrides = {} | ||
426 | + | ||
427 | + /* | ||
428 | + const scrollResponders = [ | ||
429 | + 'onMomentumScrollBegin', | ||
430 | + 'onTouchStartCapture', | ||
431 | + 'onTouchStart', | ||
432 | + 'onTouchEnd', | ||
433 | + 'onResponderRelease', | ||
434 | + ] | ||
435 | + */ | ||
436 | + | ||
437 | + for (let prop in props) { | ||
438 | + // if(~scrollResponders.indexOf(prop) | ||
439 | + if (typeof props[prop] === 'function' && | ||
440 | + prop !== 'onMomentumScrollEnd' && | ||
441 | + prop !== 'renderPagination' && | ||
442 | + prop !== 'onScrollBeginDrag' | ||
443 | + ) { | ||
444 | + let originResponder = props[prop] | ||
445 | + overrides[prop] = (e) => originResponder(e, this.fullState(), this) | ||
446 | + } | ||
447 | + } | ||
448 | + | ||
449 | + return overrides | ||
450 | + } | ||
451 | + | ||
452 | + /** | ||
453 | + * Render pagination | ||
454 | + * @return {object} react-dom | ||
455 | + */ | ||
456 | + renderPagination = () => { | ||
457 | + // By default, dots only show when `total` >= 2 | ||
458 | + if (this.state.total <= 1) return null | ||
459 | + | ||
460 | + let dots = [] | ||
461 | + const ActiveDot = this.props.activeDot || <View style={[{ | ||
462 | + backgroundColor: this.props.activeDotColor || '#007aff', | ||
463 | + width: 8, | ||
464 | + height: 8, | ||
465 | + borderRadius: 4, | ||
466 | + marginLeft: 3, | ||
467 | + marginRight: 3, | ||
468 | + marginTop: 3, | ||
469 | + marginBottom: 3 | ||
470 | + }, this.props.activeDotStyle]} /> | ||
471 | + const Dot = this.props.dot || <View style={[{ | ||
472 | + backgroundColor: this.props.dotColor || 'rgba(0,0,0,.2)', | ||
473 | + width: 8, | ||
474 | + height: 8, | ||
475 | + borderRadius: 4, | ||
476 | + marginLeft: 3, | ||
477 | + marginRight: 3, | ||
478 | + marginTop: 3, | ||
479 | + marginBottom: 3 | ||
480 | + }, this.props.dotStyle ]} /> | ||
481 | + for (let i = 0; i < this.state.total; i++) { | ||
482 | + dots.push(i === this.state.index | ||
483 | + ? React.cloneElement(ActiveDot, {key: i}) | ||
484 | + : React.cloneElement(Dot, {key: i}) | ||
485 | + ) | ||
486 | + } | ||
487 | + | ||
488 | + return ( | ||
489 | + <View pointerEvents='none' style={[styles['pagination_' + this.state.dir], this.props.paginationStyle]}> | ||
490 | + {dots} | ||
491 | + </View> | ||
492 | + ) | ||
493 | + } | ||
494 | + | ||
495 | + renderTitle = () => { | ||
496 | + const child = this.props.children[this.state.index] | ||
497 | + const title = child && child.props && child.props.title | ||
498 | + return title | ||
499 | + ? (<View style={styles.title}> | ||
500 | + {this.props.children[this.state.index].props.title} | ||
501 | + </View>) | ||
502 | + : null | ||
503 | + } | ||
504 | + | ||
505 | + renderNextButton = () => { | ||
506 | + let button = null | ||
507 | + | ||
508 | + if (this.props.loop || | ||
509 | + this.state.index !== this.state.total - 1) { | ||
510 | + button = this.props.nextButton || <Text style={styles.buttonText}>›</Text> | ||
511 | + } | ||
512 | + | ||
513 | + return ( | ||
514 | + <TouchableOpacity onPress={() => button !== null && this.scrollBy(1)}> | ||
515 | + <View> | ||
516 | + {button} | ||
517 | + </View> | ||
518 | + </TouchableOpacity> | ||
519 | + ) | ||
520 | + } | ||
521 | + | ||
522 | + renderPrevButton = () => { | ||
523 | + let button = null | ||
524 | + | ||
525 | + if (this.props.loop || this.state.index !== 0) { | ||
526 | + button = this.props.prevButton || <Text style={styles.buttonText}>‹</Text> | ||
527 | + } | ||
528 | + | ||
529 | + return ( | ||
530 | + <TouchableOpacity onPress={() => button !== null && this.scrollBy(-1)}> | ||
531 | + <View> | ||
532 | + {button} | ||
533 | + </View> | ||
534 | + </TouchableOpacity> | ||
535 | + ) | ||
536 | + } | ||
537 | + | ||
538 | + renderButtons = () => { | ||
539 | + return ( | ||
540 | + <View pointerEvents='box-none' style={[styles.buttonWrapper, { | ||
541 | + width: this.state.width, | ||
542 | + height: this.state.height | ||
543 | + }, this.props.buttonWrapperStyle]}> | ||
544 | + {this.renderPrevButton()} | ||
545 | + {this.renderNextButton()} | ||
546 | + </View> | ||
547 | + ) | ||
548 | + } | ||
549 | + | ||
550 | + renderScrollView = pages => { | ||
551 | + if (Platform.OS === 'ios') { | ||
552 | + return ( | ||
553 | + <ScrollView ref='scrollView' | ||
554 | + {...this.props} | ||
555 | + {...this.scrollViewPropOverrides()} | ||
556 | + contentContainerStyle={[styles.wrapper, this.props.style]} | ||
557 | + contentOffset={this.state.offset} | ||
558 | + onScrollBeginDrag={this.onScrollBegin} | ||
559 | + onMomentumScrollEnd={this.onScrollEnd} | ||
560 | + onScrollEndDrag={this.onScrollEndDrag}> | ||
561 | + {pages} | ||
562 | + </ScrollView> | ||
563 | + ) | ||
564 | + } | ||
565 | + return ( | ||
566 | + <YH_ViewPager ref='scrollView' | ||
567 | + {...this.props} | ||
568 | + initialPage={this.props.loop ? this.state.index + 1 : this.state.index} | ||
569 | + onPageSelected={this.onScrollEnd} | ||
570 | + style={{flex: 1}}> | ||
571 | + {pages} | ||
572 | + </YH_ViewPager> | ||
573 | + ) | ||
574 | + } | ||
575 | + | ||
576 | + /** | ||
577 | + * Default render | ||
578 | + * @return {object} react-dom | ||
579 | + */ | ||
580 | + render () { | ||
581 | + const state = this.state | ||
582 | + const props = this.props | ||
583 | + const children = props.children | ||
584 | + const index = state.index | ||
585 | + const total = state.total | ||
586 | + const loop = props.loop | ||
587 | + // let dir = state.dir | ||
588 | + // let key = 0 | ||
589 | + const loopVal = loop ? 1 : 0 | ||
590 | + | ||
591 | + let pages = [] | ||
592 | + | ||
593 | + const pageStyle = [{width: state.width, height: state.height}, styles.slide] | ||
594 | + const pageStyleLoading = { | ||
595 | + width: this.state.width, | ||
596 | + height: this.state.height, | ||
597 | + justifyContent: 'center', | ||
598 | + alignItems: 'center' | ||
599 | + } | ||
600 | + | ||
601 | + // For make infinite at least total > 1 | ||
602 | + if (total > 1) { | ||
603 | + // Re-design a loop model for avoid img flickering | ||
604 | + pages = Object.keys(children) | ||
605 | + if (loop) { | ||
606 | + pages.unshift(total - 1 + '') | ||
607 | + pages.push('0') | ||
608 | + } | ||
609 | + console.log(pages); | ||
610 | + | ||
611 | + pages = pages.map((page, i) => { | ||
612 | + if (props.loadMinimal) { | ||
613 | + if (i >= (index + loopVal - props.loadMinimalSize) && | ||
614 | + i <= (index + loopVal + props.loadMinimalSize)) { | ||
615 | + return <View style={pageStyle} key={i}>{children[page]}</View> | ||
616 | + } else { | ||
617 | + return ( | ||
618 | + <View style={pageStyleLoading} key={`loading-${i}`}> | ||
619 | + {props.loadMinimalLoader ? props.loadMinimalLoader : <ActivityIndicator />} | ||
620 | + </View> | ||
621 | + ) | ||
622 | + } | ||
623 | + } else { | ||
624 | + return <View style={pageStyle} key={i}>{children[page]}</View> | ||
625 | + } | ||
626 | + }) | ||
627 | + } else { | ||
628 | + pages = <View style={pageStyle} key={0}>{children}</View> | ||
629 | + } | ||
630 | + | ||
631 | + return ( | ||
632 | + <View style={[styles.container, { | ||
633 | + width: state.width, | ||
634 | + height: state.height | ||
635 | + }]}> | ||
636 | + {this.renderScrollView(pages)} | ||
637 | + {props.showsPagination && (props.renderPagination | ||
638 | + ? this.props.renderPagination(state.index, state.total, this) | ||
639 | + : this.renderPagination())} | ||
640 | + {this.renderTitle()} | ||
641 | + {this.props.showsButtons && this.renderButtons()} | ||
642 | + </View> | ||
643 | + ) | ||
644 | + } | ||
645 | +} |
@@ -54,7 +54,7 @@ function calcluateFloorHeight(payload) { | @@ -54,7 +54,7 @@ function calcluateFloorHeight(payload) { | ||
54 | let padding = count == 4 ? padding4 : padding5; | 54 | let padding = count == 4 ? padding4 : padding5; |
55 | floorHeight = (count == 4 ? 84 : 75) * lineNumber + padding * (lineNumber - 1) + 12; | 55 | floorHeight = (count == 4 ? 84 : 75) * lineNumber + padding * (lineNumber - 1) + 12; |
56 | break; | 56 | break; |
57 | - } | 57 | + } |
58 | case 'app_hot_brands': { | 58 | case 'app_hot_brands': { |
59 | let listLength = data.list.length; | 59 | let listLength = data.list.length; |
60 | let lineNumber = parseInt((listLength + 3) / 4); | 60 | let lineNumber = parseInt((listLength + 3) / 4); |
@@ -171,7 +171,7 @@ function calcluateFloorHeight(payload) { | @@ -171,7 +171,7 @@ function calcluateFloorHeight(payload) { | ||
171 | let leftBigImageWidth = width * 310 / 740; | 171 | let leftBigImageWidth = width * 310 / 740; |
172 | let leftBigImageHeight = leftBigImageWidth * 422 / 310; | 172 | let leftBigImageHeight = leftBigImageWidth * 422 / 310; |
173 | let containerHeight = headerHeight + leftBigImageHeight; | 173 | let containerHeight = headerHeight + leftBigImageHeight; |
174 | - if(item.big_image.length > 0){ | 174 | + if(data.big_image.length > 0){ |
175 | containerHeight = containerHeight + bannerHeight; | 175 | containerHeight = containerHeight + bannerHeight; |
176 | } | 176 | } |
177 | floorHeight = containerHeight; | 177 | floorHeight = containerHeight; |
@@ -210,7 +210,7 @@ function calcluateFloorHeight(payload) { | @@ -210,7 +210,7 @@ function calcluateFloorHeight(payload) { | ||
210 | let imageWidth = cellWidth - 14 * 2; | 210 | let imageWidth = cellWidth - 14 * 2; |
211 | let imageHeight = imageWidth * 168 / 126; | 211 | let imageHeight = imageWidth * 168 / 126; |
212 | let cellHeight = imageHeight + 20; | 212 | let cellHeight = imageHeight + 20; |
213 | - let lineNumber = parseInt((item.list.length + 3) / 4); | 213 | + let lineNumber = parseInt((data.list.length + 3) / 4); |
214 | let listHeight = Math.floor(lineNumber * cellHeight); | 214 | let listHeight = Math.floor(lineNumber * cellHeight); |
215 | let containerHeight = listHeight + headerHeight; | 215 | let containerHeight = listHeight + headerHeight; |
216 | floorHeight = containerHeight; | 216 | floorHeight = containerHeight; |
-
Please register or login to post a comment