Играю с анимацией в React Native. Хотите создать экран, позволяющий пользователю касаться экрана в любой момент. Когда они это сделают, на месте их пальца должен появиться круг и следовать за ним. Когда они отпускаются, круг должен уменьшиться до нуля.
Я начал следовать этот учебник, который предназначен для анимации, похожей на пружину, в которой вы перетаскиваете уже существующий квадрат (который начинается в центре экрана), и он возвращается в исходное состояние при отпускании.
В моем случае у круга должно быть уже существующее местоположение нет, и это вызывает проблему. Если я инициализирую состояние своего компонента, скажем, с помощью pan: new Animated.ValueXY(), а затем касаюсь экрана, кружок начинается в верхнем левом углу, нет, где находится мой палец. Я пробовал множество других решений, все заменяя закомментированную строку в методе onPanResponderGrant ниже, но, похоже, не могу понять это.
Как сделать анимацию, в которой компонент просто отслеживает мой палец, никуда не запускаясь и не возвращаясь?
Вот код компонента:
class FollowCircle extends React.Component {
constructor(props) {
super(props);
this.state = {
pan: new Animated.ValueXY(),
scale: new Animated.Value(0)
};
}
componentWillMount() {
this._panResponder = PanResponder.create({
onStartShouldSetPanResponderCapture: () => true,
onMoveShouldSetPanResponderCapture: () => true,
onPanResponderGrant: (e, gestureState) => {
// This is the line I'm playing with
this.state.pan.setValue({ x: this.state.pan.x._value, y: this.state.pan.y._value });
Animated.spring(
this.state.scale,
{ toValue: 1, friction: 3 },
).start();
},
onPanResponderMove: Animated.event([
null, { dx: this.state.pan.x, dy: this.state.pan.y }
]),
onPanResponderRelease: (/* e, gestureState */) => {
this.state.pan.flattenOffset();
Animated.spring(
this.state.scale,
{
toValue: 0,
friction: 10,
restDisplacementThreshold: 1
},
).start();
}
});
}
render() {
const { pan, scale } = this.state;
const circleStyles = {
backgroundColor: 'black',
width: 50,
height: 50,
borderRadius: 25,
transform: [
{ translateX: pan.x },
{ translateY: pan.y },
{ scale }
]
};
return (
<AppWrapper navigation = { this.props.navigation }>
<Animated.View
style = { {
flex: 1,
width: Dimensions.get('window').width,
height: Dimensions.get('window').height,
backgroundColor: 'white'
} }
{ ...this._panResponder.panHandlers }
>
<Animated.View
style = { circleStyles }
/>
</Animated.View>
</AppWrapper>
);
}
};
И вот мои альтернативные попытки (замены закомментированной строки):
this.state.pan.setValue({ x: gestureState.x0, y: gestureState.y0 });
И этот:
if (this.state.pan) {
this.state.pan.setValue({ x: this.state.pan.x._value, y: this.state.pan.y._value });
} else {
this.setState({ pan: new Animated.ValueXY(gestureState.x0, gestureState.y0) });
}
Оба, кажется, ведут себя одинаково, сбрасывая положение круга в верхнем левом углу после каждого движения и не совсем отслеживая местоположение моего пальца, если я не начинаю касание в этом источнике.
Есть идеи, как это сделать правильно?



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


В итоге я понял это, заменив материал panResponder на TouchableHighlight размером со страницу с событием onTouch.
Это событие запустило следующую функцию, которая получает местоположение nativeEvent.pageX и pageY и использует его. Мне пришлось внести небольшие изменения в расположение y, чтобы оно работало на всех телефонах:
triggerSwipe({ nativeEvent }) {
const coordinates = { x: nativeEvent.pageX, y: nativeEvent.pageY };
const self = this;
if (!this.state.animating) {
this.setState({
x: coordinates.x,
y: coordinates.y + (this.windowHeight / 2.5),
animating: true
}, () => {
Animated.timing(
self.state.scale,
{ toValue: 20, duration: 600 },
).start(() => {
this.setState({
scale: new Animated.Value(0.01), // See Notes.md for reasoning
colorIndex: this.state.colorIndex + 1,
animating: false
});
});
});
}
Анимирующий круг представляет собой Animated.View внутри TouchableHighlight, которому передаются координаты касания.