mirror of
https://github.com/zoriya/react-native-web.git
synced 2026-05-30 09:19:21 +00:00
[fix] ScrollView to use 'touch-action' to disable scroll
The recommendation from the Chrome team is to use 'touch-action' to disable scrolling (via touch modality) when passive event listeners are in use. Close #563
This commit is contained in:
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import { Button, ScrollView, StyleSheet, Text, View } from 'react-native';
|
import { Button, ScrollView, StyleSheet, Text, TouchableHighlight, View } from 'react-native';
|
||||||
|
|
||||||
export default class ScrollToExample extends PureComponent {
|
export default class ScrollToExample extends PureComponent {
|
||||||
render() {
|
render() {
|
||||||
@@ -20,11 +20,15 @@ export default class ScrollToExample extends PureComponent {
|
|||||||
style={styles.scrollViewStyle}
|
style={styles.scrollViewStyle}
|
||||||
>
|
>
|
||||||
{Array.from({ length: 50 }).map((item, i) =>
|
{Array.from({ length: 50 }).map((item, i) =>
|
||||||
<View key={i} style={[styles.box, styles.horizontalBox]}>
|
<TouchableHighlight
|
||||||
|
key={i}
|
||||||
|
onPress={() => {}}
|
||||||
|
style={[styles.box, styles.horizontalBox]}
|
||||||
|
>
|
||||||
<Text>
|
<Text>
|
||||||
{i}
|
{i}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</TouchableHighlight>
|
||||||
)}
|
)}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -671,6 +671,11 @@ const stylePropTypes = [
|
|||||||
name: 'shadowRadius',
|
name: 'shadowRadius',
|
||||||
typeInfo: 'number | string'
|
typeInfo: 'number | string'
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: 'web',
|
||||||
|
name: 'touchAction',
|
||||||
|
typeInfo: 'string'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'top',
|
name: 'top',
|
||||||
typeInfo: 'number | string'
|
typeInfo: 'number | string'
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import debounce from 'debounce';
|
import debounce from 'debounce';
|
||||||
import findNodeHandle from '../../modules/findNodeHandle';
|
import StyleSheet from '../../apis/StyleSheet';
|
||||||
import View from '../View';
|
import View from '../View';
|
||||||
import ViewPropTypes from '../View/ViewPropTypes';
|
import ViewPropTypes from '../View/ViewPropTypes';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
@@ -72,41 +72,22 @@ export default class ScrollViewBase extends Component {
|
|||||||
|
|
||||||
_debouncedOnScrollEnd = debounce(this._handleScrollEnd, 100);
|
_debouncedOnScrollEnd = debounce(this._handleScrollEnd, 100);
|
||||||
_state = { isScrolling: false, scrollLastTick: 0 };
|
_state = { isScrolling: false, scrollLastTick: 0 };
|
||||||
_node = null;
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this._node && this._node.addEventListener('scroll', this._handleScroll);
|
|
||||||
this._node && this._node.addEventListener('touchmove', this._handlePreventableTouchMove);
|
|
||||||
this._node && this._node.addEventListener('wheel', this._handlePreventableWheel);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
this._node && this._node.removeEventListener('scroll', this._handleScroll);
|
|
||||||
this._node && this._node.removeEventListener('touchmove', this._handlePreventableTouchMove);
|
|
||||||
this._node && this._node.removeEventListener('wheel', this._handlePreventableWheel);
|
|
||||||
}
|
|
||||||
|
|
||||||
_createPreventableScrollHandler = (handler: Function) => {
|
_createPreventableScrollHandler = (handler: Function) => {
|
||||||
return (e: Object) => {
|
return (e: Object) => {
|
||||||
if (!this.props.scrollEnabled) {
|
if (this.props.scrollEnabled) {
|
||||||
e.preventDefault();
|
|
||||||
} else {
|
|
||||||
if (handler) {
|
if (handler) {
|
||||||
handler(e);
|
handler(e);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// To disable scrolling in all browsers except Chrome
|
||||||
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
_handlePreventableTouchMove = (e: Object) => {
|
_handleScroll = (e: SyntheticEvent) => {
|
||||||
this._createPreventableScrollHandler(this.props.onTouchMove)(e);
|
e.persist();
|
||||||
};
|
|
||||||
|
|
||||||
_handlePreventableWheel = (e: Object) => {
|
|
||||||
this._createPreventableScrollHandler(this.props.onWheel)(e);
|
|
||||||
};
|
|
||||||
|
|
||||||
_handleScroll = (e: Object) => {
|
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
const { scrollEventThrottle } = this.props;
|
const { scrollEventThrottle } = this.props;
|
||||||
// A scroll happened, so the scroll bumps the debounce.
|
// A scroll happened, so the scroll bumps the debounce.
|
||||||
@@ -143,10 +124,6 @@ export default class ScrollViewBase extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setNodeRef = (element: View) => {
|
|
||||||
this._node = findNodeHandle(element);
|
|
||||||
};
|
|
||||||
|
|
||||||
_shouldEmitScrollEvent(lastTick: number, eventThrottle: number) {
|
_shouldEmitScrollEvent(lastTick: number, eventThrottle: number) {
|
||||||
const timeSinceLastTick = Date.now() - lastTick;
|
const timeSinceLastTick = Date.now() - lastTick;
|
||||||
return eventThrottle > 0 && timeSinceLastTick >= eventThrottle;
|
return eventThrottle > 0 && timeSinceLastTick >= eventThrottle;
|
||||||
@@ -154,16 +131,14 @@ export default class ScrollViewBase extends Component {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
scrollEnabled,
|
||||||
|
style,
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
onMomentumScrollBegin,
|
onMomentumScrollBegin,
|
||||||
onMomentumScrollEnd,
|
onMomentumScrollEnd,
|
||||||
onScroll,
|
|
||||||
onScrollBeginDrag,
|
onScrollBeginDrag,
|
||||||
onScrollEndDrag,
|
onScrollEndDrag,
|
||||||
onTouchMove,
|
|
||||||
onWheel,
|
|
||||||
removeClippedSubviews,
|
removeClippedSubviews,
|
||||||
scrollEnabled,
|
|
||||||
scrollEventThrottle,
|
scrollEventThrottle,
|
||||||
showsHorizontalScrollIndicator,
|
showsHorizontalScrollIndicator,
|
||||||
showsVerticalScrollIndicator,
|
showsVerticalScrollIndicator,
|
||||||
@@ -171,6 +146,23 @@ export default class ScrollViewBase extends Component {
|
|||||||
...other
|
...other
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
return <View {...other} ref={this._setNodeRef} />;
|
return (
|
||||||
|
<View
|
||||||
|
{...other}
|
||||||
|
onScroll={this._handleScroll}
|
||||||
|
onTouchMove={this._createPreventableScrollHandler(this.props.onTouchMove)}
|
||||||
|
onWheel={this._createPreventableScrollHandler(this.props.onWheel)}
|
||||||
|
style={[style, !scrollEnabled && styles.scrollDisabled]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Chrome doesn't support e.preventDefault in this case; touch-action must be
|
||||||
|
// used to disable scrolling.
|
||||||
|
// https://developers.google.com/web/updates/2017/01/scrolling-intervention
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
scrollDisabled: {
|
||||||
|
touchAction: 'none'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ const ViewStylePropTypes = {
|
|||||||
outlineColor: ColorPropType,
|
outlineColor: ColorPropType,
|
||||||
perspective: oneOfType([number, string]),
|
perspective: oneOfType([number, string]),
|
||||||
perspectiveOrigin: string,
|
perspectiveOrigin: string,
|
||||||
|
touchAction: string,
|
||||||
transitionDelay: string,
|
transitionDelay: string,
|
||||||
transitionDuration: string,
|
transitionDuration: string,
|
||||||
transitionProperty: string,
|
transitionProperty: string,
|
||||||
|
|||||||
Reference in New Issue
Block a user