react-native#PanResponder JavaScript 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: utils.js From react-native-bitmap-color-picker with MIT License | 7 votes |
/**
* Simplified pan responder wrapper.
*/
export function createPanResponder({ onStart = fn, onMove = fn, onEnd = fn }) {
return PanResponder.create({
onStartShouldSetPanResponder: fn,
onStartShouldSetPanResponderCapture: fn,
onMoveShouldSetPanResponder: fn,
onMoveShouldSetPanResponderCapture: fn,
onPanResponderTerminationRequest: fn,
onPanResponderGrant: (evt, state) => {
return onStart({ x: evt.nativeEvent.pageX, y: evt.nativeEvent.pageY }, evt, state, 'start')
},
onPanResponderMove: (evt, state) => {
return onMove({ x: evt.nativeEvent.pageX, y: evt.nativeEvent.pageY }, evt, state, 'move')
},
onPanResponderRelease: (evt, state) => {
return onEnd({ x: evt.nativeEvent.pageX, y: evt.nativeEvent.pageY }, evt, state, 'end')
},
})
}
Example #2
Source File: OBSlidingPanel.js From haven with MIT License | 6 votes |
constructor(props) {
super(props);
const { height = DEFAULT_CONTENT_WRAPPER_HEIGHT } = props;
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onStartShouldSetPanResponderCapture: () => false,
onMoveShouldSetPanResponder: (evt, gestureState) => {
const { dx, dy } = gestureState;
return dx > 10 || dx < -10 || dy > 10 || dy < -10;
},
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderMove: (evt, gestureState) => {
const { dy } = gestureState;
if (dy >= 0) {
this.verticalAniVal.setValue(0 - dy);
}
},
onPanResponderRelease: (evt, gestureState) => {
const { dy } = gestureState;
if (dy >= 100) {
this.disappear();
} else {
this.resetVericalPosition();
}
},
onShouldBlockNativeResponder: () => true,
});
this.verticalAniVal = new Animated.Value(-height);
}
Example #3
Source File: HDWalletComponent.js From RRWallet with MIT License | 6 votes |
constructor(props) {
super(props);
this._panResponder = PanResponder.create({
onPanResponderMove: this._onPanResponderMove,
});
DeviceEventEmitter.addListener(WALLET_TAB_JUMP_NOTIFICATION, ({ index }) => {
if (index !== WALLET_TAB_JUMP_NOTIFICATION_INDEX_HD) {
return;
}
this.props.jumpTo && this.props.jumpTo("hd");
});
}
Example #4
Source File: MultiSigWalletComponent.js From RRWallet with MIT License | 6 votes |
constructor(props) {
super(props);
this._panResponder = PanResponder.create({
onPanResponderMove: this._onPanResponderMove,
});
DeviceEventEmitter.addListener(WALLET_TAB_JUMP_NOTIFICATION, ({ index }) => {
if (index !== WALLET_TAB_JUMP_NOTIFICATION_INDEX_MULTISIG) {
return;
}
this.props.jumpTo && this.props.jumpTo("multisig");
});
}
Example #5
Source File: ImageCropOverlay.js From react-native-expo-image-cropper with MIT License | 6 votes |
UNSAFE_componentWillMount() {
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: this.handleStartShouldSetPanResponder,
onPanResponderGrant: this.handlePanResponderGrant,
onPanResponderMove: this.handlePanResponderMove,
onPanResponderRelease: this.handlePanResponderEnd,
onPanResponderTerminate: this.handlePanResponderEnd,
})
}
Example #6
Source File: index.js From react-native-gesture-bottom-sheet with MIT License | 6 votes |
createPanResponder(props) {
const { height } = props;
const { pan } = this.state;
this.panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (e, gestureState) => {
if (gestureState.dy > 0) {
Animated.event([null, { dy: pan.y }], {
useNativeDriver: false,
})(e, gestureState);
}
},
onPanResponderRelease: (e, gestureState) => {
const gestureLimitArea = height / 3;
const gestureDistance = gestureState.dy;
if (gestureDistance > gestureLimitArea) {
this.setModalVisible(false);
} else {
Animated.spring(pan, { toValue: { x: 0, y: 0 }, useNativeDriver: false, }).start();
}
},
});
}
Example #7
Source File: SliderPickerCursor.js From react-native-slider-picker with GNU General Public License v3.0 | 5 votes |
constructor(props) {
super(props);
// Props checking
this.buttonBackgroundColor = this.props.buttonBackgroundColor ? this.props.buttonBackgroundColor : 'white';
this.buttonBorderColor = this.props.buttonBorderColor ? this.props.buttonBorderColor : 'dimgrey';
this.buttonBorderWidth = this.props.buttonBorderWidth ? this.props.buttonBorderWidth : 1;
this.buttonDimensionsPercentage = this.props.buttonDimensionsPercentage ? this.props.buttonDimensionsPercentage : 6;
this.defaultValue = this.props.defaultValue;
this.maxOffset = this.props.maxOffset ? this.props.maxOffset : vw(85);
this.maxValue = this.props.maxValue ? this.props.maxValue : 10;
this.releaseCallback = this.props.releaseCallback ? this.props.releaseCallback : () => {};
this.slideBeginCallback = this.props.slideBeginCallback ? this.props.slideBeginCallback : () => {};
this.errorToleranceMargin = this.props.errorToleranceMargin ? this.props.errorToleranceMargin : null;
// Check override style props
this.buttonStylesOverride = this.props.buttonStylesOverride ? this.props.buttonStylesOverride : null;
// Set buttonWidth to width passed for dimensions by default
this.buttonWidth = vw(this.buttonDimensionsPercentage);
// If button styles have been override and the override styles have a width property
if ( this.buttonStylesOverride && Object.keys(this.buttonStylesOverride).includes('width') ) {
// Set buttonWidth to the value passed in styles override.
this.buttonWidth = this.buttonStylesOverride['width'];
}
// Make sure that defaultValue isn't out of range
if (this.defaultValue > this.maxValue) {
this.defaultValue = this.maxValue;
}
// Initialize empty array to store xOffsets in
this.offsetsMap = [];
// Get x-axis positioning of each number/separator
for (let i = 0; i <= this.maxValue; i++) {
this.offsetsMap.push({
offset: this.maxOffset * (i / this.maxValue),
value: i
});
}
// Initialize state
this.state = {
// Create instance of Animated.XY, which interpolates X and Y values, in our case, we'll only need the X value.
drag: new Animated.ValueXY(),
// used to reference latestPosition of draggable view. Updated onPanResponderRelease.
latestPosition: this.getOffsetPosition(this.defaultValue),
// Used to set state on drag if user drags past Y axis threshold.
xOffsetAtToleranceMarginSurpassed: null
};
// Initialize value to accomodate for width of button
this.state.drag.setValue({
x: this.getOffsetPosition(this.defaultValue) - (this.buttonWidth * .5),
y: 0
});
// Create panResponder, which is responsible for the dragging
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder : () => true,
onPanResponderGrant: (evt, gesture) => {
this.panResponderGrantHandler()
},
onPanResponderMove: (evt, gesture) => { // When user moves cursor/button
this.panResponderMoveHandler(gesture);
},
onPanResponderRelease: (evt, gesture) => { // When user press/drag ends
this.panResponderReleaseHandler(gesture)
},
onPanResponderTerminate: (evt, gesture) => { // When user's touch/gesture is move outside of the button/cursor of <SliderPickerCursor>
this.panResponderReleaseHandler(gesture);
}
});
}
Example #8
Source File: DisplayedElement.js From UltimateApp with MIT License | 5 votes |
/* Props must contain:
- type: which indicates how to display the element: "offense", "defense", "triangle" or "disc"
- editable: true if element can be moved by the user
- number: string defined if there is something written on the element
- eId: element index in the list of elements of the drill (-1 if it is not currently in a drill)
*/
constructor(props) {
super(props);
this.state = {
isMoving: false,
stateFromProps: _initializeStateFromProps(props),
};
this.offset = new Animated.ValueXY({ x: 0, y: 0 });
// Initiate the panResponder
this.panResponder = PanResponder.create({
// Ask to be the responder
onStartShouldSetPanResponder: () => true,
// Called when the gesture starts
onPanResponderGrant: () => {
if (this.props.onMoveStart !== undefined && this.props.onMoveStart !== null) this.props.onMoveStart();
if (this.props.editable) {
this.setState({
isMoving: true,
});
this.offset.setOffset({
x: this.state.stateFromProps.interpolateX.__getValue(),
y: this.state.stateFromProps.interpolateY.__getValue(),
});
this.offset.setValue({ x: 0, y: 0 });
}
},
onPanResponderMove: this.props.editable
? Animated.event([null, { dx: this.offset.x, dy: this.offset.y }], { useNativeDriver: false })
: undefined,
onPanResponderRelease: (event, gestureState) => {
if (this.props.editable && this.props.onMoveEnd !== undefined && this.props.onMoveEnd !== null) {
this.props.onMoveEnd(this.props.eId, this.props.type, gestureState.dx, gestureState.dy);
}
this.setState({
isMoving: false,
});
},
});
}
Example #9
Source File: MovingCircle.js From UltimateApp with MIT License | 5 votes |
MovingCircle = (props) => {
const currentPosition = new Animated.ValueXY({ x: 0, y: 0 });
let _val = { x: 0, y: 0 };
currentPosition.addListener((value) => (_val = value)); // Initialize PanResponder with move handling
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
currentPosition.setOffset({
x: _val.x,
y: _val.y,
});
currentPosition.setValue({ x: 0, y: 0 });
},
onPanResponderMove: Animated.event([null, { dx: currentPosition.x, dy: currentPosition.y }], {
useNativeDriver: false,
}),
onPanResponderRelease: (evt, gesturestate) => {
props.onMoveEnd(props.elemId, currentPosition.x._value, currentPosition.y._value, props.isCounterCut);
// Avoid a bug where the current position is added to the next MovingCircle
currentPosition.setValue({ x: 0, y: 0 });
},
});
return (
<Animated.View
{...panResponder.panHandlers}
style={[
styles.circle,
{
transform: currentPosition.getTranslateTransform(),
left: props.cx - props.radius,
top: props.cy - props.radius,
},
]}
height={2 * props.radius}
width={2 * props.radius}
/>
);
}
Example #10
Source File: DraggableDisplayedElement.js From UltimateApp with MIT License | 5 votes |
DraggableDisplayedElement = (props) => {
const { draggableBaseWidth, type, number } = props;
/* Current position of the element in pixels */
const currentPosition = new Animated.ValueXY({ x: 0, y: 0 });
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: Animated.event([null, { dx: currentPosition.x, dy: currentPosition.y }], {
useNativeDriver: false,
}),
onPanResponderRelease: (event, gestureState) => {
props.onMoveEnd(type, gestureState.moveX, gestureState.moveY);
currentPosition.setValue({ x: 0, y: 0 });
},
});
const panStyle = {
transform: currentPosition.getTranslateTransform(),
padding: 5,
height: '100%',
flexBasis: '25%',
minWidth: draggableBaseWidth,
minHeight: draggableBaseWidth,
alignItems: 'center',
justifyContent: 'center',
};
return (
<Animated.View {...panResponder.panHandlers} style={panStyle} key={type}>
{
{
defense: <Player baseWidth={draggableBaseWidth} number={number} type={type} />,
offense: <Player baseWidth={draggableBaseWidth} number={number} type={type} />,
triangle: <Cone baseWidth={draggableBaseWidth} number={number} />,
disc: <Disc baseWidth={draggableBaseWidth} number={number} />,
}[type]
}
</Animated.View>
);
}
Example #11
Source File: Holder.js From react-native-drag-text-editor with MIT License | 5 votes |
constructor(props) {
super(props);
this.position = {
x: 0,
y: 0,
};
this._panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (event, gestureState) => {return( gestureState.dx != 0 && gestureState.dy != 0)},
onMoveShouldSetPanResponderCapture: (event, gestureState) => { return (gestureState.dx != 0 && gestureState.dy != 0)},
onPanResponderGrant: (event, gestureState) => {
const {
onStart
}=this.props;
this.position = {
x: 0,
y: 0,
};
onStart([
0,
0,
]);
},
onPanResponderMove: (event, gestureState) => {
const {
onMove
}=this.props;
onMove(
[
gestureState.dx - this.position.x,
gestureState.dy - this.position.y,
]);
this.position = {
x: gestureState.dx,
y: gestureState.dy,
};
},
onPanResponderTerminationRequest: (event, gestureState) => true,
onPanResponderRelease: (event, gestureState) => {
const {
onEnd
}=this.props;
onEnd([
gestureState.moveX,
gestureState.moveY,
]);
},
});
}
Example #12
Source File: index.js From the-eye-knows-the-garbage with MIT License | 4 votes |
Swipeout = createReactClass({
mixins: [tweenState.Mixin],
propTypes: {
autoClose: PropTypes.bool,
backgroundColor: PropTypes.string,
close: PropTypes.bool,
left: PropTypes.array,
onOpen: PropTypes.func,
onClose: PropTypes.func,
right: PropTypes.array,
scroll: PropTypes.func,
style: (ViewPropTypes || View.propTypes).style,
sensitivity: PropTypes.number,
buttonWidth: PropTypes.number,
disabled: PropTypes.bool,
},
getDefaultProps: function () {
return {
disabled: false,
rowID: -1,
sectionID: -1,
sensitivity: 50,
};
},
getInitialState: function () {
return {
autoClose: this.props.autoClose || false,
btnWidth: 0,
btnsLeftWidth: 0,
btnsRightWidth: 0,
contentHeight: 0,
contentPos: 0,
contentWidth: 0,
openedRight: false,
swiping: false,
tweenDuration: 160,
timeStart: null,
};
},
componentWillMount: function () {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (event, gestureState) => true,
onStartShouldSetPanResponderCapture: (event, gestureState) =>
this.state.openedLeft || this.state.openedRight,
onMoveShouldSetPanResponderCapture: (event, gestureState) =>
Math.abs(gestureState.dx) > this.props.sensitivity &&
Math.abs(gestureState.dy) <= this.props.sensitivity,
onPanResponderGrant: this._handlePanResponderGrant,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminate: this._handlePanResponderEnd,
onShouldBlockNativeResponder: (event, gestureState) => false,
onPanResponderTerminationRequest: () => false,
});
},
componentWillReceiveProps: function (nextProps) {
if (nextProps.close) this._close();
if (nextProps.openRight) this._openRight();
if (nextProps.openLeft) this._openLeft();
},
_handlePanResponderGrant: function (e: Object, gestureState: Object) {
if (this.props.disabled) return;
if (!this.state.openedLeft && !this.state.openedRight) {
this._callOnOpen();
} else {
this._callOnClose();
}
this.refs.swipeoutContent.measure((ox, oy, width, height) => {
let buttonWidth = this.props.buttonWidth || (width / 5);
this.setState({
btnWidth: buttonWidth,
btnsLeftWidth: this.props.left ? buttonWidth * this.props.left.length : 0,
btnsRightWidth: this.props.right ? buttonWidth * this.props.right.length : 0,
swiping: true,
timeStart: (new Date()).getTime(),
});
});
},
_handlePanResponderMove: function (e: Object, gestureState: Object) {
if (this.props.disabled) return;
var posX = gestureState.dx;
var posY = gestureState.dy;
var leftWidth = this.state.btnsLeftWidth;
var rightWidth = this.state.btnsRightWidth;
if (this.state.openedRight) var posX = gestureState.dx - rightWidth;
else if (this.state.openedLeft) var posX = gestureState.dx + leftWidth;
// prevent scroll if moveX is true
var moveX = Math.abs(posX) > Math.abs(posY);
if (this.props.scroll) {
if (moveX) this.props.scroll(false);
else this.props.scroll(true);
}
if (this.state.swiping) {
// move content to reveal swipeout
if (posX < 0 && this.props.right) {
this.setState({ contentPos: Math.min(posX, 0) })
} else if (posX > 0 && this.props.left) {
this.setState({ contentPos: Math.max(posX, 0) })
};
}
},
_handlePanResponderEnd: function (e: Object, gestureState: Object) {
if (this.props.disabled) return;
var posX = gestureState.dx;
var contentPos = this.state.contentPos;
var contentWidth = this.state.contentWidth;
var btnsLeftWidth = this.state.btnsLeftWidth;
var btnsRightWidth = this.state.btnsRightWidth;
// minimum threshold to open swipeout
var openX = contentWidth * 0.33;
// should open swipeout
var openLeft = posX > openX || posX > btnsLeftWidth / 2;
var openRight = posX < -openX || posX < -btnsRightWidth / 2;
// account for open swipeouts
if (this.state.openedRight) var openRight = posX - openX < -openX;
if (this.state.openedLeft) var openLeft = posX + openX > openX;
// reveal swipeout on quick swipe
var timeDiff = (new Date()).getTime() - this.state.timeStart < 200;
if (timeDiff) {
var openRight = posX < -openX / 10 && !this.state.openedLeft;
var openLeft = posX > openX / 10 && !this.state.openedRight;
}
if (this.state.swiping) {
if (openRight && contentPos < 0 && posX < 0) {
this._open(-btnsRightWidth, 'right');
} else if (openLeft && contentPos > 0 && posX > 0) {
this._open(btnsLeftWidth, 'left');
} else {
this._close();
}
}
// Allow scroll
if (this.props.scroll) this.props.scroll(true);
},
_tweenContent: function (state, endValue) {
this.tweenState(state, {
easing: tweenState.easingTypes.easeInOutQuad,
duration: endValue === 0 ? this.state.tweenDuration * 1.5 : this.state.tweenDuration,
endValue: endValue,
});
},
_rubberBandEasing: function (value, limit) {
if (value < 0 && value < limit) return limit - Math.pow(limit - value, 0.85);
else if (value > 0 && value > limit) return limit + Math.pow(value - limit, 0.85);
return value;
},
// close swipeout on button press
_autoClose: function (btn) {
if (this.state.autoClose) this._close();
var onPress = btn.onPress;
if (onPress) onPress();
},
_open: function (contentPos, direction) {
const left = direction === 'left';
const { sectionID, rowID, onOpen } = this.props;
onOpen && onOpen(sectionID, rowID, direction);
this._tweenContent('contentPos', contentPos);
this.setState({
contentPos,
openedLeft: left,
openedRight: !left,
swiping: false,
});
},
_close: function () {
const { sectionID, rowID, onClose } = this.props;
if (onClose && (this.state.openedLeft || this.state.openedRight)) {
const direction = this.state.openedRight ? 'right' : 'left';
onClose(sectionID, rowID, direction);
}
this._tweenContent('contentPos', 0);
this._callOnClose();
this.setState({
openedRight: false,
openedLeft: false,
swiping: false,
});
},
_callOnClose: function () {
if (this.props.onClose) this.props.onClose(this.props.sectionID, this.props.rowID);
},
_callOnOpen: function () {
if (this.props.onOpen) this.props.onOpen(this.props.sectionID, this.props.rowID);
},
_openRight: function () {
this.refs.swipeoutContent.measure((ox, oy, width, height) => {
let btnWidth = this.props.buttonWidth || (width / 5);
this.setState({
btnWidth,
btnsRightWidth: this.props.right ? btnWidth * this.props.right.length : 0,
}, () => {
this._tweenContent('contentPos', -this.state.btnsRightWidth);
this._callOnOpen();
this.setState({
contentPos: -this.state.btnsRightWidth,
openedLeft: false,
openedRight: true,
swiping: false
});
});
});
},
_openLeft: function () {
this.refs.swipeoutContent.measure((ox, oy, width, height) => {
let btnWidth = this.props.buttonWidth || (width / 5);
this.setState({
btnWidth,
btnsLeftWidth: this.props.left ? btnWidth * this.props.left.length : 0,
}, () => {
this._tweenContent('contentPos', this.state.btnsLeftWidth);
this._callOnOpen();
this.setState({
contentPos: this.state.btnsLeftWidth,
openedLeft: true,
openedRight: false,
swiping: false
});
});
});
},
render: function () {
var contentWidth = this.state.contentWidth;
var posX = this.getTweeningValue('contentPos');
var styleSwipeout = [styles.swipeout, this.props.style];
if (this.props.backgroundColor) {
styleSwipeout.push([{ backgroundColor: this.props.backgroundColor }]);
}
var limit = -this.state.btnsRightWidth;
if (posX > 0) var limit = this.state.btnsLeftWidth;
var styleLeftPos = {
left: {
left: 0,
overflow: 'hidden',
width: Math.min(limit * (posX / limit), limit),
},
};
var styleRightPos = {
right: {
left: Math.abs(contentWidth + Math.max(limit, posX)),
right: 0,
},
};
var styleContentPos = {
content: {
transform: [{ translateX: this._rubberBandEasing(posX, limit) }],
},
};
var styleContent = [styles.swipeoutContent];
styleContent.push(styleContentPos.content);
var styleRight = [styles.swipeoutBtns];
styleRight.push(styleRightPos.right);
var styleLeft = [styles.swipeoutBtns];
styleLeft.push(styleLeftPos.left);
var isRightVisible = posX < 0;
var isLeftVisible = posX > 0;
return (
<View style={styleSwipeout}>
<View
ref="swipeoutContent"
style={styleContent}
onLayout={this._onLayout}
{...this._panResponder.panHandlers}
>
{this.props.children}
</View>
{this._renderButtons(this.props.right, isRightVisible, styleRight)}
{this._renderButtons(this.props.left, isLeftVisible, styleLeft)}
</View>
);
},
_onLayout: function (event) {
var { width, height } = event.nativeEvent.layout;
this.setState({
contentWidth: width,
contentHeight: height,
});
},
_renderButtons: function (buttons, isVisible, style) {
if (buttons && isVisible) {
return (<View style={style}>
{buttons.map(this._renderButton)}
</View>);
} else {
return (
<View />
);
}
},
_renderButton: function (btn, i) {
return (
<SwipeoutBtn
backgroundColor={btn.backgroundColor}
color={btn.color}
component={btn.component}
disabled={btn.disabled}
height={this.state.contentHeight}
key={i}
onPress={() => this._autoClose(btn)}
text={btn.text}
type={btn.type}
underlayColor={btn.underlayColor}
width={this.state.btnWidth}
/>
);
}
})
Example #13
Source File: HeaderScrollableTabView.js From react-native-collapsible-tabview with MIT License | 4 votes |
App = () => {
/**
* stats
*/
const [tabIndex, setIndex] = useState(0);
const [routes] = useState([
{key: 'tab1', title: 'Tab1'},
{key: 'tab2', title: 'Tab2'},
]);
const [canScroll, setCanScroll] = useState(true);
const [tab1Data] = useState(Array(40).fill(0));
const [tab2Data] = useState(Array(30).fill(0));
/**
* ref
*/
const scrollY = useRef(new Animated.Value(0)).current;
const headerScrollY = useRef(new Animated.Value(0)).current;
const listRefArr = useRef([]);
const listOffset = useRef({});
const isListGliding = useRef(false);
const headerScrollStart = useRef(0);
const _tabIndex = useRef(0);
/**
* PanResponder for header
*/
const headerPanResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponderCapture: (evt, gestureState) => false,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => false,
onStartShouldSetPanResponder: (evt, gestureState) => {
headerScrollY.stopAnimation();
syncScrollOffset();
return false;
},
onMoveShouldSetPanResponder: (evt, gestureState) => {
headerScrollY.stopAnimation();
return Math.abs(gestureState.dy) > 5;
},
onPanResponderRelease: (evt, gestureState) => {
syncScrollOffset();
if (Math.abs(gestureState.vy) < 0.2) {
return;
}
headerScrollY.setValue(scrollY._value);
Animated.decay(headerScrollY, {
velocity: -gestureState.vy,
useNativeDriver: true,
}).start(() => {
syncScrollOffset();
});
},
onPanResponderMove: (evt, gestureState) => {
listRefArr.current.forEach((item) => {
if (item.key !== routes[_tabIndex.current].key) {
return;
}
if (item.value) {
item.value.scrollToOffset({
offset: -gestureState.dy + headerScrollStart.current,
animated: false,
});
}
});
},
onShouldBlockNativeResponder: () => true,
onPanResponderGrant: (evt, gestureState) => {
headerScrollStart.current = scrollY._value;
},
}),
).current;
/**
* PanResponder for list in tab scene
*/
const listPanResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponderCapture: (evt, gestureState) => false,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => false,
onStartShouldSetPanResponder: (evt, gestureState) => false,
onMoveShouldSetPanResponder: (evt, gestureState) => {
headerScrollY.stopAnimation();
return false;
},
onShouldBlockNativeResponder: () => true,
onPanResponderGrant: (evt, gestureState) => {
headerScrollY.stopAnimation();
},
}),
).current;
/**
* effect
*/
useEffect(() => {
scrollY.addListener(({value}) => {
const curRoute = routes[tabIndex].key;
listOffset.current[curRoute] = value;
});
headerScrollY.addListener(({value}) => {
listRefArr.current.forEach((item) => {
if (item.key !== routes[tabIndex].key) {
return;
}
if (value > HeaderHeight || value < 0) {
headerScrollY.stopAnimation();
syncScrollOffset();
}
if (item.value && value <= HeaderHeight) {
item.value.scrollToOffset({
offset: value,
animated: false,
});
}
});
});
return () => {
scrollY.removeAllListeners();
headerScrollY.removeAllListeners();
};
}, [routes, tabIndex]);
/**
* helper functions
*/
const syncScrollOffset = () => {
const curRouteKey = routes[_tabIndex.current].key;
listRefArr.current.forEach((item) => {
if (item.key !== curRouteKey) {
if (scrollY._value < HeaderHeight && scrollY._value >= 0) {
if (item.value) {
item.value.scrollToOffset({
offset: scrollY._value,
animated: false,
});
listOffset.current[item.key] = scrollY._value;
}
} else if (scrollY._value >= HeaderHeight) {
if (
listOffset.current[item.key] < HeaderHeight ||
listOffset.current[item.key] == null
) {
if (item.value) {
item.value.scrollToOffset({
offset: HeaderHeight,
animated: false,
});
listOffset.current[item.key] = HeaderHeight;
}
}
}
}
});
};
const onMomentumScrollBegin = () => {
isListGliding.current = true;
};
const onMomentumScrollEnd = () => {
isListGliding.current = false;
syncScrollOffset();
};
const onScrollEndDrag = () => {
syncScrollOffset();
};
/**
* render Helper
*/
const renderHeader = () => {
const y = scrollY.interpolate({
inputRange: [0, HeaderHeight],
outputRange: [0, -HeaderHeight],
extrapolate: 'clamp',
});
return (
<Animated.View
{...headerPanResponder.panHandlers}
style={[styles.header, {transform: [{translateY: y}]}]}>
<TouchableOpacity
style={{flex: 1, justifyContent: 'center'}}
activeOpacity={1}
onPress={() => Alert.alert('header Clicked!')}>
<Text>Scrollable Header</Text>
</TouchableOpacity>
</Animated.View>
);
};
const rednerTab1Item = ({item, index}) => {
return (
<View
style={{
borderRadius: 16,
marginLeft: index % 2 === 0 ? 0 : 10,
width: tab1ItemSize,
height: tab1ItemSize,
backgroundColor: '#aaa',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>{index}</Text>
</View>
);
};
const rednerTab2Item = ({item, index}) => {
return (
<View
style={{
marginLeft: index % 3 === 0 ? 0 : 10,
borderRadius: 16,
width: tab2ItemSize,
height: tab2ItemSize,
backgroundColor: '#aaa',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>{index}</Text>
</View>
);
};
const renderLabel = ({route, focused}) => {
return (
<Text style={[styles.label, {opacity: focused ? 1 : 0.5}]}>
{route.title}
</Text>
);
};
const renderScene = ({route}) => {
const focused = route.key === routes[tabIndex].key;
let numCols;
let data;
let renderItem;
switch (route.key) {
case 'tab1':
numCols = 2;
data = tab1Data;
renderItem = rednerTab1Item;
break;
case 'tab2':
numCols = 3;
data = tab2Data;
renderItem = rednerTab2Item;
break;
default:
return null;
}
return (
<Animated.FlatList
// scrollEnabled={canScroll}
{...listPanResponder.panHandlers}
numColumns={numCols}
ref={(ref) => {
if (ref) {
const found = listRefArr.current.find((e) => e.key === route.key);
if (!found) {
listRefArr.current.push({
key: route.key,
value: ref,
});
}
}
}}
scrollEventThrottle={16}
onScroll={
focused
? Animated.event(
[
{
nativeEvent: {contentOffset: {y: scrollY}},
},
],
{useNativeDriver: true},
)
: null
}
onMomentumScrollBegin={onMomentumScrollBegin}
onScrollEndDrag={onScrollEndDrag}
onMomentumScrollEnd={onMomentumScrollEnd}
ItemSeparatorComponent={() => <View style={{height: 10}} />}
ListHeaderComponent={() => <View style={{height: 10}} />}
contentContainerStyle={{
paddingTop: HeaderHeight + TabBarHeight,
paddingHorizontal: 10,
minHeight: windowHeight - SafeStatusBar + HeaderHeight,
}}
showsHorizontalScrollIndicator={false}
data={data}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
keyExtractor={(item, index) => index.toString()}
/>
);
};
const renderTabBar = (props) => {
const y = scrollY.interpolate({
inputRange: [0, HeaderHeight],
outputRange: [HeaderHeight, 0],
extrapolate: 'clamp',
});
return (
<Animated.View
style={{
top: 0,
zIndex: 1,
position: 'absolute',
transform: [{translateY: y}],
width: '100%',
}}>
<TabBar
{...props}
onTabPress={({route, preventDefault}) => {
if (isListGliding.current) {
preventDefault();
}
}}
style={styles.tab}
renderLabel={renderLabel}
indicatorStyle={styles.indicator}
/>
</Animated.View>
);
};
const renderTabView = () => {
return (
<TabView
onSwipeStart={() => setCanScroll(false)}
onSwipeEnd={() => setCanScroll(true)}
onIndexChange={(id) => {
_tabIndex.current = id;
setIndex(id);
}}
navigationState={{index: tabIndex, routes}}
renderScene={renderScene}
renderTabBar={renderTabBar}
initialLayout={{
height: 0,
width: windowWidth,
}}
/>
);
};
return (
<View style={styles.container}>
{renderTabView()}
{renderHeader()}
</View>
);
}
Example #14
Source File: react-native-ring-picker.js From react-native-ring-picker with GNU General Public License v3.0 | 4 votes |
constructor(props) {
super(props);
let icons = this.mapPropsIconsToAnimatedOnes();
this.state = {
pan: new Animated.Value(0),
icons: icons,
showArrowHint: this.props.showArrowHint,
currentSnappedIcon: this.getCurrentSnappedMiddleIcon(icons),
ICON_PATH_RADIUS: 0,
XY_AXES_COORDINATES: {
X: 0,
Y: 0,
PAGE_Y: 0,
PAGE_X: 0
},
CURRENT_ICON_SHIFT: 0
};
this.INDEX_EXTRACTORS = {};
this.GIRTH_ANGLE = this.props.girthAngle;
this.AMOUNT_OF_ICONS = icons.length;
this.ICON_POSITION_ANGLE = this.GIRTH_ANGLE / this.AMOUNT_OF_ICONS;
// 2*Ï€*r / 360
this.STEP_LENGTH_TO_1_ANGLE = 0;
this.DIRECTIONS = {
CLOCKWISE: "CLOCKWISE",
COUNTERCLOCKWISE: "COUNTERCLOCKWISE"
};
this.CIRCLE_SECTIONS = {
TOP_LEFT: "TOP_LEFT",
TOP_RIGHT: "TOP_RIGHT",
BOTTOM_LEFT: "BOTTOM_LEFT",
BOTTOM_RIGHT: "BOTTOM_RIGHT"
};
this.CURRENT_CIRCLE_SECTION = null;
this.CURRENT_DIRECTION = null;
this.CURRENT_VECTOR_DIFFERENCE_LENGTH = 0;
this.PREVIOUS_POSITION = {
X: 0,
Y: 0
};
this.ICON_HIDE_ON_THE_BACK_DURATION = this.props.iconHideOnTheBackDuration;
this.ALL_ICONS_FINISH_ANIMATIONS = {
promises: this.state.icons.reduce((promises, icon) => {promises[icon.id] = null; return promises}, {}),
resolvers: this.state.icons.reduce((resolvers, icon) => {resolvers[icon.id] = null; return resolvers}, {})
};
this._panResponder = PanResponder.create({
onMoveShouldSetResponderCapture: () => true, //Tell iOS that we are allowing the movement
onMoveShouldSetPanResponderCapture: () => true, // Same here, tell iOS that we allow dragging
onPanResponderGrant: (e, gestureState) => {
this.hideArrowHint();
this.resetCurrentValues();
this.setPreviousDifferenceLengths(0 ,0);
this.state.pan.setValue(this.state.pan._value);
},
onPanResponderMove: (e, gestureState) => {
this.defineCurrentSection(gestureState.moveX, gestureState.moveY);
this.checkPreviousDifferenceLengths(gestureState.dx, gestureState.dy);
this.state.pan.setValue(this.CURRENT_VECTOR_DIFFERENCE_LENGTH);
this.setState({
...this.state,
CURRENT_ICON_SHIFT: this.CURRENT_VECTOR_DIFFERENCE_LENGTH / this.STEP_LENGTH_TO_1_ANGLE
}, () => this.calculateIconCurrentPositions(gestureState.vx));
},
onPanResponderRelease: (evt, gestureState) => {
let lastGesture = {...gestureState};
this.createFinishAnimationPromisesAndResolveIfIconsAreNotMovingAlready();
Promise
.all(this.getFinishAnimationPromises())
.then(() => this.snapNearestIconToVerticalAxis(lastGesture));
}
});
}
Example #15
Source File: index.js From react-native-circular-picker with MIT License | 4 votes |
CircularPicker = ({
size,
strokeWidth,
defaultPos,
steps,
gradients,
backgroundColor,
stepColor,
borderColor,
children,
onChange,
}) => {
const [pos, setPos] = useState(percentToPos(defaultPos));
const circle = useRef(null);
const padding = 8;
const radius = (size - strokeWidth) / 2 - padding;
const center = (radius + strokeWidth / 2);
const gradient = selectGradient(gradients, pos);
useEffect(()=>{
setPos(percentToPos(defaultPos));
}, [defaultPos]);
if (steps) {
steps = steps.map((p) => {
const pos = percentToPos(p);
const { x2, y2 } = calculateAngle(pos, radius);
const { endX: x, endY: y } = calculateRealPos(x2, y2, radius, strokeWidth);
return { x, y, p };
});
}
const { x1, y1, x2, y2 } = calculateAngle(pos, radius);
const { endX, endY } = calculateRealPos(x2, y2, radius, strokeWidth);
const goToPercent = (p) => {
const newPos = percentToPos(p);
setPos(newPos);
onChange(posToPercent(newPos));
}
const pan = PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderMove: (_, { moveX, moveY }) => {
circle.current.measure((x, y, width, height, px, py) => {
const newPos = calculateMovement(moveX - px, moveY - py, radius, strokeWidth);
/**
* @TODO
*/
if ((newPos < -0.3 && pos > 1.3)
|| (newPos > 1.3 && pos < -0.3)) {
return;
}
setPos(newPos);
onChange(posToPercent(newPos));
});
}
});
const d = `
M ${x2.toFixed(3)} ${y2.toFixed(3)}
A ${radius} ${radius}
${(pos < 0.5) ? '1' : '0'} ${(pos > 0.5) ? '1' : '0'} 0
${x1.toFixed(3)} ${y1.toFixed(3)}
`;
return (
<Svg height={size} width={size} ref={circle}>
<Defs>
<LinearGradient id="grad" x1="0" y1="0" x2="100%" y2="0">
<Stop offset="0" stopColor={gradient[0]} />
<Stop offset="1" stopColor={gradient[1]} />
</LinearGradient>
</Defs>
<G transform={{ translate: `${strokeWidth / 2 + radius + padding}, ${strokeWidth / 2 + radius + padding}` }}>
<Circle
r={radius}
strokeWidth={strokeWidth}
fill="transparent"
stroke={backgroundColor}
/>
<Path
d={d}
strokeWidth={strokeWidth}
stroke={`url(#grad)`}
fill="none"
/>
</G>
<G transform={{ translate: `${center + padding}, ${strokeWidth / 2 + padding}` }}>
<Circle r={(strokeWidth) / 2} fill={backgroundColor} />
</G>
{steps && steps.map((step, index) => (
<G transform={{ translate: `${step.x + padding}, ${step.y + padding}` }} key={index}>
<Circle
r={strokeWidth}
fill="transparent"
strokeWidth="12"
onPress={() => goToPercent(step.p)}
/>
<Circle
r={(strokeWidth / 2.5) / 2}
fill={stepColor}
strokeWidth="12"
onPress={() => goToPercent(step.p)}
/>
</G>
))}
<G transform={{ translate: `${endX + padding}, ${endY + padding}` }} {...pan.panHandlers}>
<Circle
r={(strokeWidth) / 2 + (padding / 2)}
fill={gradient[1]}
stroke={borderColor}
strokeWidth={padding / 1.5}
/>
</G>
{children && (
<View style={{ height: size, alignItems: 'center', justifyContent: 'center' }}>
<View>{children}</View>
</View>
)}
</Svg>
);
}
Example #16
Source File: SingleShapeView.js From geometry_3d with MIT License | 4 votes |
function SingleShapeView(props) {
const { shape, edges, points, newText, oldText, newTextGeo } = props;
/*const [cameraHandler, setCameraHandler] = useState(null);
const [renderer, setRenderer] = useState(null);
const [camera, setCamera] = useState(null);
const [scene, setScene] = useState(null);*/
let clonePoints = points
? points.map((item) => {
return {
...item,
text: item.text.clone(),
};
})
: [];
useEffect(() => {
//console.log(oldText);
const shapePosition = shape.position;
if (newText !== "" && oldText !== "" && newTextGeo) {
const { scene, cameraHandler } = props.singleShapeComponents;
for (let point of clonePoints) {
//console.log(point.trueText);
if (point.trueText === newText) {
//console.log("found");
const oldTexGeo = scene.getObjectByName(oldText);
scene.remove(oldTexGeo);
point.text = newTextGeo.clone();
scene.add(point.text);
let { x, y, z } = point.text.position;
point.text.position.set(
x - shapePosition.x,
y - shapePosition.y,
z - shapePosition.z
);
cameraHandler.addObjectsToTrack([point.text]);
break;
}
}
}
}, [newText, newTextGeo, oldText]);
const _transformEvent = (event) => {
event.preventDefault = event.preventDefault || (() => {});
event.stopPropagation = event.stopPropagation || (() => {});
return event;
};
const handleStartShouldSetPanResponder = () => {
return true;
};
// We were granted responder status! Let's update the UI
const handlePanResponderGrant = (e, gestureState) => {
const event = _transformEvent({ ...e, gestureState });
const { cameraHandler } = props.singleShapeComponents;
if (cameraHandler) cameraHandler.handlePanResponderGrant(event.nativeEvent);
};
// Every time the touch/mouse moves
const handlePanResponderMove = (e, gestureState) => {
// Keep track of how far we've moved in total (dx and dy)
const event = _transformEvent({ ...e, gestureState });
const { cameraHandler } = props.singleShapeComponents;
cameraHandler.handlePanResponderMove(event.nativeEvent, gestureState);
};
// When the touch/mouse is lifted
const handlePanResponderEnd = (e, gestureState) => {
const { cameraHandler } = props.singleShapeComponents;
const event = _transformEvent({ ...e, gestureState });
cameraHandler.handlePanResponderEnd(event.nativeEvent);
};
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: handleStartShouldSetPanResponder,
onPanResponderGrant: handlePanResponderGrant,
onPanResponderMove: handlePanResponderMove,
onPanResponderRelease: handlePanResponderEnd,
onPanResponderTerminate: handlePanResponderEnd,
onShouldBlockNativeResponder: () => false,
onPanResponderTerminationRequest: () => true,
});
const fitCameraToObject = (camera, object) => {
let offset = 4;
const boundingBox = new THREE.Box3();
// get bounding box of object - this will be used to setup controls and camera
boundingBox.setFromObject(object);
//const center = boundingBox.getCenter();
const size = boundingBox.getSize();
// get the max side of the bounding box (fits to width OR height as needed )
const maxDim = Math.max(size.x, size.y, size.z);
const fov = camera.fov * (Math.PI / 180);
let cameraZ = Math.abs((maxDim / 4) * Math.tan(fov * 2));
cameraZ *= offset; // zoom out a little so that objects don't fill the screen
camera.position.z = cameraZ;
const minZ = boundingBox.min.z;
const cameraToFarEdge = minZ < 0 ? -minZ + cameraZ : cameraZ - minZ;
camera.far = cameraToFarEdge * 3;
camera.updateProjectionMatrix();
return cameraZ;
};
const onContextCreate = ({ gl, width, height, scale }) => {
console.log(points.length);
let _renderer = new ExpoTHREE.Renderer({ gl });
_renderer.setPixelRatio(scale);
_renderer.setSize(width, height);
_renderer.setClearColor(0x000000, 1.0);
//console.log(renderer.domElement)
let shapePosition = shape.position;
const cloneShape = shape.clone();
const cloneEdges = edges.clone();
cloneShape.position.set(0, 0, 0);
cloneEdges.position.set(0, 0, 0);
let _scene = new THREE.Scene();
let _camera = new THREE.PerspectiveCamera(70, width / height, 0.1, 10000);
const baseDistance = 10; //fitCameraToObject(_camera, cloneShape);
let _cameraHandler = new UniCameraHandler(_camera, baseDistance);
for (let point of clonePoints) {
const textGeo = point.text;
textGeo.name = point.trueText;
let { x, y, z } = textGeo.position;
//console.log(cloneShape.position);
textGeo.position.set(
x - shapePosition.x,
y - shapePosition.y,
z - shapePosition.z
);
//console.log(textGeo.position);
_scene.add(textGeo);
}
const axisHelper = new THREE.AxesHelper(200);
cloneShape.add(axisHelper);
_scene.add(cloneShape, cloneEdges);
props.reduxSetSingleShapeComponents({
cameraHandler: _cameraHandler,
camera: _camera,
renderer: _renderer,
scene: _scene,
});
};
const onRender = () => {
let {
cameraHandler,
renderer,
camera,
scene,
} = props.singleShapeComponents;
cameraHandler.render(clonePoints);
renderer.render(scene, camera);
};
return (
<View
{...panResponder.panHandlers}
style={{
flex: 1,
borderWidth: 1,
borderRadius: 5,
}}
>
<ExpoGraphics.View
// style={{ flex: 1 }}
onContextCreate={(props) => {
onContextCreate(props);
}}
onRender={(_props) => onRender(_props)}
arEnabled={false}
onShouldReloadContext={() => true}
/>
</View>
);
}
Example #17
Source File: LayoutSetup.js From geometry_3d with MIT License | 4 votes |
function LayoutSetup(props) {
const raw_font = require("../../assets/fonts/bebas_neue.typeface.json");
const font = new THREE.Font(raw_font);
const [pan, setPan] = useState(new Animated.ValueXY());
const [mouse, setMouse] = useState(new THREE.Vector2(-10, -10));
const [val, setVal] = useState({ x: 0, y: 0 });
const [isLock, setIsLock] = useState(false);
const _transformEvent = (event) => {
event.preventDefault = event.preventDefault || (() => {});
event.stopPropagation = event.stopPropagation || (() => {});
return event;
};
//Should we become active when the user presses down on the square?
const handleStartShouldSetPanResponder = () => {
return true;
};
// We were granted responder status! Let's update the UI
const handlePanResponderGrant = (e, gestureState) => {
const event = _transformEvent({ ...e, gestureState });
const disableCamera = props.miscData.disableCamera;
if (disableCamera) {
pan.setOffset({
x: val.x,
y: val.y,
});
pan.setValue({ x: -10, y: -10 });
mouse.x = (event.nativeEvent.pageX / width) * 2 - 1;
mouse.y = -(event.nativeEvent.pageY / height) * 2 + 1;
//props.basicComponents.controls.onDocumentTouchStart(mouse);
}
if (!disableCamera)
props.basicComponents.cameraHandler.handlePanResponderGrant(
event.nativeEvent
);
};
// Every time the touch/mouse moves
const handlePanResponderMove = (e, gestureState) => {
// Keep track of how far we've moved in total (dx and dy)
const event = _transformEvent({ ...e, gestureState });
const disableCamera = props.miscData.disableCamera;
if (disableCamera) {
mouse.x = (event.nativeEvent.pageX / width) * 2 - 1;
mouse.y = -(event.nativeEvent.pageY / height) * 2 + 1;
//props.basicComponents.controls.onDocumentMouseMove(mouse);
}
if (!disableCamera)
props.basicComponents.cameraHandler.handlePanResponderMove(
event.nativeEvent,
gestureState
);
};
// When the touch/mouse is lifted
const handlePanResponderEnd = (e, gestureState) => {
const event = _transformEvent({ ...e, gestureState });
//clearTimeout(longPressTimeout);
const disableCamera = props.miscData.disableCamera;
if (disableCamera) {
mouse.x = -10;
mouse.y = -10;
//props.basicComponents.controls.onDocumentMouseCancel();
}
if (!disableCamera)
props.basicComponents.cameraHandler.handlePanResponderEnd(
event.nativeEvent
);
};
pan.addListener((value) => setVal(() => value));
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: handleStartShouldSetPanResponder,
onPanResponderGrant: handlePanResponderGrant,
onPanResponderMove: handlePanResponderMove,
onPanResponderRelease: handlePanResponderEnd,
onPanResponderTerminate: handlePanResponderEnd,
onShouldBlockNativeResponder: () => false,
onPanResponderTerminationRequest: () => true,
});
const updatePoints = () => {
if (props.basicComponents.points != null) {
props.getPointsCallback(
props.basicComponents.points.map((item) => {
return {
point: item.position,
text: item.trueText,
item: item,
};
})
);
}
}; //, [props.basicComponents.points]);
const loadFont = (listOfVertices) => {
let listOfObjects = [];
for (let item of listOfVertices) {
const vertex = item.point;
const textGeo = new THREE.TextGeometry(item.text, {
font: font,
size: 0.5,
height: 0.01,
});
let textMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
});
let text = new THREE.Mesh(textGeo, textMaterial);
props.basicComponents.scene.add(text);
text.position.set(vertex.x, vertex.y, vertex.z);
text.quaternion.copy(props.basicComponents.camera.quaternion);
let point = {
text: text,
position: vertex,
trueText: item.text,
};
listOfObjects.push(point);
}
let holder =
props.basicComponents.points == null ? [] : props.basicComponents.points;
props.reduxSetPoint([...listOfObjects, ...holder]);
updatePoints();
return listOfObjects;
};
const onContextCreate = ({ gl, width, height, scale }) => {
if (props.basicComponents.scene) props.basicComponents.scene.dispose();
props.reduxSetPoint([]);
props.reduxSetLine([]);
props.reduxSetShape([]);
props.reduxSetDisableCamera(false);
//props.reduxSetControls(null);
let renderer = new ExpoTHREE.Renderer({ gl });
renderer.setPixelRatio(scale);
renderer.setSize(width, height);
renderer.setClearColor(0x000000, 1.0);
//console.log(renderer.domElement)
let scene = new THREE.Scene();
let camera = new THREE.PerspectiveCamera(100, width / height, 0.1, 1000);
//grid size = 2pixel
let geometry = null;
switch (props.initShape) {
case "cube": {
geometry = new THREE.BoxBufferGeometry(5, 5, 5);
//numVertices = 8
break;
}
case "cone": {
geometry = new THREE.ConeBufferGeometry(5, 10, 32);
//numVertices = 1
break;
}
case "sphere": {
geometry = new THREE.SphereBufferGeometry(5, 20, 20);
//numVertices = 1
break;
}
case "octahedron": {
geometry = new THREE.OctahedronBufferGeometry(5, 0);
//numVertices = 6
break;
}
case "prism": {
geometry = new THREE.CylinderBufferGeometry(5, 5, 10, 3);
break;
}
default: {
if (props.savedState) {
loadSavedState(props, scene, updatePoints);
}
break;
}
}
const material = new THREE.MeshBasicMaterial({
color: 0xe7ff37,
opacity: 0.5,
transparent: true,
side: THREE.DoubleSide,
});
const axisHelper = new THREE.AxesHelper(200);
const cameraHandler = new UniCameraHandler(camera);
props.getCam(cameraHandler);
const plane = new THREE.GridHelper(200, 200, "#ff3700");
scene.add(plane, axisHelper);
props.reduxSetBasicComponents({
camera: camera,
cameraHandler: cameraHandler,
scene: scene,
renderer: renderer,
});
if (geometry) {
let mesh = new THREE.Mesh(geometry, material);
let edges = new THREE.EdgesGeometry(geometry);
let line = new THREE.LineSegments(
edges,
new THREE.LineBasicMaterial({ color: 0xffffff })
);
//let wrapper = new THREE.Object3D();
//wrapper.add(mesh, line);
scene.add(mesh, line);
const holder = loadFont(getVerticesWithText(mesh, props.initShape));
//console.log(holder)
props.reduxSetShape([
{
object: mesh,
edges: line,
color: "#e7ff37",
name: "default",
type: props.initShape,
id: 0,
position: new THREE.Vector3(0, 0, 0),
rotation: new THREE.Vector3(0, 0, 0),
points: holder,
},
]);
props.getShapesCallback(props.basicComponents.shapes);
}
};
const onRender = (delta) => {
props.basicComponents.cameraHandler.render(props.basicComponents.points);
props.basicComponents.renderer.render(
props.basicComponents.scene,
props.basicComponents.camera
);
};
useEffect(() => {
if (props.pointsEdit && props.pointsEdit.length > 0) {
switch (props.action) {
case "add_points": {
addPoints(loadFont, props.pointsEdit);
break;
}
case "remove_points": {
removePoints(props, updatePoints, props.pointsEdit);
break;
}
default: {
break;
}
}
}
}, [props.signalEditPoints]);
useEffect(() => {
if (props.pointsConnect && props.pointsConnect.length > 0) {
switch (props.action) {
case "connect_points": {
connectPoints(props, loadFont, props.pointsConnect);
break;
}
case "disconnect_points": {
disconnectPoints(props, props.pointsConnect);
break;
}
default: {
break;
}
}
}
}, [props.signalPoints]);
useEffect(() => {
if (props.shapesConnect && props.shapesConnect.length > 0) {
switch (props.action) {
case "add_shapes": {
addShapes(props, props.shapesConnect, updatePoints);
break;
}
case "remove_shapes": {
removeShapes(props, props.shapesConnect);
break;
}
default: {
break;
}
}
}
}, [props.signalShapes]);
return (
<>
<View
{...panResponder.panHandlers}
style={{
flex: 1,
overflow: "hidden",
width: "100%",
height: "100%",
}}
>
<ExpoGraphics.View
style={{ flex: 1 }}
onContextCreate={(props) => {
onContextCreate(props);
}}
onRender={(_props) => onRender(_props)}
arEnabled={false}
onShouldReloadContext={() => true}
/>
</View>
<TouchableOpacity
style={{
backgroundColor: "black",
top: 20,
right: 90,
position: "absolute",
paddingVertical: 5,
paddingHorizontal: 10,
borderRadius: 5,
borderColor: "red",
borderWidth: 2,
}}
onPress={() => {
props.reduxSetDisableCamera(!props.miscData.disableCamera);
setIsLock(() => !isLock);
}}
>
<Text
style={{
color: "red",
}}
>
{isLock ? "Unlock" : "Lock"}
</Text>
</TouchableOpacity>
<TouchableOpacity
style={{
backgroundColor: "black",
top: 20,
right: 20,
position: "absolute",
paddingVertical: 5,
paddingHorizontal: 10,
borderRadius: 5,
borderColor: "white",
borderWidth: 2,
}}
onPress={() => {
const currentItem = {
shapes: props.basicComponents.shapes,
lines: props.basicComponents.lines,
points: props.basicComponents.points,
name: formatDateTime(new Date()),
fileName: Date.now(),
isSynced: false,
url: "",
};
props.reduxAddSaveItem(currentItem);
Toast.show({
type: "success",
position: "top",
text1: "Current state saved",
text2: "Success",
visibilityTime: 3000,
autoHide: true,
});
}}
>
<Text
style={{
color: "white",
}}
>
Save
</Text>
</TouchableOpacity>
</>
);
}
Example #18
Source File: index.js From designcode-app with MIT License | 4 votes |
export default function Projects() {
const [index, setIndex] = useState(0);
const [pan, setPan] = useState(new Animated.ValueXY());
const [maskOpacity, setMaskOpacity] = useState(new Animated.Value(0));
const [scale, setScale] = useState(new Animated.Value(0.9));
const [translateY, setTranslateY] = useState(new Animated.Value(44));
const [thirdScale, setThirdScale] = useState(new Animated.Value(0.8));
const [thirdTranslateY, setThirdTranslateY] = useState(
new Animated.Value(-50)
);
const action = store.getState().app.action;
const panResponder = useMemo(() => {
return PanResponder.create({
onMoveShouldSetPanResponder: (event, gestureState) => {
if (gestureState.dx === 0 && gestureState.dy === 0) {
return false;
} else {
if (store.getState().app.action === "openCard") {
} else {
return true;
}
}
},
onPanResponderGrant: () => {
Animated.spring(scale, { toValue: 1, useNativeDriver: false }).start();
Animated.spring(translateY, {
toValue: 0,
useNativeDriver: false, //true
}).start();
Animated.spring(thirdScale, {
toValue: 0.9,
useNativeDriver: false, //true
}).start();
Animated.spring(thirdTranslateY, {
toValue: 44,
useNativeDriver: false, //true
}).start();
Animated.timing(maskOpacity, {
toValue: 1,
useNativeDriver: false, //true
}).start();
},
onPanResponderMove: Animated.event([null, { dx: pan.x, dy: pan.y }], {
useNativeDriver: false,
}),
onPanResponderRelease: () => {
const positionY = pan.y.__getValue();
Animated.timing(maskOpacity, {
toValue: 0,
useNativeDriver: false, //true
}).start();
if (positionY > 200) {
Animated.timing(pan, {
toValue: { x: 0, y: 1000 },
useNativeDriver: false, //true
}).start(() => {
pan.setValue({ x: 0, y: 0 });
scale.setValue(0.9);
translateY.setValue(44);
thirdScale.setValue(0.8);
thirdTranslateY.setValue(-50);
setIndex(getNextIndex(index));
});
} else {
Animated.spring(pan, {
toValue: { x: 0, y: 0 },
useNativeDriver: false, //true
}).start();
Animated.spring(scale, {
toValue: 0.9,
useNativeDriver: false, //true
}).start();
Animated.spring(translateY, {
toValue: 44,
useNativeDriver: false, //true
}).start();
Animated.spring(thirdScale, {
toValue: 0.8,
useNativeDriver: false, //true
}).start();
Animated.spring(thirdTranslateY, {
toValue: -50,
useNativeDriver: false, //true
}).start();
}
},
});
}, [index]);
return (
<Container>
<AnimatedMask style={{ opacity: maskOpacity }} />
<Animated.View
style={{
transform: [{ translateX: pan.x }, { translateY: pan.y }],
}}
{...panResponder.panHandlers}
>
<Project
title={projects[index].title}
image={projects[index].image}
author={projects[index].author}
text={projects[index].text}
canOpen={true}
/>
</Animated.View>
<Animated.View
style={{
position: "absolute",
top: 0,
left: 0,
zIndex: -1,
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center",
transform: [{ scale: scale }, { translateY: translateY }],
}}
>
<Project
title={projects[getNextIndex(index)].title}
image={projects[getNextIndex(index)].image}
author={projects[getNextIndex(index)].author}
text={projects[getNextIndex(index)].text}
/>
</Animated.View>
<Animated.View
style={{
position: "absolute",
top: 0,
left: 0,
zIndex: -3,
width: "100%",
height: "100%",
justifyContent: "center",
alignItems: "center",
transform: [{ scale: thirdScale }, { translateY: thirdTranslateY }],
}}
>
<Project
title={projects[getNextIndex(index + 1)].title}
image={projects[getNextIndex(index + 1)].image}
author={projects[getNextIndex(index + 1)].author}
text={projects[getNextIndex(index + 1)].text}
/>
</Animated.View>
</Container>
);
}
Example #19
Source File: PullRefreshTabView.js From react-native-collapsible-tabview with MIT License | 4 votes |
App = () => {
/**
* stats
*/
const [tabIndex, setIndex] = useState(0);
const [routes] = useState([
{key: 'tab1', title: 'Tab1'},
{key: 'tab2', title: 'Tab2'},
]);
const [canScroll, setCanScroll] = useState(true);
const [tab1Data] = useState(Array(40).fill(0));
const [tab2Data] = useState(Array(30).fill(0));
/**
* ref
*/
const scrollY = useRef(new Animated.Value(0)).current;
const headerScrollY = useRef(new Animated.Value(0)).current;
// for capturing header scroll on Android
const headerMoveScrollY = useRef(new Animated.Value(0)).current;
const listRefArr = useRef([]);
const listOffset = useRef({});
const isListGliding = useRef(false);
const headerScrollStart = useRef(0);
const _tabIndex = useRef(0);
const refreshStatusRef = useRef(false);
/**
* PanResponder for header
*/
const headerPanResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponderCapture: (evt, gestureState) => false,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => false,
onStartShouldSetPanResponder: (evt, gestureState) => {
headerScrollY.stopAnimation();
syncScrollOffset();
return false;
},
onMoveShouldSetPanResponder: (evt, gestureState) => {
headerScrollY.stopAnimation();
return Math.abs(gestureState.dy) > 5;
},
onPanResponderEnd: (evt, gestureState) => {
handlePanReleaseOrEnd(evt, gestureState);
},
onPanResponderMove: (evt, gestureState) => {
const curListRef = listRefArr.current.find(
(ref) => ref.key === routes[_tabIndex.current].key,
);
const headerScrollOffset = -gestureState.dy + headerScrollStart.current;
if (curListRef.value) {
// scroll up
if (headerScrollOffset > 0) {
curListRef.value.scrollToOffset({
offset: headerScrollOffset,
animated: false,
});
// start pull down
} else {
if (Platform.OS === 'ios') {
curListRef.value.scrollToOffset({
offset: headerScrollOffset / 3,
animated: false,
});
} else if (Platform.OS === 'android') {
if (!refreshStatusRef.current) {
headerMoveScrollY.setValue(headerScrollOffset / 1.5);
}
}
}
}
},
onShouldBlockNativeResponder: () => true,
onPanResponderGrant: (evt, gestureState) => {
headerScrollStart.current = scrollY._value;
},
}),
).current;
/**
* PanResponder for list in tab scene
*/
const listPanResponder = useRef(
PanResponder.create({
onStartShouldSetPanResponderCapture: (evt, gestureState) => false,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => false,
onStartShouldSetPanResponder: (evt, gestureState) => false,
onMoveShouldSetPanResponder: (evt, gestureState) => {
headerScrollY.stopAnimation();
return false;
},
onShouldBlockNativeResponder: () => true,
onPanResponderGrant: (evt, gestureState) => {
headerScrollY.stopAnimation();
},
}),
).current;
/**
* effect
*/
useEffect(() => {
scrollY.addListener(({value}) => {
const curRoute = routes[tabIndex].key;
listOffset.current[curRoute] = value;
});
headerScrollY.addListener(({value}) => {
listRefArr.current.forEach((item) => {
if (item.key !== routes[tabIndex].key) {
return;
}
if (value > HeaderHeight || value < 0) {
headerScrollY.stopAnimation();
syncScrollOffset();
}
if (item.value && value <= HeaderHeight) {
item.value.scrollToOffset({
offset: value,
animated: false,
});
}
});
});
return () => {
scrollY.removeAllListeners();
headerScrollY.removeAllListeners();
};
}, [routes, tabIndex]);
/**
* helper functions
*/
const syncScrollOffset = () => {
const curRouteKey = routes[_tabIndex.current].key;
listRefArr.current.forEach((item) => {
if (item.key !== curRouteKey) {
if (scrollY._value < HeaderHeight && scrollY._value >= 0) {
if (item.value) {
item.value.scrollToOffset({
offset: scrollY._value,
animated: false,
});
listOffset.current[item.key] = scrollY._value;
}
} else if (scrollY._value >= HeaderHeight) {
if (
listOffset.current[item.key] < HeaderHeight ||
listOffset.current[item.key] == null
) {
if (item.value) {
item.value.scrollToOffset({
offset: HeaderHeight,
animated: false,
});
listOffset.current[item.key] = HeaderHeight;
}
}
}
}
});
};
const startRefreshAction = () => {
if (Platform.OS === 'ios') {
listRefArr.current.forEach((listRef) => {
listRef.value.scrollToOffset({
offset: -50,
animated: true,
});
});
refresh().finally(() => {
syncScrollOffset();
// do not bounce back if user scroll to another position
if (scrollY._value < 0) {
listRefArr.current.forEach((listRef) => {
listRef.value.scrollToOffset({
offset: 0,
animated: true,
});
});
}
});
} else if (Platform.OS === 'android') {
Animated.timing(headerMoveScrollY, {
toValue: -150,
duration: 300,
useNativeDriver: true,
}).start();
refresh().finally(() => {
Animated.timing(headerMoveScrollY, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
});
}
};
const handlePanReleaseOrEnd = (evt, gestureState) => {
// console.log('handlePanReleaseOrEnd', scrollY._value);
syncScrollOffset();
headerScrollY.setValue(scrollY._value);
if (Platform.OS === 'ios') {
if (scrollY._value < 0) {
if (scrollY._value < -PullToRefreshDist && !refreshStatusRef.current) {
startRefreshAction();
} else {
// should bounce back
listRefArr.current.forEach((listRef) => {
listRef.value.scrollToOffset({
offset: 0,
animated: true,
});
});
}
} else {
if (Math.abs(gestureState.vy) < 0.2) {
return;
}
Animated.decay(headerScrollY, {
velocity: -gestureState.vy,
useNativeDriver: true,
}).start(() => {
syncScrollOffset();
});
}
} else if (Platform.OS === 'android') {
if (
headerMoveScrollY._value < 0 &&
headerMoveScrollY._value / 1.5 < -PullToRefreshDist
) {
startRefreshAction();
} else {
Animated.timing(headerMoveScrollY, {
toValue: 0,
duration: 300,
useNativeDriver: true,
}).start();
}
}
};
const onMomentumScrollBegin = () => {
isListGliding.current = true;
};
const onMomentumScrollEnd = () => {
isListGliding.current = false;
syncScrollOffset();
// console.log('onMomentumScrollEnd');
};
const onScrollEndDrag = (e) => {
syncScrollOffset();
const offsetY = e.nativeEvent.contentOffset.y;
// console.log('onScrollEndDrag', offsetY);
// iOS only
if (Platform.OS === 'ios') {
if (offsetY < -PullToRefreshDist && !refreshStatusRef.current) {
startRefreshAction();
}
}
// check pull to refresh
};
const refresh = async () => {
console.log('-- start refresh');
refreshStatusRef.current = true;
await new Promise((resolve, reject) => {
setTimeout(() => {
resolve('done');
}, 2000);
}).then((value) => {
console.log('-- refresh done!');
refreshStatusRef.current = false;
});
};
/**
* render Helper
*/
const renderHeader = () => {
const y = scrollY.interpolate({
inputRange: [0, HeaderHeight],
outputRange: [0, -HeaderHeight],
extrapolateRight: 'clamp',
// extrapolate: 'clamp',
});
return (
<Animated.View
{...headerPanResponder.panHandlers}
style={[styles.header, {transform: [{translateY: y}]}]}>
<TouchableOpacity
style={{flex: 1, justifyContent: 'center'}}
activeOpacity={1}
onPress={() => Alert.alert('header Clicked!')}>
<Text>Pull to Refresh Header</Text>
</TouchableOpacity>
</Animated.View>
);
};
const rednerTab1Item = ({item, index}) => {
return (
<View
style={{
borderRadius: 16,
marginLeft: index % 2 === 0 ? 0 : 10,
width: tab1ItemSize,
height: tab1ItemSize,
backgroundColor: '#aaa',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>{index}</Text>
</View>
);
};
const rednerTab2Item = ({item, index}) => {
return (
<View
style={{
marginLeft: index % 3 === 0 ? 0 : 10,
borderRadius: 16,
width: tab2ItemSize,
height: tab2ItemSize,
backgroundColor: '#aaa',
justifyContent: 'center',
alignItems: 'center',
}}>
<Text>{index}</Text>
</View>
);
};
const renderLabel = ({route, focused}) => {
return (
<Text style={[styles.label, {opacity: focused ? 1 : 0.5}]}>
{route.title}
</Text>
);
};
const renderScene = ({route}) => {
const focused = route.key === routes[tabIndex].key;
let numCols;
let data;
let renderItem;
switch (route.key) {
case 'tab1':
numCols = 2;
data = tab1Data;
renderItem = rednerTab1Item;
break;
case 'tab2':
numCols = 3;
data = tab2Data;
renderItem = rednerTab2Item;
break;
default:
return null;
}
return (
<Animated.FlatList
scrollToOverflowEnabled={true}
// scrollEnabled={canScroll}
{...listPanResponder.panHandlers}
numColumns={numCols}
ref={(ref) => {
if (ref) {
const found = listRefArr.current.find((e) => e.key === route.key);
if (!found) {
listRefArr.current.push({
key: route.key,
value: ref,
});
}
}
}}
scrollEventThrottle={16}
onScroll={
focused
? Animated.event(
[
{
nativeEvent: {contentOffset: {y: scrollY}},
},
],
{useNativeDriver: true},
)
: null
}
onMomentumScrollBegin={onMomentumScrollBegin}
onScrollEndDrag={onScrollEndDrag}
onMomentumScrollEnd={onMomentumScrollEnd}
ItemSeparatorComponent={() => <View style={{height: 10}} />}
ListHeaderComponent={() => <View style={{height: 10}} />}
contentContainerStyle={{
paddingTop: HeaderHeight + TabBarHeight,
paddingHorizontal: 10,
minHeight: windowHeight - SafeStatusBar + HeaderHeight,
}}
showsHorizontalScrollIndicator={false}
data={data}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
keyExtractor={(item, index) => index.toString()}
/>
);
};
const renderTabBar = (props) => {
const y = scrollY.interpolate({
inputRange: [0, HeaderHeight],
outputRange: [HeaderHeight, 0],
// extrapolate: 'clamp',
extrapolateRight: 'clamp',
});
return (
<Animated.View
style={{
top: 0,
zIndex: 1,
position: 'absolute',
transform: [{translateY: y}],
width: '100%',
}}>
<TabBar
{...props}
onTabPress={({route, preventDefault}) => {
if (isListGliding.current) {
preventDefault();
}
}}
style={styles.tab}
renderLabel={renderLabel}
indicatorStyle={styles.indicator}
/>
</Animated.View>
);
};
const renderTabView = () => {
return (
<TabView
onSwipeStart={() => setCanScroll(false)}
onSwipeEnd={() => setCanScroll(true)}
onIndexChange={(id) => {
_tabIndex.current = id;
setIndex(id);
}}
navigationState={{index: tabIndex, routes}}
renderScene={renderScene}
renderTabBar={renderTabBar}
initialLayout={{
height: 0,
width: windowWidth,
}}
/>
);
};
const renderCustomRefresh = () => {
// headerMoveScrollY
return Platform.select({
ios: (
<AnimatedIndicator
style={{
top: -50,
position: 'absolute',
alignSelf: 'center',
transform: [
{
translateY: scrollY.interpolate({
inputRange: [-100, 0],
outputRange: [120, 0],
extrapolate: 'clamp',
}),
},
],
}}
animating
/>
),
android: (
<Animated.View
style={{
transform: [
{
translateY: headerMoveScrollY.interpolate({
inputRange: [-300, 0],
outputRange: [150, 0],
extrapolate: 'clamp',
}),
},
],
backgroundColor: '#eee',
height: 38,
width: 38,
borderRadius: 19,
borderWidth: 2,
borderColor: '#ddd',
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
top: -50,
position: 'absolute',
}}>
<ActivityIndicator animating />
</Animated.View>
),
});
};
return (
<View style={styles.container}>
{renderTabView()}
{renderHeader()}
{renderCustomRefresh()}
</View>
);
}