react-native#PanResponder TypeScript Examples

The following examples show how to use react-native#PanResponder. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: CircularSlider.tsx    From nyxo-app with GNU General Public License v3.0 5 votes vote down vote up
constructor(props: CircularSliderProps) {
    super(props)

    this._sleepPanResponder = PanResponder.create({
      onMoveShouldSetPanResponder: (_evt, _gestureState) => true,
      onMoveShouldSetPanResponderCapture: (_evt, _gestureState) => true,
      onPanResponderGrant: (_evt, _gestureState) => this.setCircleCenter(),
      onPanResponderMove: (_evt, { moveX, moveY }) => {
        const { circleCenterX, circleCenterY } = this.state
        const { angleLength, startAngle, onUpdate } = this.props

        const currentAngleStop = (startAngle + angleLength) % (2 * Math.PI)
        let newAngle =
          Math.atan2(moveY - circleCenterY, moveX - circleCenterX) + Math.PI / 2

        if (newAngle < 0) {
          newAngle += 2 * Math.PI
        }

        let newAngleLength = currentAngleStop - newAngle

        if (newAngleLength < 0) {
          newAngleLength += 2 * Math.PI
        }

        onUpdate({
          startAngle: newAngle,
          angleLength: newAngleLength % (2 * Math.PI)
        })
      }
    })

    this._wakePanResponder = PanResponder.create({
      onMoveShouldSetPanResponder: (_evt, _gestureState) => true,
      onMoveShouldSetPanResponderCapture: (_evt, _gestureState) => true,
      onPanResponderGrant: (_evt, _gestureState) => this.setCircleCenter(),
      onPanResponderMove: (_evt, { moveX, moveY }) => {
        const { circleCenterX, circleCenterY } = this.state
        const { angleLength, startAngle, onUpdate } = this.props

        const newAngle =
          Math.atan2(moveY - circleCenterY, moveX - circleCenterX) + Math.PI / 2
        let newAngleLength = (newAngle - startAngle) % (2 * Math.PI)

        if (newAngleLength < 0) {
          newAngleLength += 2 * Math.PI
        }

        onUpdate({ startAngle, angleLength: newAngleLength })
      }
    })
  }
