Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add config component for layout animations #5045

Merged
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3f61542
Add `LayoutConfig`
bartlomiejbloniarz Aug 28, 2023
8b83d6d
Change `LayoutConfig` implementation to use refs
bartlomiejbloniarz Aug 29, 2023
c22ae4e
Reorganize examples
bartlomiejbloniarz Aug 29, 2023
9e3dd8d
Merge branch 'main' into @bartlomiejbloniarz/add-entering-config
bartlomiejbloniarz Aug 29, 2023
d385494
Rename ehh
bartlomiejbloniarz Aug 31, 2023
381c8fe
Minor fixes
bartlomiejbloniarz Aug 31, 2023
fae6c31
Fix `Animated.ts` formatting
bartlomiejbloniarz Aug 31, 2023
6ad11b7
Remove undefined
bartlomiejbloniarz Aug 31, 2023
893d853
Add `LayoutAnimationConfig`
bartlomiejbloniarz Sep 5, 2023
bbca933
Add fabric mock
bartlomiejbloniarz Sep 5, 2023
0ba76ce
Merge branch 'main' into @bartlomiejbloniarz/add-layout-animation-config
bartlomiejbloniarz Sep 8, 2023
906f077
Merge branch 'main' into @bartlomiejbloniarz/add-layout-animation-config
bartlomiejbloniarz Sep 12, 2023
3f7242d
Cleanup
bartlomiejbloniarz Sep 12, 2023
0eed12a
Move `skipEntering`
bartlomiejbloniarz Sep 12, 2023
157f00c
Change `skipExiting` logic
bartlomiejbloniarz Sep 19, 2023
7b0a076
Improve example
bartlomiejbloniarz Sep 20, 2023
2f558d2
Account for nested `skipExiting`
bartlomiejbloniarz Sep 20, 2023
62def34
Minor fixes
bartlomiejbloniarz Sep 20, 2023
da3a690
Minor fix
bartlomiejbloniarz Sep 20, 2023
7ff3f18
Rename `current` to `shouldAnimate`
bartlomiejbloniarz Sep 25, 2023
e51f5f4
Merge branch 'main' into @bartlomiejbloniarz/add-layout-animation-config
bartlomiejbloniarz Sep 25, 2023
077ff8e
Rename 2
bartlomiejbloniarz Sep 25, 2023
af9b2e6
Fix rename :(
bartlomiejbloniarz Sep 26, 2023
ace7af4
Add comment to `LayoutAnimationConfig`
bartlomiejbloniarz Sep 26, 2023
29685a6
Minor fix
bartlomiejbloniarz Sep 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Common/cpp/LayoutAnimations/LayoutAnimationsManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ void LayoutAnimationsManager::configureAnimation(
}
}

void LayoutAnimationsManager::disableExiting(int tag) {
auto lock = std::unique_lock<std::mutex>(animationsMutex_);
tjzel marked this conversation as resolved.
Show resolved Hide resolved
disableExiting_.emplace(tag);
}

bool LayoutAnimationsManager::isDisabledExiting(int tag) {
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
auto lock = std::unique_lock<std::mutex>(animationsMutex_);
return collection::contains(disableExiting_, tag);
}

bool LayoutAnimationsManager::hasLayoutAnimation(
int tag,
LayoutAnimationType type) {
Expand Down Expand Up @@ -65,6 +75,7 @@ void LayoutAnimationsManager::clearLayoutAnimationConfig(int tag) {
}
viewTagToSharedTag_.erase(tag);
ignoreProgressAnimationForTag_.erase(tag);
disableExiting_.erase(tag);
}

