react-native-navigation的迁移库

TouchablePreview.tsx 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import * as React from 'react';
  2. import * as PropTypes from 'prop-types';
  3. import {
  4. View,
  5. Platform,
  6. findNodeHandle,
  7. TouchableOpacity,
  8. TouchableHighlight,
  9. TouchableNativeFeedback,
  10. TouchableWithoutFeedback,
  11. GestureResponderEvent,
  12. NativeTouchEvent,
  13. NativeSyntheticEvent,
  14. } from 'react-native';
  15. // Polyfill GestureResponderEvent type with additional `force` property (iOS)
  16. interface NativeTouchEventWithForce extends NativeTouchEvent {
  17. force: number;
  18. }
  19. interface GestureResponderEventWithForce extends NativeSyntheticEvent<NativeTouchEventWithForce> {}
  20. export interface Props {
  21. children: React.ReactNode;
  22. touchableComponent?:
  23. | TouchableHighlight
  24. | TouchableOpacity
  25. | TouchableNativeFeedback
  26. | TouchableWithoutFeedback
  27. | React.ReactNode;
  28. onPress?: () => void;
  29. onPressIn?: (payload: { reactTag: number | null }) => void;
  30. onPeekIn?: () => void;
  31. onPeekOut?: () => void;
  32. }
  33. const PREVIEW_DELAY = 350;
  34. const PREVIEW_MIN_FORCE = 0.1;
  35. const PREVIEW_TIMEOUT = 1250;
  36. export class TouchablePreview extends React.PureComponent<Props> {
  37. static propTypes = {
  38. children: PropTypes.node,
  39. touchableComponent: PropTypes.func,
  40. onPress: PropTypes.func,
  41. onPressIn: PropTypes.func,
  42. onPeekIn: PropTypes.func,
  43. onPeekOut: PropTypes.func,
  44. };
  45. static defaultProps = {
  46. touchableComponent: TouchableWithoutFeedback,
  47. };
  48. static peeking = false;
  49. private timeout: number | undefined;
  50. private touchStartedAt: number = 0;
  51. private onRef = React.createRef<any>();
  52. onPress = () => {
  53. const { onPress } = this.props;
  54. if (typeof onPress !== 'function' || TouchablePreview.peeking) {
  55. return;
  56. }
  57. return onPress();
  58. }
  59. onPressIn = () => {
  60. if (Platform.OS === 'ios') {
  61. const { onPressIn } = this.props;
  62. if (!onPressIn) {
  63. return;
  64. }
  65. const reactTag = findNodeHandle(this.onRef.current);
  66. return onPressIn({ reactTag });
  67. }
  68. // Other platforms don't support 3D Touch Preview API
  69. return null;
  70. }
  71. onTouchStart = (event: GestureResponderEvent) => {
  72. // Store a timstamp of the initial touch start
  73. this.touchStartedAt = event.nativeEvent.timestamp;
  74. }
  75. onTouchMove = (event: GestureResponderEventWithForce) => {
  76. clearTimeout(this.timeout);
  77. const { force, timestamp } = event.nativeEvent;
  78. const diff = timestamp - this.touchStartedAt;
  79. if (force > PREVIEW_MIN_FORCE && diff > PREVIEW_DELAY) {
  80. TouchablePreview.peeking = true;
  81. if (typeof this.props.onPeekIn === 'function') {
  82. this.props.onPeekIn();
  83. }
  84. }
  85. this.timeout = setTimeout(this.onTouchEnd, PREVIEW_TIMEOUT);
  86. }
  87. onTouchEnd = () => {
  88. clearTimeout(this.timeout);
  89. TouchablePreview.peeking = false;
  90. if (typeof this.props.onPeekOut === 'function') {
  91. this.props.onPeekOut();
  92. }
  93. }
  94. render() {
  95. const { children, touchableComponent, onPress, onPressIn, ...props } = this.props;
  96. // Default to TouchableWithoutFeedback for iOS if set to TouchableNativeFeedback
  97. const Touchable = (Platform.OS === 'ios' && touchableComponent instanceof TouchableNativeFeedback) ?
  98. TouchableWithoutFeedback :
  99. touchableComponent as typeof React.Component;
  100. // Wrap component with Touchable for handling platform touches
  101. // and a single react View for detecting force and timing.
  102. return (
  103. <Touchable ref={this.onRef} onPress={this.onPress} onPressIn={this.onPressIn} {...props}>
  104. <View
  105. onTouchStart={this.onTouchStart}
  106. onTouchMove={this.onTouchMove as (event: GestureResponderEvent) => void}
  107. onTouchEnd={this.onTouchEnd}
  108. >
  109. {children}
  110. </View>
  111. </Touchable>
  112. );
  113. }
  114. }