Example #2
Source File: CircularSlider.tsx    From nyxo-app with GNU General Public License v3.0 5 votes vote down vote up
_wakePanResponder: PanResponder
Example #3
Source File: CircularSlider.tsx    From nyxo-app with GNU General Public License v3.0 5 votes vote down vote up
_sleepPanResponder: PanResponder
Example #4
Source File: IterableInboxMessageCell.tsx    From react-native-sdk with MIT License 4 votes vote down vote up
IterableInboxMessageCell = ({
   index,
   last,
   dataModel,
   rowViewModel,
   customizations,
   swipingCheck,
   messageListItemLayout,
   deleteRow,
   handleMessageSelect,
   contentWidth,
   isPortrait
}: MessageCellProps) => {
   const position = useRef(new Animated.ValueXY()).current

   let deleteSliderHeight = customizations.messageRow?.height ? customizations.messageRow.height: 150
   
   if(messageListItemLayout(last, rowViewModel)) {
      deleteSliderHeight = messageListItemLayout(last, rowViewModel)[1]
   }

   const styles = StyleSheet.create({
      textContainer: {
         width: '100%',
         elevation: 2
      },
   
      deleteSlider: {
         flexDirection: 'row',
         alignItems: 'center',
         justifyContent: 'flex-end',
         paddingRight: 10,
         backgroundColor: 'red',
         position: 'absolute',
         elevation: 1,
         width: '100%',
         height: deleteSliderHeight
      },
   
      textStyle: {
         fontWeight: 'bold',
         fontSize: 15,
         color: 'white'
      }
   })

   let { textContainer, deleteSlider, textStyle } = styles

   deleteSlider = (isPortrait) ? deleteSlider : { ...deleteSlider, paddingRight: 40 }

   const scrollThreshold = contentWidth / 15
   const FORCING_DURATION = 350

   //If user swipes, either complete swipe or reset 
   function userSwipedLeft(gesture: any) {
      if (gesture.dx < -0.6 * contentWidth) {
         completeSwipe()
      } else {
         resetPosition()
      }
   }

   function completeSwipe() {
      const x = -2000
      Animated.timing(position, {
         toValue: { x, y: 0 },
         duration: FORCING_DURATION,
         useNativeDriver: false
      }).start(() => deleteRow(rowViewModel.inAppMessage.messageId))
   }

   function resetPosition() {
      Animated.timing(position, {
         toValue: { x: 0, y: 0 },
         duration: 200,
         useNativeDriver: false
      }).start()
   }

   const panResponder = useRef(
      PanResponder.create({
         onStartShouldSetPanResponder: () => false,
         onMoveShouldSetPanResponder: (event, gestureState) => {
            const { dx, dy } = gestureState
            // return true if user is swiping, return false if it's a single click
            return Math.abs(dx) !== 0 && Math.abs(dy) !== 0
         },
         onMoveShouldSetPanResponderCapture: (event, gestureState) => {
            const { dx, dy } = gestureState
            // return true if user is swiping, return false if it's a single click
            return Math.abs(dx) !== 0 && Math.abs(dy) !== 0
         },
         onPanResponderTerminationRequest: () => false,
         onPanResponderGrant: () => {
            position.setValue({ x: 0, y: 0 })
         },
         onPanResponderMove: (event, gesture) => {
            if (gesture.dx <= -scrollThreshold) {
               //enables swipeing when threshold is reached
               swipingCheck(true)
               //threshold value is deleted from movement
               const x = gesture.dx
               //position is set to the new value
               position.setValue({ x, y: 0 })
            }
         },
         onPanResponderRelease: (event, gesture) => {
            position.flattenOffset()
            if (gesture.dx < 0) {
               userSwipedLeft(gesture)
            } 
            else {
               resetPosition();
            }
            swipingCheck(false)
         }
      })
   ).current

   return (
      <>
         <View style={deleteSlider}>
            <Text style={textStyle}>DELETE</Text>
         </View>
         <Animated.View
            style={[textContainer, position.getLayout()]}
            {...panResponder.panHandlers}
         >
            <TouchableOpacity
               activeOpacity={1}
               onPress={() => {
                  handleMessageSelect(rowViewModel.inAppMessage.messageId, index)
               }}
            >
               {messageListItemLayout(last, rowViewModel) ? 
                  messageListItemLayout(last, rowViewModel)[0] : 
                  defaultMessageListLayout(last, dataModel, rowViewModel, customizations, isPortrait)}
            </TouchableOpacity>
         </Animated.View>
      </>
   )
}
Example #5
Source File: drag.ts    From keen-slider with MIT License 4 votes vote down vote up
export default function Drag(
  slider: SliderInstance<
    NativeOptions,
    NativeInstance<{}>,
    | HOOK_DRAG_ENDED
    | HOOK_DRAG_STARTED
    | HOOK_DRAGGED
    | HOOK_UPDATED
    | HOOK_LAYOUT_CHANGED
    | HOOK_DRAG_CHECKED
  >
): void {
  const breakFactorValue = 2
  let direction
  let defaultDirection
  let size
  let dragActive
  let dragSpeed
  let dragIdentifier
  let dragJustStarted
  let lastValue
  let lastX
  let lastY
  let min
  let max
  function dragStart(e) {
    if (
      dragActive ||
      !slider.track.details ||
      !slider.track.details.length ||
      !slider.options.drag
    )
      return
    dragActive = true
    dragJustStarted = true
    dragIdentifier = e.idChanged
    isSlide(e)
    lastValue = xy(e)
    slider.emit('dragStarted')
    return true
  }
  function drag(e) {
    if (!dragActive || dragIdentifier !== e.idChanged) {
      return
    }

    const value = xy(e)
    if (dragJustStarted) {
      if (!isSlide(e)) return dragStop(e)
      slider.emit('dragChecked')
      dragJustStarted = false
    }

    const distance = rubberband(
      (dragSpeed(lastValue - value) / slider.size) * defaultDirection
    )

    direction = sign(distance)
    slider.track.add(distance)
    lastValue = value
    slider.emit('dragged')
  }
  function setSpeed() {
    const speed = slider.options.dragSpeed || 1
    dragSpeed =
      typeof speed === 'function' ? speed : val => val * (speed as number)
  }
  function dragStop(e) {
    if (!dragActive || dragIdentifier !== e.idChanged) return
    dragActive = false
    slider.emit('dragEnded')
  }

  function isSlide(e) {
    const vertical = slider.options.vertical
    const x = vertical ? e.y : e.x
    const y = vertical ? e.x : e.y
    const isSlide =
      lastX !== undefined &&
      lastY !== undefined &&
      Math.abs(lastY - y) <= Math.abs(lastX - x)
    lastX = x
    lastY = y
    return isSlide
  }

  function rubberband(distance) {
    if (min === -Infinity && max === Infinity) return distance
    const details = slider.track.details
    const length = details.length
    const position = details.position
    const clampedDistance = clamp(distance, min - position, max - position)
    if (length === 0) return 0
    if (!slider.options.rubberband) {
      return clampedDistance
    }

    if (position <= max && position >= min) return distance
    if (
      (position < min && direction > 0) ||
      (position > max && direction < 0)
    ) {
      return distance
    }

    const overflow = (position < min ? position - min : position - max) / length

    const trackSize = size * length
    const overflowedSize = Math.abs(overflow * trackSize)
    const p = Math.max(0, 1 - (overflowedSize / size) * breakFactorValue)
    return p * p * distance
  }

  function setSizes() {
    size = slider.size
    const details = slider.track.details
    if (!details) return
    min = details.min
    max = details.max
  }

  function xy(e) {
    return slider.options.vertical ? e.y : e.x
  }

  function update() {
    setSizes()
    setSpeed()
    defaultDirection = !slider.options.rtl ? 1 : -1
  }

  slider.on('updated', update)
  slider.on('layoutChanged', update)
  slider.on('created', update)

  const pan = PanResponder.create({
    onPanResponderMove: inputHandler(drag),
    onPanResponderRelease: inputHandler(dragStop),
    onPanResponderTerminate: inputHandler(dragStop),
    onStartShouldSetPanResponder: inputHandler(dragStart),
  })
  Object.assign(slider.containerProps, pan.panHandlers)
}
Example #6
Source File: AnalogClock.tsx    From react-native-paper-dates with MIT License 4 votes vote down vote up
function AnalogClock({
  hours,
  minutes,
  focused,
  is24Hour,
  onChange,
}: {
  hours: number
  minutes: number
  focused: PossibleClockTypes
  is24Hour: boolean
  onChange: (hoursMinutesAndFocused: {
    hours: number
    minutes: number
    focused?: undefined | PossibleClockTypes
  }) => any
}) {
  const theme = useTheme()
  const { mode } = React.useContext(DisplayModeContext)
  // used to make pointer shorter if hours are selected and above 12
  const shortPointer = (hours === 0 || hours > 12) && is24Hour
  const clockRef = React.useRef<View | null>(null)
  // Hooks are nice, sometimes... :-)..
  // We need the latest values, since the onPointerMove uses a closure to the function
  const hoursRef = useLatest(hours)
  const onChangeRef = useLatest(onChange)
  const minutesRef = useLatest(minutes)
  const focusedRef = useLatest(focused)
  const is24HourRef = useLatest(is24Hour)
  const modeRef = useLatest(mode)
  const onPointerMove = React.useCallback(
    (e: GestureResponderEvent, final: boolean) => {
      let x = e.nativeEvent.locationX
      let y = e.nativeEvent.locationY
      let angle = getAngle(x, y, circleSize)
      if (focusedRef.current === clockTypes.hours) {
        let hours24 = is24HourRef.current
        let previousHourType = getHourType(hoursRef.current)
        let pickedHours = getHours(angle, previousHourType)

        let hours12AndPm = !hours24 && modeRef.current === 'AM'

        let hourTypeFromOffset = getHourTypeFromOffset(x, y, circleSize)
        let hours24AndPM = hours24 && hourTypeFromOffset === hourTypes.pm

        // Avoiding the "24h"
        // Should be 12h for 12 hours and PM mode

        if (hours12AndPm || hours24AndPM) {
          pickedHours += 12
        }
        if (modeRef.current === 'AM' && pickedHours === 12) {
          pickedHours = 0
        }

        if (
          (!hours24 && modeRef.current === 'AM' && pickedHours === 12) ||
          pickedHours === 24
        ) {
          pickedHours = 0
        }

        if (hoursRef.current !== pickedHours || final) {
          onChangeRef.current({
            hours: pickedHours,
            minutes: minutesRef.current,
            focused: final ? clockTypes.minutes : undefined,
          })
        }
      } else if (focusedRef.current === clockTypes.minutes) {
        let pickedMinutes = getMinutes(angle)
        if (minutesRef.current !== pickedMinutes) {
          onChangeRef.current({
            hours: hoursRef.current,
            minutes: pickedMinutes,
          })
        }
      }
    },
    [focusedRef, is24HourRef, hoursRef, onChangeRef, minutesRef, modeRef]
  )
  const panResponder = React.useRef(
    PanResponder.create({
      onPanResponderGrant: (e) => onPointerMove(e, false),
      onPanResponderMove: (e) => onPointerMove(e, false),
      onPanResponderRelease: (e) => onPointerMove(e, true),
      onStartShouldSetPanResponder: returnTrue,
      onStartShouldSetPanResponderCapture: () => false,
      onMoveShouldSetPanResponder: returnTrue,
      onMoveShouldSetPanResponderCapture: returnTrue,
      onPanResponderTerminationRequest: returnTrue,
      onShouldBlockNativeResponder: returnTrue,
    })
  ).current
  const dynamicSize = focused === clockTypes.hours && shortPointer ? 33 : 0
  const pointerNumber = focused === clockTypes.hours ? hours : minutes
  const degreesPerNumber = focused === clockTypes.hours ? 30 : 6
  return (
    <View
      ref={clockRef}
      {...panResponder.panHandlers}
      style={[
        styles.clock,
        {
          backgroundColor: theme.dark
            ? Color(theme.colors.surface).lighten(1.2).hex()
            : Color(theme.colors.surface).darken(0.1).hex(),
        },
      ]}
      // @ts-ignore -> https://github.com/necolas/react-native-web/issues/506
      cursor={'pointer'}
    >
      <View
        style={[
          styles.line,
          {
            backgroundColor: theme.colors.primary,
            transform: [
              { rotate: -90 + pointerNumber * degreesPerNumber + 'deg' },
              {
                translateX: circleSize / 4 - 4 - dynamicSize / 2,
              },
            ],
            width: circleSize / 2 - 4 - dynamicSize,
          },
        ]}
        pointerEvents="none"
      >
        <View
          style={[styles.endPoint, { backgroundColor: theme.colors.primary }]}
        />
      </View>
      <View
        style={[StyleSheet.absoluteFill, styles.center]}
        pointerEvents="none"
      >
        <View
          style={[
            styles.middlePoint,
            {
              backgroundColor: theme.colors.primary,
            },
          ]}
        />
      </View>
      <AnimatedClockSwitcher
        focused={focused}
        hours={<AnalogClockHours is24Hour={is24Hour} hours={hours} />}
        minutes={<AnalogClockMinutes minutes={minutes} />}
      />
    </View>
  )
}