Skip to content

Instantly share code, notes, and snippets.

@necolas
Last active Aug 20, 2019
Embed
What would you like to do?
React gesture responders
import useLongPressToDrag from './useLongPressToDrag';
import usePressability from './usePressability';
function MyPressableView(props) {
const [gestureState, gestureListeners] = usePressability(config, {
onPress(e) {
log('press');
}
});
return (
<View
listeners={gestureListeners}
style={[{
backgroundColor: gestureState.focusVisible ? Color.blue : Color.transparent,
boxShadow: [ gestureState.longPressed ? smallShadowBelow : largeShadowBelow ],
color: gestureState.pressed ? Color.yellow : Color.green,
}]}
>
{gestureState.hovered ? <Tooltip /> : null}
{props.children}
</View>
);
}
function MyDraggableView(props) {
const [gestureState, gestureListeners] = useLongPressToDrag({
dragStartBehavior: 'start',
longPressMinimumDuration: 500
}, {
onDrag(e) {
log(e.velocity)
}
});
return (
<View
listeners={gestureListeners}
style={[
gestureState.dragging && styles.dragging,
gestureState.pressing && styles.pressing,
{
transform: [{
translateX: gestureState.dx,
translateY: gestureState.dy
}]
}
]}
/>
);
}
import { useDrag, useLongPress, sequentialGesture } from './gestures';
/* custom "interaction" */
function useLongPressToDrag(config, handlers) {
const { longPressMinimumDuration } = config;
const { onDrag, ...etc } = handlers;
const initialGestureState = { isActive: false, isDragging: false, dx: 0, dy: 0 };
const [gestureState, dispatch] = useReducer(function (state, action) {
switch (action.type) {
case 'inactive': {
return { active: false, dragging: false, dx: 0, dy: 0 };
}
case 'pressing': {
return { active: true, dragging: false, dx: 0, dy: 0 };
}
case 'dragging': {
return { active: true, dragging: true, dx: action.dx, dy: action.dy };
}
case 'complete': {
return { active: false, dragging: false, dx: action.dx, dy: action.dy };
}
}
}, initialDragState);
const longPress = useLongpress({ longPressDelay }, {
onStart() {
// long press is in progress
dispatch({ type: 'pressing' });
},
onCancel() {
dispatch({ type: 'inactive' });
}
});
const drag = useDrag(null, {
onStart(e) {
// long press completed, dragging has begun
dispatch({ type: 'dragging', dx: e.dx, dy: e.dy });
if (onDragStart) {
onDragState(e);
}
},
onEnd() {
dispatch({ type: 'complete', dx: e.dx, dy: e.dy });
},
onCancel() {
dispatch({ type: 'inactive' });
}
});
// drag responder only receives events if longpress completed
const listeners = sequentialGesture([
longPress,
drag
]);
return [ gestureState, listeners ];
}
import { useState } from 'react';
import { useFocus, useHover, usePress } from './gestures';
/* custom "interaction" */
function usePressability(config, handlers) {
const { onPress, ...etc } = handlers;
const [ focused, updateFocus ] = useState(false);
const [ focusVisible, updateFocusVisible ] = useState(false);
const [ hovered, updateHover ] = useState(false);
const [ pressed, updatePress ] = useState(false);
const focus = useFocus({}, {
onFocusChange: updateFocus,
onFocusVisibleChange: updateFocusVisible,
...etc
});
const hover = useHover({}, {
onHoverChange: updateHover,
...etc
});
const press = usePress({}, {
onPress,
onPressChange: updatePress,
...etc
});
const gestureState = [
focused,
focusVisible,
hovered,
pressed
];
const listeners = [
hover,
focus,
press
];
return [ gestureState, listeners ]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment