2

在下面的简单滑块(打字稿)示例中,来自 react-native-gesture-handler 的 PanGestureHandler 只会在手势启动后设置。用户将需要移动手指。

这就是我想要实现的: Taps 还应该设置滑块值(包括点击和拖动)。这是一种常见的模式,例如在寻找视频文件或将音量设置为最大然后进行调整时。

我想我可以将它包装在 TapGestureHandler 中,但我正在寻找最优雅的方式来实现这一点,而无需太多样板。

// example extracted from https://www.npmjs.com/package/react-native-reanimated-slider
import React, { Component } from 'react';
import Animated from 'react-native-reanimated';
import { PanGestureHandler, State } from 'react-native-gesture-handler';

const { Value, event, cond, eq, Extrapolate, interpolate } = Animated;

interface IProps {
  minimumTrackTintColor: string;
  maximumTrackTintColor: string;
  cacheTrackTintColor: string;
  value: number;
  style: any;
  cache;
  onSlidingStart;
  onSlidingComplete;
}
class Slider extends Component<IProps, {}> {
  static defaultProps = {
    minimumTrackTintColor: '#f3f',
    maximumTrackTintColor: 'transparent',
    cacheTrackTintColor: '#777',
  };

  private gestureState;
  private x;
  private width;
  private clamped_x;
  private onGestureEvent;

  public constructor(props: IProps) {
    super(props);

    this.gestureState = new Value(State.UNDETERMINED);
    this.x = new Value(0);
    this.width = new Value(0);

    this.clamped_x = cond(
      eq(this.width, 0),
      0,
      interpolate(this.x, {
        inputRange: [0, this.width],
        outputRange: [0, this.width],
        extrapolate: Extrapolate.CLAMP,
      })
    );

    this.onGestureEvent = event([
      {
        nativeEvent: {
          state: this.gestureState,
          x: this.x,
        },
      },
    ]);
  }

  onLayout = ({ nativeEvent }) => {
    this.width.setValue(nativeEvent.layout.width);
  };

  render() {
    const { style, minimumTrackTintColor, maximumTrackTintColor } = this.props;

    return (
      <PanGestureHandler
        onGestureEvent={this.onGestureEvent}
        onHandlerStateChange={this.onGestureEvent}
      >
        <Animated.View
          style={[
            {
              flex: 1,
              height: 30,
              overflow: 'visible',
              alignItems: 'center',
              justifyContent: 'center',
              backgroundColor: '#3330',
            },
            style,
          ]}
          onLayout={this.onLayout}
        >
          <Animated.View
            style={{
              width: '100%',
              height: 5,
              borderRadius: 2,
              overflow: 'hidden',
              borderWidth: 1,
              backgroundColor: maximumTrackTintColor,
            }}
          >
            <Animated.View
              style={{
                backgroundColor: minimumTrackTintColor,
                height: '100%',
                maxWidth: '100%',
                width: this.clamped_x,
                position: 'absolute',
              }}
            />
          </Animated.View>
        </Animated.View>
      </PanGestureHandler>
    );
  }
}

export default Slider;

提前致谢!

编辑:这按预期工作,但有一个可见的渲染怪癖和一个小的延迟。

import React, { Component } from 'react';
import Animated from 'react-native-reanimated';
import { PanGestureHandler, TapGestureHandler, State } from 'react-native-gesture-handler';

const { Value, event, cond, eq, Extrapolate, interpolate } = Animated;

interface IProps {
  minimumTrackTintColor?: string;
  maximumTrackTintColor?: string;
  cacheTrackTintColor?: string;
  value: number;
  style?: any;
  onSlidingStart;
  onSlidingComplete;
}
class Slider extends Component<IProps, {}> {
  static defaultProps = {
    minimumTrackTintColor: '#f3f',
    maximumTrackTintColor: 'transparent',
    cacheTrackTintColor: '#777',
  };

  private gestureState;
  private x;
  private width;
  private clamped_x;
  private onGestureEvent;
  private onTapGesture;
  public constructor(props: IProps) {
    super(props);

    this.gestureState = new Value(State.UNDETERMINED);
    this.x = new Value(0);
    this.width = new Value(0);

    this.clamped_x = cond(
      eq(this.width, 0),
      0,
      interpolate(this.x, {
        inputRange: [0, this.width],
        outputRange: [0, this.width],
        extrapolate: Extrapolate.CLAMP,
      })
    );

    this.onGestureEvent = event([
      {
        nativeEvent: {
          state: this.gestureState,
          x: this.x,
        },
      },
    ]);

    this.onTapGesture = event([
      {
        nativeEvent: {
          state: this.gestureState,
          x: this.x,
        },
      },
    ]);
  }

  onLayout = ({ nativeEvent }) => {
    this.width.setValue(nativeEvent.layout.width);
  };

  render() {
    const { style, minimumTrackTintColor, maximumTrackTintColor } = this.props;

    return (
      <TapGestureHandler
        onGestureEvent={this.onTapGesture}
        onHandlerStateChange={this.onTapGesture}
      >
        <Animated.View>
          <PanGestureHandler
            onGestureEvent={this.onGestureEvent}
            onHandlerStateChange={this.onGestureEvent}
          >
            <Animated.View
              style={[
                {
                  flex: 1,
                  height: 30,
                  overflow: 'visible',
                  alignItems: 'center',
                  justifyContent: 'center',
                  backgroundColor: '#3330',
                },
                style,
              ]}
              onLayout={this.onLayout}
            >
              <Animated.View
                style={{
                  width: '100%',
                  height: 5,
                  borderRadius: 2,
                  overflow: 'hidden',
                  borderWidth: 1,
                  backgroundColor: maximumTrackTintColor,
                }}
              >
                <Animated.View
                  style={{
                    backgroundColor: minimumTrackTintColor,
                    height: '100%',
                    maxWidth: '100%',
                    width: this.clamped_x,
                    position: 'absolute',
                  }}
                />
              </Animated.View>
            </Animated.View>
          </PanGestureHandler>
        </Animated.View>
      </TapGestureHandler>
    );
  }
}

export default Slider;
4

1 回答 1

4

多次阅读文档后,我想通了。它比预期的要简单:)

<PanGestureHandler
   onGestureEvent={this.onGestureEvent}
   onHandlerStateChange={this.onGestureEvent}
   minDist={0}
>

该属性minDist可以设置为0

实际上需要使用 LongPressGestureHandler,因为 PanHandler 仅在一些初始移动后更改其状态,而不是在触摸开始时。

解决方案是使用类似的东西:

<LongPressGestureHandler
    onGestureEvent={this.onGestureEvent}
    onHandlerStateChange={this.onGestureEvent}
    minDurationMs={0}
    maxDist={Number.MAX_SAFE_INTEGER}
    shouldCancelWhenOutside={false}
    hitSlop={10}
>
{...}
</LongPressGestureHandler>
于 2019-08-23T20:22:34.243 回答