It is possible to get the current animated value using ._value while nativeDriver is false.
But is it possible to get current value when nativeDriver is turned to true ?
Do we have any work arounds ?
use addListener like so:
this._animatedValue = new Animated.Value(0);
this._animatedValue.addListener(({value}) => this._value = value);
Animated.timing(this._animatedValue, {
toValue: 100,
duration: 500
}).start();
I figured out a workaround for scenarios like this, where the native driver spoils our offset and gives a jump in our animation towards the end.
Create a pseudo animated value (which will not be used for any animation) and keep updating it alongside your original animated value. Finally when your animation is over (like decay, spring, etc.) set the offsets and current angle with the pseudo value as below:
const angle = useRef(new Animated.Value(0)).current; //original value used in animation; will run on native side
const psuedoAngle = useRef(new Animated.Value(0)).current; //only to track the value on JS side
//using PanResponder
...
onPanResponderMove: (evt, gestureState) => {
angle.setValue(some_value);
psuedoAngle.setValue(same_value);
},
onPanResponderRelease: (evt, {vx, vy}) => {
// calculate your velocity
...
const decay1 = Animated.decay(angle,{
velocity: velocity,
deceleration: 0.997,
useNativeDriver: true
}
);
const decay2 = Animated.decay(psuedoAngle,{
velocity: velocity,
deceleration: 0.997,
useNativeDriver: false //this will keep your pseudo value intact
}
);
//set offset and current angle in callback as below
Animated.parallel([decay1,decay2]).start(() => {
psuedoAngle.flattenOffset();
angle.setOffset(psuedoAngle._value); //correct value passed to original value
psuedoAngle.setOffset(psuedoAngle._value);
angle.setValue(0);
psuedoAngle.setValue(0);
});
}
Related
I've started to play around with react-native-reanimated v2.
const fontSize = useSharedValue(25);
const config = {
duration: 500,
easing: Easing.bezier(0.5, 0.01, 0, 1),
};
const fontStyle = useAnimatedStyle(() => {
return {
fontSize: withTiming(fontSize.value, config, (isFinished) => {
if (isFinished) {
console.log('isFinished');
}
}),
};
});
return (
<Button
title="toggle"
onPress={() => {
fontSize.value = 1;
}}
/>
)
The above code block initially sets the font size to 25, and on press starts an animation where it gradually reduces it to 1.
When the animation is finished, I want to change the text.
However, the callback with isFinished is called instantly, before I press the button. The moment the app renders.
The code block is pretty much from their docs, so I'm unsure what I'm doing wrong.
Is it possible to make sure the callback is only called after the animation is finished? Not on render?
UPDATE
A hack I've put together is setting a state const [buttonPressed, setButtonPressed] = useState(false); and checking for this along with isFinished, and setting this to true when the button has been pressed.
But this is a horrible workaround.
I am developing an app where I can add multiple images, which can be dragged accross the whole screen. This works till a certain extend.
But what I am trying to do is saving the image, position (x, y) to firestore. I haven't been able to find some sample code that does that.
Anyone an idea?
Managed to find a code snippet for dragging and dropping a html object. According to this discussion, you can use element.getBoundingClientRect() method and have the shape's positions logged. Should look something like this:
var rect = element.getBoundingClientRect();
console.log(rect.top, rect.right, rect.bottom, rect.left);
From there, for the positions returned. You can follow this documentation on adding data to a collection in Firestore.
I managed to find the solution, as I use panresponder. I get the x and y values and add them to firestore.
This is part of the code that I use:
const pan = useState(new Animated.ValueXY({x: xval, y: yval}))[0];
const panResponder = useState(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
pan.setOffset({
x: pan.x._value,
y: pan.y._value
});
pan.setValue({x: xval, y: yval});
},
onPanResponderMove: Animated.event(
[
null,
{ dx: pan.x, dy: pan.y }
],
{useNativeDriver: false}
),
onPanResponderRelease: () => {
pan.flattenOffset();
stagesRef.doc(parent).collection('stageimages').doc(id)
.update({
x: pan.x._value,
y: pan.y._value,
}).then(function() {
});
}
})
)[0];
The stagesRef is the base for the firestore.
The xval and yval are values coming from anther file.
Trying to make swiper with React Native PanResponder. When I click again to button after release button starts at begining position. I tried using setOffset in onPanResponderGrant but that makes button exceeds parent container when you scroll it. If sser scrolled 45% of the containers half width I'm animating button to that halfs end.
Here is a link for my code
https://snack.expo.io/#sargnec/react-native-swiper
I think I made it work, with a few changes:
I kept
onPanResponderGrant: () => {
this.pan.setOffset({x: this.pan.x._value});
},handlePanResponderMove
This is important so that, when a user click a second time on your button and make gestureState.dx = 10, you do read 10px, not dx since initial position (after first click)
on handlePanResponderMove, I commented "// this.setState({ xPosition: gestureState.dx })" Your "xPosition" is useful to know where was your point in the beginning, so xPosition + dx move past the limit or not. If you update it on panResponderMove, if you make many very small dx steps, you will reach DRAG_TOP_LIMIT before the end
on onPanResponderRelease: (a) Here, I changed the xPosition, and (b) I do not make a test "gesture.dx > DRAG_TOP_LIMIT", but "xPosition + gesture.dx > DRAG_TOP_LIMIT"
(optional) Actually, your xPosition is not used (and not useful) for the render, so you should remove it from the state and just make this._xPosition
So, here is the code
pan = new Animated.ValueXY();
handlePanResponderMove = (e, gestureState) => {
const currentValue = this.state.xPosition + gestureState.dx
if (currentValue > DRAG_TOP_LIMIT) {
this.pan.setValue({ x: DRAG_TOP_LIMIT })
} else if (currentValue < DRAG_ALT_LIMIT) {
this.pan.setValue({ x: DRAG_ALT_LIMIT })
} else {
this.pan.setValue({ x: gestureState.dx })
}
};
panResponder = PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: () => {
this.pan.setOffset({x: this.pan.x._value});
},
onPanResponderMove: (e, gestureState) => this.handlePanResponderMove(e, gestureState),
onPanResponderRelease: (e, gestureState) => {
this.pan.flattenOffset();
const currPosition = gestureState.dx + this.state.xPosition
if (currPosition >= DRAG_TOP_LIMIT * 0.45) {
Animated.timing(this.pan.x, {
toValue: DRAG_TOP_LIMIT,
duration: 100
}).start()
this.setState({xPosition: DRAG_TOP_LIMIT})
} else if (currPosition <= DRAG_ALT_LIMIT * 0.45) {
Animated.timing(this.pan.x, {
toValue: DRAG_ALT_LIMIT,
duration: 100
}).start()
this.setState({xPosition: DRAG_ALT_LIMIT})
} else {
this.setState({xPosition: this.state.xPosition + gestureState.dx })
}
}
})
I have a fade animation in my componentDidUpdate function :
if (prevState.fadeStarting !== this.state.fadeStarting){
const fadestart = new Animated.Value(1);
this.setState(
{fadeStarting: false,animFade:fadestart},
() => {
Animated.timing(
this.state.animFade, {
toValue: 0.0,
duration: 100000000,
}
).start(this.endFade())
}
)
}
I assumed that the endFade method would only be called after the duration expired, but the endFade method is called instantly. Is there any reason that this animation would end early?
Could you mean start(this.endFade) instead?
You mean to pass the callback function, not its result (which is evaluated immediately).
I have the following code to animate in React Native
Animated.timing(
this.state.absoluteChangeX,
{toValue: 0},
).start(function() {
this.lastX = 0;
this.lastY = 0;
});
Pretty simple, but whenever it's triggered, I receive the error:
singleValue.stopTracking is not a function
Here's where the error originates:
/react-native/Libraries/Animates/src/AnimtaedImplementation.js
var timing = function(
value: AnimatedValue | AnimatedValueXY,
config: TimingAnimationConfig,
): CompositeAnimation {
return maybeVectorAnim(value, config, timing) || {
start: function(callback?: ?EndCallback): void {
var singleValue: any = value;
var singleConfig: any = config;
singleValue.stopTracking(); // <--------------- HERE!!!
if (config.toValue instanceof Animated) {
singleValue.track(new AnimatedTracking(
singleValue,
config.toValue,
TimingAnimation,
singleConfig,
callback
));
} else {
singleValue.animate(new TimingAnimation(singleConfig), callback);
}
},
stop: function(): void {
value.stopAnimation();
},
};
};
I'm not extremely versed in typeScript, but var singleValue: any means that "singleValue" could be any type. In my case, it's a number. Since numbers don't have methods, it would make sense that this would error.
Am I doing something wrong?
The value you wish to animate must be an instance of Animated.Value, or one of its subtypes. When you initialize your state, it should look something like this:
getInitialState() {
return { absoluteChangeX: new Animated.Value(0) };
}
The fact that the type declaration in the framework method is any is just a lack of constraint, not an explicit invitation to pass any value into it.
See the Animated docs for more examples.
I run into this issue sometimes (React hooks instead) when I forget to set my variable to the .current of the ref:
function MyComponent() {
const animatedValue = useRef(new Animated.Value(0.0)).current; // Notice the .current
}
This may not necessarily answer the original question, but developers who encounter this error while using React hooks may end up here so maybe it will help someone.
I ran into this issue because I used the animated value (2) instead of the object (1):
const animatedValue = useRef(new Animated.Value(0.0)).current; // (1)
const transform = animatedValue.interpolate({
inputRange: [0.0, 1.0],
outputRange: [0, 100]
}); // (2)
Animated.timing(animatedValue, { // USE animatedValue, NOT transform HERE!
toValue: 1.0,
duration: 3000,
});
Hope this can help anyone that was new to React Native Animation (like me :) )...