UNPKG

citong-react-web

Version:

A framework for building web apps with React

276 lines (235 loc) 9.47 kB
/** * Copyright (c) 2015-present, Alibaba Group Holding Limited. * All rights reserved. * * Copyright (c) 2015, Facebook, Inc. All rights reserved. * * @providesModule ReactScrollView */ 'use strict'; import React, { PropTypes, Component} from 'react'; import ReactDOM from 'react-dom'; import ScrollResponder from 'ReactScrollResponder'; import StyleSheet from 'ReactStyleSheet'; import View from 'ReactView'; import throttle from 'domkit/throttle'; import mixin from 'react-mixin'; import autobind from 'autobind-decorator'; const SCROLLVIEW = 'ScrollView'; const INNERVIEW = 'InnerScrollView'; const CONTENT_EXT_STYLE = ['padding', 'paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight']; /** * Component that wraps platform ScrollView while providing * integration with touch locking "responder" system. * * Keep in mind that ScrollViews must have a bounded height in order to work, * since they contain unbounded-height children into a bounded container (via * a scroll interaction). In order to bound the height of a ScrollView, either * set the height of the view directly (discouraged) or make sure all parent * views have bounded height. Forgetting to transfer `{flex: 1}` down the * view stack can lead to errors here, which the element inspector makes * easy to debug. * * Doesn't yet support other contained responders from blocking this scroll * view from becoming the responder. */ class ScrollView extends Component { state = this.scrollResponderMixinGetInitialState(); componentWillUnmount() { if (this._aniFrame) { window.cancelAnimationFrame(this._aniFrame); this._aniFrame = null; } } /** * Returns a reference to the underlying scroll responder, which supports * operations like `scrollTo`. All ScrollView-like components should * implement this method so that they can be composed while providing access * to the underlying scroll responder's methods. */ getScrollResponder() { return this; } getInnerViewNode() { return this.refs[INNERVIEW]; } scrollTo(opts) { // $FlowFixMe - Don't know how to pass Mixin correctly. Postpone for now // this.getScrollResponder().scrollResponderScrollTo(destX || 0, destY || 0); if (typeof opts === 'number') { opts = { y: opts, x: arguments[1] }; } this._scrollViewDom = this._scrollViewDom||ReactDOM.findDOMNode(this.refs[SCROLLVIEW]); let srcX = this._scrollViewDom.scrollLeft; let srcY = this._scrollViewDom.scrollTop; let stepX = (opts.x||0)-this._scrollViewDom.scrollLeft; let stepY = (opts.y||0)-this._scrollViewDom.scrollTop; let now = Date.now(); const durtion = 100; const stepFoo = (timestamp)=>{ let now2 = Date.now(); if (now2 - now < durtion && now2 - now > 0) { let s = (now2 - now) / durtion; this._scrollViewDom.scrollLeft = srcX + stepX*s; this._scrollViewDom.scrollTop = srcY + stepY*s; this._aniFrame = window.requestAnimationFrame(stepFoo); } else { this._scrollViewDom.scrollLeft = (opts.x||0); this._scrollViewDom.scrollTop = (opts.y||0); this._aniFrame = null; } }; this._aniFrame = window.requestAnimationFrame(stepFoo); // this.state.__offset.setValue({ // y: this._scrollViewDom.scrollTop, // x: this._scrollViewDom.scrollLeft // }); // this.state.__scrolling = true; // Animated.spring(this.state.__offset, { // toValue: {x:-(opts.x||0),y:-(opts.y||0)}, // bounciness: 0, // restSpeedThreshold: 1 // }).start(()=>{ // // this.setState({__scrolling:false}, ()=> { // this._scrollViewDom = ReactDOM.findDOMNode(this.refs[SCROLLVIEW]); // this._scrollViewDom.scrollTop = -(opts.x||0); // this._scrollViewDom.scrollLeft = -(opts.y||0); // // }); // }); //this.scrollWithoutAnimationTo(opts.y, opts.x); } scrollWithoutAnimationTo(destY?: number, destX?: number) { // $FlowFixMe - Don't know how to pass Mixin correctly. Postpone for now // this.getScrollResponder().scrollResponderScrollWithouthAnimationTo( // destX || 0, // destY || 0, // ); this.state.__offset.setValue({x:-(destX || 0), y:-(destY || 0)}); // this._scrollViewDom = ReactDOM.findDOMNode(this.refs[SCROLLVIEW]); // this._scrollViewDom.scrollTop = destY || 0; // this._scrollViewDom.scrollLeft = destX || 0; } handleScroll(e: Event) { // if (__DEV__) { // if (this.props.onScroll && !this.props.scrollEventThrottle) { // console.log( // 'You specified `onScroll` on a <ScrollView> but not ' + // '`scrollEventThrottle`. You will only receive one event. ' + // 'Using `16` you get all the events but be aware that it may ' + // 'cause frame drops, use a bigger number if you don\'t need as ' + // 'much precision.' // ); // } // } // if (Platform.OS === 'android') { // if (this.props.keyboardDismissMode === 'on-drag') { // dismissKeyboard(); // } // } if (!this._scrollViewDom) this._scrollViewDom = ReactDOM.findDOMNode(this.refs[SCROLLVIEW]); e.nativeEvent = e.nativeEvent || {}; e.nativeEvent.contentOffset = {x:this._scrollViewDom.scrollLeft, y:this._scrollViewDom.scrollTop}; this.props.onScroll && this.props.onScroll(e); } render() { let { style, ...otherProps } = this.props; let contentContainerExtStyle = {}; if (style) { for (let i = 0; i < CONTENT_EXT_STYLE.length; i++) { if (typeof style[CONTENT_EXT_STYLE[i]] === 'number') { contentContainerExtStyle[CONTENT_EXT_STYLE[i]] = style[CONTENT_EXT_STYLE[i]]; } } } let contentContainerStyle = [ styles.contentContainer, this.props.horizontal && styles.contentContainerHorizontal, this.props.contentContainerStyle, contentContainerExtStyle, ]; // if (__DEV__ && this.props.style) { // let style = flattenStyle(this.props.style); // let childLayoutProps = ['alignItems', 'justifyContent'] // .filter((prop) => style && style[prop] !== undefined); // invariant( // childLayoutProps.length === 0, // 'ScrollView child layout (' + JSON.stringify(childLayoutProps) + // ') must by applied through the contentContainerStyle prop.' // ); // } let contentContainer = <View ref={INNERVIEW} style={contentContainerStyle} removeClippedSubviews={this.props.removeClippedSubviews} collapsable={false} > {this.props.children} </View>; let alwaysBounceHorizontal = this.props.alwaysBounceHorizontal !== undefined ? this.props.alwaysBounceHorizontal : this.props.horizontal; let alwaysBounceVertical = this.props.alwaysBounceVertical !== undefined ? this.props.alwaysBounceVertical : !this.props.horizontal; let handleScroll = () => {}; if (this.props.scrollEventThrottle && this.props.onScroll) { handleScroll = throttle(this.handleScroll, this.props.scrollEventThrottle ? 1000/this.props.scrollEventThrottle : 1000); } let props = { ...otherProps, alwaysBounceHorizontal, alwaysBounceVertical, style: ([styles.base, this.props.style]: ?Array<any>), onTouchStart: this.scrollResponderHandleTouchStart, onTouchMove: this.scrollResponderHandleTouchMove, onTouchEnd: this.scrollResponderHandleTouchEnd, onScrollBeginDrag: this.scrollResponderHandleScrollBeginDrag, onScrollEndDrag: this.scrollResponderHandleScrollEndDrag, onMomentumScrollBegin: this.scrollResponderHandleMomentumScrollBegin, onMomentumScrollEnd: this.scrollResponderHandleMomentumScrollEnd, onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder, onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture, // onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder, // onScroll: handleScroll, onScrollShouldSetResponder: handleScroll, // replace onScroll in the props onScroll: () => {}, onResponderGrant: this.scrollResponderHandleResponderGrant, onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest, onResponderTerminate: this.scrollResponderHandleTerminate, onResponderRelease: this.scrollResponderHandleResponderRelease, onResponderReject: this.scrollResponderHandleResponderReject, }; return ( <View {...props} ref={SCROLLVIEW}> {contentContainer} </View> ); } }; let styles = StyleSheet.create({ base: { overflow: 'scroll', WebkitOverflowScrolling: 'touch', flex: 1, }, contentContainer: { position: 'absolute', minWidth: '100%', }, contentContainerHorizontal: { alignSelf: 'flex-start', flexDirection: 'row', }, }); mixin.onClass(ScrollView, ScrollResponder.Mixin); autobind(ScrollView); ScrollView.isReactNativeComponent = true; export default ScrollView;