Is there any way to listen to an object position that is part of the lottie animation? I can change its position by applying a value callback to its keypath. What I need is to listen to its position without modifying it.
If not, will you handle it in future releases?
I tried to use LottieValueCallback to get the position:
lottieAnimationView.addValueCallback(
new KeyPath("Gift Outlines"),
LottieProperty.TRANSFORM_POSITION,
new LottieValueCallback<PointF>(){
@Override
public PointF getValue(LottieFrameInfo<PointF> frameInfo) {
return super.getValue(frameInfo);
}
}
);
The problem is that super.getValue(frameInfo) always returns null. I assumed super.getValue(frameInfo) return the position of the "Gift Outlines" at the current call and it will be called each time "Gift Outlines" moves . I need t to listen to "Gift Outlines" position.
@islamassi Can you attach a sample project that reproduces this?
LottieSample.zip
@gpeal
I attached a sample project contains the code and the json file. I tried to use different json files but still getting super.getValue(frameInfo) null.
After much much research I have reached to this code.
Essentially, you have to insert that frameInfo that you receive in the callback inside LottieRelativePointValueCallback which actually does the computation of the animated value, given the startValue, endValue and current animation time.
lottieAnimationView.addValueCallback(
resolvedKeyPath,
LottieProperty.TRANSFORM_POSITION,
new LottieValueCallback<PointF>() {
@Override
public PointF getValue(LottieFrameInfo<PointF> frameInfo) {
LottieRelativePointValueCallback valueCallback = new LottieRelativePointValueCallback(new PointF());
PointF value = valueCallback.getValue(frameInfo); // This is the value you want.
return value;
}
}
);
BTW, this architecture is flawed. A callback should return normal value in the animation by default.
Also, if we want to use this approach for ScaleXY, then we must do a hack:
lottieAnimationView.addValueCallback(
resolvedKeyPath,
LottieProperty.TRANSFORM_SCALE,
new LottieValueCallback<ScaleXY>() {
@Override
public ScaleXY getValue(LottieFrameInfo<ScaleXY> frameInfo) {
LottieRelativePointValueCallback valueCallback = new LottieRelativePointValueCallback(new PointF());
LottieFrameInfo<PointF> lottieFrameInfo = new LottieFrameInfo<>();
lottieFrameInfo.set(
frameInfo.getStartFrame(),
frameInfo.getEndFrame(),
new PointF(frameInfo.getStartValue().getScaleX(), frameInfo.getStartValue().getScaleY()),
new PointF(frameInfo.getEndValue().getScaleX(), frameInfo.getEndValue().getScaleY()),
frameInfo.getLinearKeyframeProgress(),
frameInfo.getInterpolatedKeyframeProgress(),
frameInfo.getOverallProgress()
);
PointF scalePoint = valueCallback.getValue(lottieFrameInfo);
ScaleXY scale = new ScaleXY(scalePoint.x, scalePoint.y); // This is the wanted value.
return scale;
}
}
);
The hack is necessary due to a lack of a class such as LottieRelativeScaleXYValueCallback.
Also, the hack is bad because LottieFrameInfo#set(...) is annotated with @RestrictTo(RestrictTo.Scope.LIBRARY).
Until any further changes of architecture, please remove these annotations, because the library needs hacks like these in order to be used normally.
Anyway, the above approach does not give us correct positions for nested layers. Inside the Java library, what are the positions received in the callback relative to?
Thanks for the investigation. I'll try to look in to this further for a future release.
I now allow returning null from the callback in #960.
Regarding the interpolation thing, I thought about it for a bit and it wouldn't be trivial to do because of type erasure in Java which makes it hard to know how to do the interpolation in a generic way. You don't need to do what you are doing. Just interpolate between the start and end values by frameInfo. getInterpolatedKeyframeProgress() for X and Y.
This doesn't resolve anything.
You don't understand the problem. Please read again what @islamassi wrote.
At the moment, the so called LottieValueCallback is more of a dynamic getter than a callback. Lottie uses this getter in order to change the animated values for each frame.
We need a callback with the animated value without changing the value returned by the getter. For this, we need to use the hacks I wrote above.
If we return null in the getter, we just lose the animation. Nothing will happen.
So, there no way of getting the object absolute position on screen?
You can get the correct absolute position on screen via the method above only for non parented layers (layers that are parented directly to the composition).
If you have layers with a parent, then you will have a rough time. The above method returns some values for these children layers, but I could not figure out what are they relative to. I tried taking into account the children layers' anchor points, the parent's anchor points and combinations of these 2, but no luck unfortunately.
Most helpful comment
This doesn't resolve anything.
You don't understand the problem. Please read again what @islamassi wrote.
At the moment, the so called
LottieValueCallbackis more of a dynamic getter than a callback. Lottie uses this getter in order to change the animated values for each frame.We need a callback with the animated value without changing the value returned by the getter. For this, we need to use the hacks I wrote above.
If we return null in the getter, we just lose the animation. Nothing will happen.