void LayoutAnimationsManager::startLayoutAnimation(
Expand Down
3 changes: 3 additions & 0 deletions Common/cpp/LayoutAnimations/LayoutAnimationsManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ class LayoutAnimationsManager {
LayoutAnimationType type,
const std::string &sharedTransitionTag,
std::shared_ptr<Shareable> config);
void disableExiting(int tag);
bool isDisabledExiting(int tag);
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
bool hasLayoutAnimation(int tag, LayoutAnimationType type);
void startLayoutAnimation(
jsi::Runtime &rt,
Expand Down Expand Up @@ -61,6 +63,7 @@ class LayoutAnimationsManager {
std::unordered_map<int, std::string> viewsScreenSharedTagMap_;
#endif

std::unordered_set<int> disableExiting_;
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
std::unordered_map<int, std::shared_ptr<Shareable>> enteringAnimations_;
std::unordered_map<int, std::shared_ptr<Shareable>> exitingAnimations_;
std::unordered_map<int, std::shared_ptr<Shareable>> layoutAnimations_;
Expand Down
7 changes: 7 additions & 0 deletions Common/cpp/NativeModules/NativeReanimatedModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,13 @@ jsi::Value NativeReanimatedModule::configureLayoutAnimation(
return jsi::Value::undefined();
}

jsi::Value NativeReanimatedModule::disableExiting(
tjzel marked this conversation as resolved.
Show resolved Hide resolved
jsi::Runtime &rt,
const jsi::Value &viewTag) {
layoutAnimationsManager_.disableExiting(viewTag.asNumber());
return jsi::Value::undefined();
}

bool NativeReanimatedModule::isAnyHandlerWaitingForEvent(
const std::string &eventName,
const int emitterReactTag) {
Expand Down
2 changes: 2 additions & 0 deletions Common/cpp/NativeModules/NativeReanimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec {
const jsi::Value &type,
const jsi::Value &sharedTransitionTag,
const jsi::Value &config) override;
jsi::Value disableExiting(jsi::Runtime &rt, const jsi::Value &viewTag)
override;

void onRender(double timestampMs);

Expand Down
11 changes: 11 additions & 0 deletions Common/cpp/NativeModules/NativeReanimatedModuleSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,15 @@ static jsi::Value SPEC_PREFIX(configureLayoutAnimation)(
std::move(args[3]));
}

static jsi::Value SPEC_PREFIX(disableExiting)(
jsi::Runtime &rt,
TurboModule &turboModule,
const jsi::Value *args,
size_t) {
return static_cast<NativeReanimatedModuleSpec *>(&turboModule)
->disableExiting(rt, std::move(args[0]));
}

NativeReanimatedModuleSpec::NativeReanimatedModuleSpec(
std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("NativeReanimated", jsInvoker) {
Expand Down Expand Up @@ -213,5 +222,7 @@ NativeReanimatedModuleSpec::NativeReanimatedModuleSpec(

methodMap_["configureLayoutAnimation"] =
MethodMetadata{4, SPEC_PREFIX(configureLayoutAnimation)};
methodMap_["disableExitingAnimation"] =
MethodMetadata{1, SPEC_PREFIX(disableExiting)};
}
} // namespace reanimated
4 changes: 4 additions & 0 deletions Common/cpp/NativeModules/NativeReanimatedModuleSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ class JSI_EXPORT NativeReanimatedModuleSpec : public TurboModule {
const jsi::Value &type,
const jsi::Value &sharedTransitionTag,
const jsi::Value &config) = 0;

virtual jsi::Value disableExiting(
jsi::Runtime &rt,
const jsi::Value &viewTag) = 0;
};

} // namespace reanimated
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,11 @@ public int findPrecedingViewTagForTransition(int tag) {
return -1;
}

@Override
public boolean isDisabledExiting(int tag) {
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
return false;
}

@Override
public boolean hasAnimation(int tag, int type) {
return false;
Expand Down
11 changes: 11 additions & 0 deletions android/src/main/cpp/LayoutAnimations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ void LayoutAnimations::setHasAnimationBlock(
this->hasAnimationBlock_ = hasAnimationBlock;
}

void LayoutAnimations::setIsDisabledExitingBlock(
IsDisabledExitingBlock isDisabledExitingBlock) {
this->isDisabledExitingBlock_ = isDisabledExitingBlock;
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
}

#ifdef DEBUG
void LayoutAnimations::setCheckDuplicateSharedTag(
CheckDuplicateSharedTag checkDuplicateSharedTag) {
Expand All @@ -62,6 +67,10 @@ bool LayoutAnimations::hasAnimationForTag(int tag, int type) {
return hasAnimationBlock_(tag, type);
}

bool LayoutAnimations::isDisabledExiting(int tag) {
return isDisabledExitingBlock_(tag);
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
}

void LayoutAnimations::setClearAnimationConfigBlock(
ClearAnimationConfigBlock clearAnimationConfigBlock) {
this->clearAnimationConfigBlock_ = clearAnimationConfigBlock;
Expand Down Expand Up @@ -102,6 +111,8 @@ void LayoutAnimations::registerNatives() {
"startAnimationForTag", LayoutAnimations::startAnimationForTag),
makeNativeMethod(
"hasAnimationForTag", LayoutAnimations::hasAnimationForTag),
makeNativeMethod(
"isDisabledExiting", LayoutAnimations::isDisabledExiting),
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
makeNativeMethod(
"clearAnimationConfigForTag",
LayoutAnimations::clearAnimationConfigForTag),
Expand Down
4 changes: 4 additions & 0 deletions android/src/main/cpp/LayoutAnimations.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class LayoutAnimations : public jni::HybridClass<LayoutAnimations> {
using AnimationStartingBlock =
std::function<void(int, int, alias_ref<JMap<jstring, jstring>>)>;
using HasAnimationBlock = std::function<bool(int, int)>;
using IsDisabledExitingBlock = std::function<bool(int)>;
#ifdef DEBUG
using CheckDuplicateSharedTag = std::function<void(int, int)>;
#endif
Expand All @@ -34,10 +35,12 @@ class LayoutAnimations : public jni::HybridClass<LayoutAnimations> {
int type,
alias_ref<JMap<jstring, jstring>> values);
bool hasAnimationForTag(int tag, int type);
bool isDisabledExiting(int tag);
bool isLayoutAnimationEnabled();

void setAnimationStartingBlock(AnimationStartingBlock animationStartingBlock);
void setHasAnimationBlock(HasAnimationBlock hasAnimationBlock);
void setIsDisabledExitingBlock(IsDisabledExitingBlock isDisabledExitingBlock);
#ifdef DEBUG
void setCheckDuplicateSharedTag(
CheckDuplicateSharedTag checkDuplicateSharedTag);
Expand All @@ -64,6 +67,7 @@ class LayoutAnimations : public jni::HybridClass<LayoutAnimations> {
jni::global_ref<LayoutAnimations::javaobject> javaPart_;
AnimationStartingBlock animationStartingBlock_;
HasAnimationBlock hasAnimationBlock_;
IsDisabledExitingBlock isDisabledExitingBlock_;
ClearAnimationConfigBlock clearAnimationConfigBlock_;
CancelAnimationBlock cancelAnimationBlock_;
FindPrecedingViewTagForTransitionBlock
Expand Down
9 changes: 9 additions & 0 deletions android/src/main/cpp/NativeProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,15 @@ void NativeProxy::setupLayoutAnimations() {
return false;
});

layoutAnimations_->cthis()->setIsDisabledExitingBlock(
[weakNativeReanimatedModule](int tag) {
if (auto nativeReanimatedModule = weakNativeReanimatedModule.lock()) {
return nativeReanimatedModule->layoutAnimationsManager()
.isDisabledExiting(tag);
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
}
return false;
});

#ifdef DEBUG
layoutAnimations_->cthis()->setCheckDuplicateSharedTag(
[weakNativeReanimatedModule](int viewTag, int screenTag) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public void onViewRemoval(View view, ViewGroup parent, Runnable callback) {
Integer tag = view.getId();
mCallbacks.put(tag, callback);

if (!removeOrAnimateExitRecursive(view, true)) {
if (!removeOrAnimateExitRecursive(view, true, true)) {
removeView(view, parent);
}
}
Expand Down Expand Up @@ -489,6 +489,10 @@ public void updateLayout(
}
}

public boolean isDisabledExiting(int tag) {
return mNativeMethodsHolder.isDisabledExiting(tag);
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
}

public boolean hasAnimationForTag(int tag, int type) {
return mNativeMethodsHolder.hasAnimation(tag, type);
}
Expand All @@ -497,7 +501,8 @@ public boolean isLayoutAnimationEnabled() {
return mNativeMethodsHolder != null && mNativeMethodsHolder.isLayoutAnimationEnabled();
}

private boolean removeOrAnimateExitRecursive(View view, boolean shouldRemove) {
private boolean removeOrAnimateExitRecursive(
tjzel marked this conversation as resolved.
Show resolved Hide resolved
View view, boolean shouldRemove, boolean shouldAnimate) {
int tag = view.getId();
ViewManager viewManager = resolveViewManager(tag);

Expand All @@ -512,8 +517,11 @@ private boolean removeOrAnimateExitRecursive(View view, boolean shouldRemove) {
}
}

shouldAnimate &= !isDisabledExiting(tag);

boolean hasExitAnimation =
hasAnimationForTag(tag, LayoutAnimations.Types.EXITING) || mExitingViews.containsKey(tag);
(hasAnimationForTag(tag, LayoutAnimations.Types.EXITING) || mExitingViews.containsKey(tag))
&& shouldAnimate;
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
boolean hasAnimatedChildren = false;
shouldRemove = shouldRemove && !hasExitAnimation;

Expand All @@ -531,7 +539,7 @@ private boolean removeOrAnimateExitRecursive(View view, boolean shouldRemove) {
ViewGroup viewGroup = (ViewGroup) view;
for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
View child = viewGroup.getChildAt(i);
if (removeOrAnimateExitRecursive(child, shouldRemove)) {
if (removeOrAnimateExitRecursive(child, shouldRemove, shouldAnimate)) {
hasAnimatedChildren = true;
} else if (shouldRemove && child.getId() != -1) {
toBeRemoved.add(child);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public LayoutAnimations(ReactApplicationContext context) {

public native boolean hasAnimationForTag(int tag, int type);

public native boolean isDisabledExiting(int tag);
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved

public native void checkDuplicateSharedTag(int viewTag, int screenTag);

public native void clearAnimationConfigForTag(int tag);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
public interface NativeMethodsHolder {
void startAnimation(int tag, int type, HashMap<String, Object> values);

boolean isDisabledExiting(int tag);
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved

boolean hasAnimation(int tag, int type);

void clearAnimationConfig(int tag);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,15 @@ public void startAnimation(int tag, int type, HashMap<String, Object> values) {
}
}

@Override
public boolean isDisabledExiting(int tag) {
LayoutAnimations layoutAnimations = weakLayoutAnimations.get();
if (layoutAnimations != null) {
return layoutAnimations.isDisabledExiting(tag);
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
}
return false;
}

@Override
public boolean isLayoutAnimationEnabled() {
LayoutAnimations layoutAnimations = weakLayoutAnimations.get();
Expand Down
120 changes: 120 additions & 0 deletions app/src/examples/LayoutAnimations/FlatListSkipLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import React, { useState } from 'react';
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
import { Button, StyleSheet } from 'react-native';
import Animated, {
FadeInUp,
LayoutAnimationConfig,
PinwheelIn,
PinwheelOut,
SlideInRight,
SlideOutLeft,
} from 'react-native-reanimated';

const digits = [...Array(3).keys()];
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved

export default function FlatListSkipLayout() {
bartlomiejbloniarz marked this conversation as resolved.
Show resolved Hide resolved
return <List />;
}

function List() {
const [show, setShow] = useState(true);
const [data, setData] = useState(digits);

return (
<>
<Button onPress={() => setShow(!show)} title="toggle" />
<Button
title="add"
onPress={() =>
setData((data) => {
return [...data, data.length];
})
}
/>
<Button
title="remove"
onPress={() =>
setData((data) => {
data.pop();
return [...data];
})
}
/>
{show && (
<Animated.FlatList
skipLayoutAnimations
style={styles.container}
contentContainerStyle={[styles.contentContainer]}
decelerationRate="fast"
data={data}
renderItem={() => <Item />}
/>
)}
</>
);
}

function Item() {
return (
<Animated.View
entering={SlideInRight.duration(500)}
exiting={SlideOutLeft}
style={styles.card}>
<Animated.View
entering={FadeInUp.duration(1000).delay(500)}
style={styles.outerBox}>
<LayoutAnimationConfig skipEntering={false}>
<Animated.View
style={styles.box}
entering={PinwheelIn.duration(2000)}
exiting={PinwheelOut}
/>
</LayoutAnimationConfig>
</Animated.View>
<Animated.View
entering={FadeInUp.duration(1000).delay(500)}
style={styles.outerBox}>
<Animated.View
style={styles.box}
entering={PinwheelIn.duration(2000)}
exiting={PinwheelOut}
/>
</Animated.View>
</Animated.View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
margin: 10,
},
contentContainer: {
alignItems: 'center',
height: 1000,
},
card: {
width: 330,
height: 100,
backgroundColor: 'white',
borderRadius: 20,
borderColor: '#eee',
borderWidth: 1,
margin: 10,
alignItems: 'center',
flexDirection: 'row',
},
outerBox: {
width: 50,
height: 50,
backgroundColor: '#b58df1',
alignItems: 'center',
justifyContent: 'center',
marginLeft: 80,
borderRadius: 5,
},
box: {
width: 30,
height: 30,
backgroundColor: '#782aeb',
},
});
Loading