Hi,
if you run this code it will work great on IOS but not on Android.
its look like the pan gestures are not working as expected.
the move not working the ListView stack at the bottom and if you touch for some time
it will go up.
Also you can see this project:
https://github.com/paramaggarwal/rn-paper-interface
This is the code below:
import React, {Component} from 'react';
import {
Animated,
Image,
Platform,
Dimensions,
ScrollView,
StatusBar,
StyleSheet,
PanResponder,
ListView,
Text,
View,
Slider
} from 'react-native';
const deviceHeight = Dimensions.get('window').height;
const deviceWidth = Dimensions.get('window').width; //full width
const videoWidth = deviceWidth;
const videoHeight = Math.round((deviceWidth / 16) * 9);
let AnimatedListView = Animated.createAnimatedComponent(ListView);
let panDiff = 240;
let cards = ["http://wdtprs.com/blog/wp-content/uploads/2015/05/C__Data_Users_DefApps_AppData_INTERNETEXPLORER_Temp_Saved-Images_0tmX52a-Imgur_zpsu8dmz3za.jpg",
"http://cdn3-www.comingsoon.net/assets/uploads/gallery/marvels-daredevil-1413056220/marvels-daredevil-poster-2.jpg",
"http://nerdreactor.com/wp-content/uploads/2016/06/Narcos.jpg"];
export default class TVPaper extends Component {
constructor(props) {
super(props);
let pan = new Animated.ValueXY();
let ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
isDocked: true,
pan: pan,
dataSource: ds.cloneWithRows(cards),
dockAnimation: pan.y.interpolate({
inputRange: [-panDiff, 0],
outputRange: [0, 1],
extrapolate: 'clamp'
})
}
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (evt, gestureState) => true,
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
onMoveShouldSetPanResponder: (evt, gestureState) => true,
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
onPanResponderGrant: this._handleOnPanResponderGrant.bind(this),
onPanResponderMove: this._handleOnPanResponderMove.bind(this),
onPanResponderRelease: this._handlePanResponderRelease.bind(this)
})
}
_handleOnPanResponderGrant() {
}
_handleOnPanResponderMove(evt, gestureState) {
console.log("moving...");
console.log("x", this.state.pan.x);
console.log("y", this.state.pan.y);
console.log("evt",gestureState.moveY);
Animated.event([null, { dx: this.state.pan.x, dy: this.state.pan.y }])
}
_handlePanResponderRelease(evt, gestureState) {
console.log("release");
let shouldToggle = this.state.isDocked ? (gestureState.dy < (-panDiff / 3)) : (gestureState.dy > (panDiff / 3))
if (!shouldToggle) {
// return to original position
Animated.spring(this.state.pan.y, {
toValue: this.state.isDocked ? 0 : 0
}).start();
} else {
// toggle between docked and zoomed
Animated.spring(this.state.pan.y, {
toValue: this.state.isDocked ? (-panDiff) : panDiff
}).start(() => {
this.setState({
isDocked: !this.state.isDocked,
dockAnimation: !this.state.isDocked ? this.state.pan.y.interpolate({
inputRange: [-panDiff, 0],
outputRange: [0, 1],
extrapolate: 'clamp'
}) : this.state.pan.y.interpolate({
inputRange: [0, panDiff],
outputRange: [0, 1],
extrapolate: 'clamp'
})
});
})
}
}
getListViewStyle() {
return [
styles.container,
{
borderWidth: 1,
borderColor: 'red',
width: this.state.dockAnimation.interpolate({
inputRange: [0, 1],
outputRange: [Dimensions.get('window').width, Dimensions.get('window').width * 2]
}),
},
{
transform: [
{
scale: this.state.dockAnimation.interpolate({
inputRange: [0, 1],
outputRange: [1, 0.5]
}),
},
{
translateX: this.state.dockAnimation.interpolate({
inputRange: [0, 1],
outputRange: [0, -(Dimensions.get('window').width)]
}),
},
{
translateY: this.state.dockAnimation.interpolate({
inputRange: [0, 1],
outputRange: [0, (Dimensions.get('window').height / 2)]
}),
}
],
}
];
}
render() {
return (
<View style={styles.box}>
<AnimatedListView
horizontal={true}
pagingEnabled={!this.state.isDocked}
style={this.getListViewStyle() }
{...this._panResponder.panHandlers}
dataSource={this.state.dataSource}
renderRow={ (rowData, i) => {
return (
<View style={{ flex: 1 }} key={i}>
<Image style={{ width: deviceWidth, height: deviceHeight, resizeMode: 'cover' }} source={{ uri: rowData }} />
</View>
)
} }
/>
</View>
);
}
}
const styles = StyleSheet.create({
box: {
flex: 1,
backgroundColor: 'black',
},
container: {
flex: 1,
},
});
+1
@yanirmanor I was faced with the same issue. Resolved by passing return true in each handler, like:
onStartShouldSetPanResponder: (e, gestureState) => {
console.log(gestureState);
return true;
},
onPanResponderEnd: (e, gestureState) => {
console.log(gestureState);
return true;
}
@Arkanine that not working.
can you pls send example that work.
@yanirmanor it works for me, the full example you can use is:
componentWillMount: function() {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponder: (e, gestureState) => {
console.log(gestureState);
return true;
},
onPanResponderEnd: (e, gestureState) => {
console.log(gestureState);
return true;
}
})
}
Its not working!
Please attached or write the code that works on Android.
import React, { Component,PropTypes} from 'react';
import {
StyleSheet,
View,
Dimensions,
Animated,
AppRegistry,
Alert,
Modal,
Text,
TouchableHighlight
} from 'react-native';
var alertMessage = 'Hello'
import MapView from 'react-native-maps';
import PanController from './PanController';
import PriceMarker from './AnimatedPriceMarker';
const screen = Dimensions.get('window');
const ASPECT_RATIO = screen.width / screen.height;
const LATITUDE = 37.78825;
const LONGITUDE = -122.4324;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
const ITEM_SPACING = 10;
const ITEM_PREVIEW = 10;
const ITEM_WIDTH = screen.width - (2 * ITEM_SPACING) - (2 * ITEM_PREVIEW);
const SNAP_WIDTH = ITEM_WIDTH + ITEM_SPACING;
const ITEM_PREVIEW_HEIGHT = 40;
const SCALE_END = screen.width / ITEM_WIDTH;
const BREAKPOINT1 = 246;
const BREAKPOINT2 = 350;
const ONE = new Animated.Value(1);
function getMarkerState(panX, panY, scrollY, i) {
const xLeft = (-SNAP_WIDTH * i) + (SNAP_WIDTH / 2);
const xRight = (-SNAP_WIDTH * i) - (SNAP_WIDTH / 2);
const xPos = -SNAP_WIDTH * i;
const isIndex = panX.interpolate({
inputRange: [xRight - 1, xRight, xLeft, xLeft + 1],
outputRange: [0, 1, 1, 0],
extrapolate: 'clamp',
});
const isNotIndex = panX.interpolate({
inputRange: [xRight - 1, xRight, xLeft, xLeft + 1],
outputRange: [1, 0, 0, 1],
extrapolate: 'clamp',
});
const center = panX.interpolate({
inputRange: [xPos - 10, xPos, xPos + 10],
outputRange: [0, 1, 0],
extrapolate: 'clamp',
});
const selected = panX.interpolate({
inputRange: [xRight, xPos, xLeft],
outputRange: [0, 1, 0],
extrapolate: 'clamp',
});
const translateY = Animated.multiply(isIndex, panY);
const translateX = panX;
const anim = Animated.multiply(isIndex, scrollY.interpolate({
inputRange: [0, BREAKPOINT1],
outputRange: [0, 1],
extrapolate: 'clamp',
}));
const scale = Animated.add(ONE, Animated.multiply(isIndex, scrollY.interpolate({
inputRange: [BREAKPOINT1, BREAKPOINT2],
outputRange: [0, SCALE_END - 1],
extrapolate: 'clamp',
})));
// [0 => 1]
let opacity = scrollY.interpolate({
inputRange: [BREAKPOINT1, BREAKPOINT2],
outputRange: [0, 1],
extrapolate: 'clamp',
});
// if i === index: [0 => 0]
// if i !== index: [0 => 1]
opacity = Animated.multiply(isNotIndex, opacity);
// if i === index: [1 => 1]
// if i !== index: [1 => 0]
opacity = opacity.interpolate({
inputRange: [0, 1],
outputRange: [1, 0],
});
let markerOpacity = scrollY.interpolate({
inputRange: [0, BREAKPOINT1],
outputRange: [0, 1],
extrapolate: 'clamp',
});
markerOpacity = Animated.multiply(isNotIndex, markerOpacity).interpolate({
inputRange: [0, 1],
outputRange: [1, 0],
});
const markerScale = selected.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.2],
});
return {
translateY,
translateX,
scale,
opacity,
anim,
center,
selected,
markerOpacity,
markerScale,
};
}
class AnimatedViews extends React.Component {
constructor(props) {
super(props);
const panX = new Animated.Value(0);
const panY = new Animated.Value(0);
const scrollY = panY.interpolate({
inputRange: [-1, 1],
outputRange: [1, -1],
});
const scrollX = panX.interpolate({
inputRange: [-1, 1],
outputRange: [1, -1],
});
const scale = scrollY.interpolate({
inputRange: [0, BREAKPOINT1],
outputRange: [1, 1.6],
extrapolate: 'clamp',
});
const translateY = scrollY.interpolate({
inputRange: [0, BREAKPOINT1],
outputRange: [0, -100],
extrapolate: 'clamp',
});
const markers = [
{
id: 0,
amount: 99,
coordinate: {
latitude: LATITUDE,
longitude: LONGITUDE,
},
},
{
id: 1,
amount: 199,
coordinate: {
latitude: LATITUDE + 0.004,
longitude: LONGITUDE - 0.004,
},
},
{
id: 2,
amount: 285,
coordinate: {
latitude: LATITUDE - 0.004,
longitude: LONGITUDE - 0.004,
},
},
];
const animations = markers.map((m, i) =>
getMarkerState(panX, panY, scrollY, i));
this.state = {
modalVisible: false,
panX,
panY,
animations,
index: 0,
canMoveHorizontal: true,
scrollY,
scrollX,
scale,
translateY,
markers,
region: new MapView.AnimatedRegion({
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}),
};
setModalVisible=(visible)=> {
this.setState({modalVisible: visible});
}
}
componentDidMount() {
const { region, panX, panY, scrollX, markers } = this.state;
panX.addListener(this.onPanXChange);
panY.addListener(this.onPanYChange);
region.stopAnimation();
region.timing({
latitude: scrollX.interpolate({
inputRange: markers.map((m, i) => i * SNAP_WIDTH),
outputRange: markers.map(m => m.coordinate.latitude),
}),
longitude: scrollX.interpolate({
inputRange: markers.map((m, i) => i * SNAP_WIDTH),
outputRange: markers.map(m => m.coordinate.longitude),
}),
duration: 0,
}).start();
}
onStartShouldSetPanResponder=(e) => {
console.log("value of geasture e",e);
// we only want to move the view if they are starting the gesture on top
// of the view, so this calculates that and returns true if so. If we return
// false, the gesture should get passed to the map view appropriately.
const { panY } = this.state;
const { pageY } = e.nativeEvent;
const topOfMainWindow = ITEM_PREVIEW_HEIGHT + panY.__getValue();
const topOfTap = screen.height - pageY;
return topOfTap < topOfMainWindow;
console.log("onStartShouldSetPanResponder",topOfTap,topOfMainWindow);
//setModalVisible(true);
/Alert.alert(
'Alert Title',
alertMessage,
[{text: 'Closed', onPress: () => setModalVisible(true)},]
)/
}
onMoveShouldSetPanResponder =(e)=> {
console.log("value of geasture e",e);
const { panY } = this.state;
const { pageY } = e.nativeEvent;
const topOfMainWindow = ITEM_PREVIEW_HEIGHT + panY.__getValue();
const topOfTap = screen.height - pageY;
console.log("onMoveShouldSetPanResponder",topOfTap,topOfMainWindow);
return topOfTap < topOfMainWindow;
}
onPanXChange({ value }) {
const { index } = this.state;
const newIndex = Math.floor(((-1 * value) + (SNAP_WIDTH / 2)) / SNAP_WIDTH);
if (index !== newIndex) {
this.setState({ index: newIndex });
}
}
onPanYChange({ value }) {
const { canMoveHorizontal, region, scrollY, scrollX, markers, index } = this.state;
const shouldBeMovable = Math.abs(value) < 2;
if (shouldBeMovable !== canMoveHorizontal) {
this.setState({ canMoveHorizontal: shouldBeMovable });
if (!shouldBeMovable) {
const { coordinate } = markers[index];
region.stopAnimation();
region.timing({
latitude: scrollY.interpolate({
inputRange: [0, BREAKPOINT1],
outputRange: [
coordinate.latitude,
coordinate.latitude - (LATITUDE_DELTA * 0.5 * 0.375),
],
extrapolate: 'clamp',
}),
latitudeDelta: scrollY.interpolate({
inputRange: [0, BREAKPOINT1],
outputRange: [LATITUDE_DELTA, LATITUDE_DELTA * 0.5],
extrapolate: 'clamp',
}),
longitudeDelta: scrollY.interpolate({
inputRange: [0, BREAKPOINT1],
outputRange: [LONGITUDE_DELTA, LONGITUDE_DELTA * 0.5],
extrapolate: 'clamp',
}),
duration: 0,
}).start();
} else {
region.stopAnimation();
region.timing({
latitude: scrollX.interpolate({
inputRange: markers.map((m, i) => i * SNAP_WIDTH),
outputRange: markers.map(m => m.coordinate.latitude),
}),
longitude: scrollX.interpolate({
inputRange: markers.map((m, i) => i * SNAP_WIDTH),
outputRange: markers.map(m => m.coordinate.longitude),
}),
duration: 0,
}).start();
}
}
}
onRegionChange(/* region */) {
// this.state.region.setValue(region);
}
render() {
const {
panX,
panY,
animations,
canMoveHorizontal,
markers,
region,
} = this.state;
return (
<View style={styles.container}>
<Modal
animationType={"slide"}
transparent={false}
visible={this.state.modalVisible}
onRequestClose={() => {alert("Modal has been closed.")}}
>
<View style={{marginTop: 22}}>
<View>
<Text>Hello World!</Text>
<TouchableHighlight onPress={() =>setModalVisible(false)
}>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
<PanController
style={styles.container}
vertical
horizontal
xMode="snap"
snapSpacingX={SNAP_WIDTH}
yBounds={[-1 * screen.height, 0]}
xBounds={[-screen.width * (markers.length - 1), 0]}
panY={panY}
panX={panX}
onStartShouldSetPanResponder={this.onStartShouldSetPanResponder}
onMoveShouldSetPanResponder={this.onMoveShouldSetPanResponder}
>
<MapView.Animated
provider={this.props.provider}
style={styles.map}
region={region}
onRegionChange={this.onRegionChange}
>
{markers.map((marker, i) => {
const {
selected,
markerOpacity,
markerScale,
} = animations[i];
return (
<MapView.Marker
key={marker.id}
coordinate={marker.coordinate}
>
<PriceMarker
style={{
opacity: markerOpacity,
transform: [
{ scale: markerScale },
],
}}
amount={marker.amount}
selected={selected}
/>
</MapView.Marker>
);
})}
</MapView.Animated>
<View style={styles.itemContainer}>
{markers.map((marker, i) => {
const {
translateY,
translateX,
scale,
opacity,
} = animations[i];
return (
<Animated.View
key={marker.id}
style={[styles.item, {
opacity,
transform: [
{ translateY },
{ translateX },
{ scale },
],
}]}
/>
);
})}
</View>
</PanController>
</View>
);
}
}
AnimatedViews.propTypes = {
provider: MapView.ProviderPropType,
};
const styles = StyleSheet.create({
container: {
...StyleSheet.absoluteFillObject,
},
itemContainer: {
backgroundColor: 'transparent',
flexDirection: 'row',
paddingHorizontal: (ITEM_SPACING / 2) + ITEM_PREVIEW,
position: 'absolute',
// top: screen.height - ITEM_PREVIEW_HEIGHT - 64,
paddingTop: screen.height - ITEM_PREVIEW_HEIGHT - 64,
//paddingTop: !ANDROID ? 0 : screen.height - ITEM_PREVIEW_HEIGHT - 64,
},
map: {
backgroundColor: 'transparent',
...StyleSheet.absoluteFillObject,
},
item: {
width: ITEM_WIDTH,
height: screen.height + (2 * ITEM_PREVIEW_HEIGHT),
backgroundColor: 'red',
marginHorizontal: ITEM_SPACING / 2,
overflow: 'hidden',
borderRadius: 3,
borderColor: '#000',
},
});
AppRegistry.registerComponent('AnimatedViews', ()=> AnimatedViews);
module.exports = AnimatedViews;
panresponser not working ios and android both
Panresonser move start method geasture property not working properly
onStartShouldSetPanResponder not working on MAPVIEW react native
I have the exact same behavior nativeEvent.locationX and nativeEvent.locationY do not update on Android. pageX and pageY do update on Android. @Arkanine 's suggestion to return true does not fix this.
This issue is pretty unclear. Lets continue the discussion in #12591 because it seems better articulated there
Most helpful comment
@yanirmanor I was faced with the same issue. Resolved by passing return true in each handler, like:
onStartShouldSetPanResponder: (e, gestureState) => {
console.log(gestureState);
return true;
},
onPanResponderEnd: (e, gestureState) => {
console.log(gestureState);
return true;
}