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

Layout Animations rewrite #3332

Merged
merged 49 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0fa7649
Stashing work
kmagiera Apr 22, 2022
fd6bc69
Stashing moar work
kmagiera Apr 26, 2022
bcfa8e3
Stashing moar work
kmagiera Apr 26, 2022
ae65f1a
Fix entering animation flicker
kmagiera Apr 27, 2022
35d72a4
Even moar changes
kmagiera May 4, 2022
75e963a
Sort out ref dependency cycle between animations manager and layout p…
kmagiera Jun 28, 2022
e6ad2ad
Naming cleanup
kmagiera Jun 28, 2022
53fc972
Work on interrupting entering with layout animation
kmagiera Jul 4, 2022
65447aa
Fix overriding enter with layout
kmagiera Jul 5, 2022
d1d7116
Fix lint and some warnings
kmagiera Jul 5, 2022
754720a
Remove even more old, unused code
kmagiera Jul 6, 2022
cf59766
Stashing work on Android
kmagiera Jul 6, 2022
752add2
entering
jwajgelt Oct 17, 2022
46b0306
layout, exiting
jwajgelt Oct 17, 2022
e7287d1
Merge branch 'layoutanim_rewrite' into @jwajgelt/layout-animations
jwajgelt Oct 19, 2022
3c65659
use NativeHierarchyManager to resolve ViewManagers
jwajgelt Oct 21, 2022
7990c04
keep only exiting views in AnimationsManager
jwajgelt Oct 21, 2022
a68f0b7
Rewrite view deletion logic in AnimationsManager
jwajgelt Oct 24, 2022
f9e76f0
fix nested `exiting` animations
jwajgelt Oct 24, 2022
61631de
fix ios nested exiting animations, disable view interaction on android
jwajgelt Oct 28, 2022
f69a5ae
Merge branch 'main' into layoutanim_rewrite
kmagiera Oct 28, 2022
deac27b
fix exiting not running for entering views
jwajgelt Oct 28, 2022
a17b6f3
Android cleanup
jwajgelt Nov 2, 2022
cc5ac2c
fix linter errors
jwajgelt Nov 2, 2022
f04a6e6
Fix color handling
piaskowyk Nov 3, 2022
d6bb8da
code review changes
jwajgelt Nov 4, 2022
8314587
handle startLayoutAnimation being called before initializing LayoutAn…
jwajgelt Nov 4, 2022
6312882
code review changes
jwajgelt Nov 4, 2022
fcae36c
fix fabric ios
jwajgelt Nov 4, 2022
3c8ac7a
add layout animation examples
jwajgelt Nov 4, 2022
17beed4
add remaining types
jwajgelt Nov 4, 2022
690f37b
more code review changes
jwajgelt Nov 4, 2022
99c5edb
remove layout animation configs when not needed
jwajgelt Nov 7, 2022
7aba259
remove animation config when removing views on iOS
jwajgelt Nov 7, 2022
c042065
Merge branch 'main' into layoutanim_rewrite
piaskowyk Nov 8, 2022
d4f6010
Fix nativeProxy for Fabric
piaskowyk Nov 8, 2022
b81203e
add comment about magic constant
jwajgelt Nov 9, 2022
06f3e49
Add new examples
tomekzaw Nov 10, 2022
0d66c07
add code review changes
jwajgelt Nov 10, 2022
06713ef
fix ios reporting error on removing views
jwajgelt Nov 10, 2022
7a7a367
Update android/src/main/cpp/NativeProxy.cpp
jwajgelt Nov 10, 2022
03a1f1e
code review fixes
jwajgelt Nov 14, 2022
47cc15f
fix cleanup of layout animations
jwajgelt Nov 14, 2022
a84d5a7
fix typo
jwajgelt Nov 14, 2022
1ef44f1
fix imports
jwajgelt Nov 14, 2022
9744ae5
Merge branch 'main' into layoutanim_rewrite
jwajgelt Nov 14, 2022
dfc6da6
remove only roots of to-be-removed trees on Android
jwajgelt Nov 14, 2022
a5ae203
remove only roots of to-be-removed trees on iOS
jwajgelt Nov 14, 2022
be4fd9f
Fix race condition by @kmagiera
piaskowyk Nov 16, 2022
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
112 changes: 97 additions & 15 deletions Common/cpp/LayoutAnimations/LayoutAnimationsProxy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,35 +11,117 @@ namespace reanimated {
const long long idOffset = 1e9;

LayoutAnimationsProxy::LayoutAnimationsProxy(
std::function<void(int, jsi::Object newProps)> _notifyAboutProgress,
std::function<void(int, bool)> _notifyAboutEnd)
: notifyAboutProgress(std::move(_notifyAboutProgress)),
notifyAboutEnd(std::move(_notifyAboutEnd)) {}
std::function<void(int, jsi::Object newProps)> progressHandler,
std::function<void(int, bool, bool)> endHandler,
std::weak_ptr<ErrorHandler> weakErrorHandler)
: progressHandler_(std::move(progressHandler)),
endHandler_(std::move(endHandler)),
weakErrorHandler_(weakErrorHandler) {}

void LayoutAnimationsProxy::startObserving(
int tag,
std::shared_ptr<MutableValue> sv,
jsi::Runtime &rt) {
observedValues[tag] = sv;
observedValues_[tag] = sv;
this->progressHandler_(tag, sv->value->toJSValue(rt).asObject(rt));
sv->addListener(tag + idOffset, [sv, tag, this, &rt]() {
std::shared_ptr<FrozenObject> newValue =
ValueWrapper::asFrozenObject(sv->value->valueContainer);
this->notifyAboutProgress(tag, newValue->shallowClone(rt));
this->progressHandler_(tag, sv->value->toJSValue(rt).asObject(rt));
});
}

void LayoutAnimationsProxy::stopObserving(int tag, bool finished) {
if (observedValues.count(tag) == 0) {
void LayoutAnimationsProxy::stopObserving(
int tag,
bool finished,
jwajgelt marked this conversation as resolved.
Show resolved Hide resolved
bool removeView) {
if (observedValues_.count(tag) == 0) {
return;
}
std::shared_ptr<MutableValue> sv = observedValues[tag];
std::shared_ptr<MutableValue> sv = observedValues_[tag];
sv->removeListener(tag + idOffset);
observedValues.erase(tag);
this->notifyAboutEnd(tag, !finished);
observedValues_.erase(tag);
this->endHandler_(tag, !finished, removeView);
}

void LayoutAnimationsProxy::configureAnimation(
int tag,
const std::string &type,
std::shared_ptr<ShareableValue> config,
std::shared_ptr<ShareableValue> viewSharedValue) {
auto lock = std::unique_lock<std::mutex>(animationsMutex_);
if (type == "entering") {
enteringAnimations_[tag] = config;
} else if (type == "exiting") {
exitingAnimations_[tag] = config;
} else if (type == "layout") {
layoutAnimations_[tag] = config;
}
jwajgelt marked this conversation as resolved.
Show resolved Hide resolved
viewSharedValues_[tag] = viewSharedValue;
}

bool LayoutAnimationsProxy::hasLayoutAnimation(
int tag,
const std::string &type) {
auto lock = std::unique_lock<std::mutex>(animationsMutex_);
if (type == "entering") {
jwajgelt marked this conversation as resolved.
Show resolved Hide resolved
return enteringAnimations_.find(tag) != enteringAnimations_.end();
} else if (type == "exiting") {
return exitingAnimations_.find(tag) != exitingAnimations_.end();
} else if (type == "layout") {
return layoutAnimations_.find(tag) != layoutAnimations_.end();
}
return false;
}

void LayoutAnimationsProxy::clearLayoutAnimationConfig(int tag) {
auto lock = std::unique_lock<std::mutex>(animationsMutex_);
enteringAnimations_.erase(tag);
exitingAnimations_.erase(tag);
layoutAnimations_.erase(tag);
viewSharedValues_.erase(tag);
}

void LayoutAnimationsProxy::notifyAboutCancellation(int tag) {
this->notifyAboutEnd(tag, false);
void LayoutAnimationsProxy::startLayoutAnimation(
jsi::Runtime &rt,
int tag,
const std::string &type,
const jsi::Object &values) {
std::shared_ptr<ShareableValue> config;
std::shared_ptr<ShareableValue> viewSharedValue;
{
auto lock = std::unique_lock<std::mutex>(animationsMutex_);
if (type == "entering") {
config = enteringAnimations_[tag];
} else if (type == "exiting") {
config = exitingAnimations_[tag];
} else if (type == "layout") {
config = layoutAnimations_[tag];
}
viewSharedValue = viewSharedValues_[tag];
}

jsi::Value layoutAnimationRepositoryAsValue =
rt.global()
.getPropertyAsObject(rt, "global")
.getProperty(rt, "LayoutAnimationRepository");
if (layoutAnimationRepositoryAsValue.isUndefined()) {
auto errorHandler = weakErrorHandler_.lock();
if (errorHandler != nullptr) {
errorHandler->setError(
"startLayoutAnimation called before initializing LayoutAnimationRepository");
errorHandler->raise();
}
return;
}
jsi::Function startAnimationForTag =
layoutAnimationRepositoryAsValue.getObject(rt).getPropertyAsFunction(
rt, "startAnimationForTag");
startAnimationForTag.call(
rt,
jsi::Value(tag),
jsi::String::createFromAscii(rt, type),
jwajgelt marked this conversation as resolved.
Show resolved Hide resolved
values,
config->toJSValue(rt),
viewSharedValue->toJSValue(rt));
}

} // namespace reanimated
40 changes: 32 additions & 8 deletions Common/cpp/LayoutAnimations/LayoutAnimationsProxy.h
Original file line number Diff line number Diff line change
@@ -1,32 +1,56 @@
#pragma once

#include <ErrorHandler.h>
#include <jsi/jsi.h>
#include <stdio.h>
#include <functional>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>

namespace reanimated {

using namespace facebook;

class MutableValue;
class ShareableValue;
jwajgelt marked this conversation as resolved.
Show resolved Hide resolved

class LayoutAnimationsProxy {
public:
LayoutAnimationsProxy(
std::function<void(int, jsi::Object newProps)> _notifyAboutProgress,
std::function<void(int, bool)> _notifyAboutEnd);
std::function<void(int, jsi::Object newProps)> progressHandler,
std::function<void(int, bool, bool)> endHandler,
std::weak_ptr<ErrorHandler> weakErrorHandler);

void
startObserving(int tag, std::shared_ptr<MutableValue> sv, jsi::Runtime &rt);
void stopObserving(int tag, bool finished);
void notifyAboutCancellation(int tag);
void stopObserving(int tag, bool finished, bool removeView);
void configureAnimation(
int tag,
const std::string &type,
std::shared_ptr<ShareableValue> config,
std::shared_ptr<ShareableValue> viewSharedValue);
bool hasLayoutAnimation(int tag, const std::string &type);
void startLayoutAnimation(
jsi::Runtime &rt,
int tag,
const std::string &type,
const jsi::Object &values);
void clearLayoutAnimationConfig(int tag);

private:
std::function<void(int, jsi::Object newProps)> notifyAboutProgress;
std::function<void(int, bool)> notifyAboutEnd;
std::map<int, std::shared_ptr<MutableValue>> observedValues;
std::function<void(int, jsi::Object newProps)> progressHandler_;
std::function<void(int, bool, bool)> endHandler_;
std::weak_ptr<ErrorHandler> weakErrorHandler_;
std::unordered_map<int, std::shared_ptr<MutableValue>> observedValues_;
std::unordered_map<int, std::shared_ptr<ShareableValue>> viewSharedValues_;
std::unordered_map<int, std::shared_ptr<ShareableValue>> enteringAnimations_;
std::unordered_map<int, std::shared_ptr<ShareableValue>> exitingAnimations_;
std::unordered_map<int, std::shared_ptr<ShareableValue>> layoutAnimations_;
mutable std::mutex
animationsMutex_; // Protects `enteringAnimations_`, `exitingAnimations_`,
// `layoutAnimations_` and `viewSharedValues_`.
};

} // namespace reanimated
17 changes: 16 additions & 1 deletion Common/cpp/NativeModules/NativeReanimatedModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ NativeReanimatedModule::NativeReanimatedModule(
PlatformDepMethodsHolder platformDepMethodsHolder)
: NativeReanimatedModuleSpec(jsInvoker),
RuntimeManager(rt, errorHandler, scheduler, RuntimeType::UI),
layoutAnimationsProxy_(layoutAnimationsProxy),
mapperRegistry(std::make_shared<MapperRegistry>()),
eventHandlerRegistry(std::make_shared<EventHandlerRegistry>()),
requestRender(platformDepMethodsHolder.requestRender),
Expand All @@ -109,7 +110,7 @@ NativeReanimatedModule::NativeReanimatedModule(
maybeRequestRender();
};

this->layoutAnimationsProxy = layoutAnimationsProxy;
this->layoutAnimationsProxy_ = layoutAnimationsProxy;

#ifdef RCT_NEW_ARCH_ENABLED
auto updateProps = [this](
Expand Down Expand Up @@ -337,6 +338,20 @@ jsi::Value NativeReanimatedModule::configureProps(
return jsi::Value::undefined();
}

jsi::Value NativeReanimatedModule::configureLayoutAnimation(
jsi::Runtime &rt,
const jsi::Value &viewTag,
const jsi::Value &type,
const jsi::Value &config,
const jsi::Value &viewSharedValue) {
layoutAnimationsProxy_->configureAnimation(
viewTag.asNumber(),
type.asString(rt).utf8(rt),
ShareableValue::adapt(rt, config, this),
ShareableValue::adapt(rt, viewSharedValue, this));
return jsi::Value::undefined();
}

void NativeReanimatedModule::onEvent(
std::string eventName,
#ifdef RCT_NEW_ARCH_ENABLED
Expand Down
9 changes: 8 additions & 1 deletion Common/cpp/NativeModules/NativeReanimatedModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,19 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec,
jsi::Runtime &rt,
const jsi::Value &listenerId) override;

jsi::Value configureLayoutAnimation(
jsi::Runtime &rt,
const jsi::Value &viewTag,
const jsi::Value &type,
const jsi::Value &config,
const jsi::Value &viewSharedValue) override;

private:
#ifdef RCT_NEW_ARCH_ENABLED
bool isThereAnyLayoutProp(jsi::Runtime &rt, const jsi::Value &props);
#endif // RCT_NEW_ARCH_ENABLED

std::shared_ptr<LayoutAnimationsProxy> layoutAnimationsProxy_;
std::shared_ptr<MapperRegistry> mapperRegistry;
std::shared_ptr<EventHandlerRegistry> eventHandlerRegistry;
std::function<void(FrameCallback &, jsi::Runtime &)> requestRender;
Expand All @@ -158,7 +166,6 @@ class NativeReanimatedModule : public NativeReanimatedModuleSpec,
std::function<jsi::Value(jsi::Runtime &, const int, const jsi::String &)>
propObtainer;
std::function<void(double)> onRenderCallback;
std::shared_ptr<LayoutAnimationsProxy> layoutAnimationsProxy;
AnimatedSensorModule animatedSensorModule;
ConfigurePropsFunction configurePropsPlatformFunction;

Expand Down
17 changes: 17 additions & 0 deletions Common/cpp/NativeModules/NativeReanimatedModuleSpec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,20 @@ static jsi::Value SPEC_PREFIX(unsubscribeFromKeyboardEvents)(
return jsi::Value::undefined();
}

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

NativeReanimatedModuleSpec::NativeReanimatedModuleSpec(
std::shared_ptr<CallInvoker> jsInvoker)
: TurboModule("NativeReanimated", jsInvoker) {
Expand Down Expand Up @@ -188,5 +202,8 @@ NativeReanimatedModuleSpec::NativeReanimatedModuleSpec(
MethodMetadata{1, SPEC_PREFIX(subscribeForKeyboardEvents)};
methodMap_["unsubscribeFromKeyboardEvents"] =
MethodMetadata{1, SPEC_PREFIX(unsubscribeFromKeyboardEvents)};

methodMap_["configureLayoutAnimation"] =
MethodMetadata{4, SPEC_PREFIX(configureLayoutAnimation)};
}
} // namespace reanimated
8 changes: 8 additions & 0 deletions Common/cpp/NativeModules/NativeReanimatedModuleSpec.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,14 @@ class JSI_EXPORT NativeReanimatedModuleSpec : public TurboModule {
jsi::Runtime &rt,
const jsi::Value &uiProps,
const jsi::Value &nativeProps) = 0;

// layout animations
virtual jsi::Value configureLayoutAnimation(
jsi::Runtime &rt,
const jsi::Value &viewTag,
const jsi::Value &type,
const jsi::Value &config,
const jsi::Value &viewSharedValue) = 0;
};

} // namespace reanimated
3 changes: 2 additions & 1 deletion Common/cpp/Tools/RuntimeDecorator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,8 @@ void RuntimeDecorator::decorateUIRuntime(
if (layoutProxy.expired()) {
return jsi::Value::undefined();
}
proxy->stopObserving(args[0].asNumber(), args[1].getBool());
proxy->stopObserving(
args[0].asNumber(), args[1].getBool(), args[2].getBool());
return jsi::Value::undefined();
};
jsi::Value _stopObservingProgress = jsi::Function::createFromHostFunction(
Expand Down
25 changes: 25 additions & 0 deletions Example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,11 @@ import {
SpringLayoutAnimation,
SwipeableList,
NativeModals,
NestedTest,
CombinedTest,
BasicNestedLayoutAnimation,
BasicNestedAnimation,
BasicLayoutAnimation,
} from './LayoutReanimation';

import AnimatedStyleUpdateExample from './AnimatedStyleUpdateExample';
Expand Down Expand Up @@ -64,6 +69,26 @@ if (Platform.OS === 'android') {
type Screens = Record<string, { screen: React.ComponentType; title?: string }>;

const SCREENS: Screens = {
BasicLayoutAnimation: {
screen: BasicLayoutAnimation,
title: '🆕 Basic layout animation',
},
BasicNestedAnimation: {
screen: BasicNestedAnimation,
title: '🆕 Basic nested animation',
},
BasicNestedLayoutAnimation: {
screen: BasicNestedLayoutAnimation,
title: '🆕 Basic nested layout animation',
},
NestedLayoutAnimations: {
screen: NestedTest,
title: '🆕 Nested layout animations',
},
CombinedLayoutAnimations: {
screen: CombinedTest,
title: '🆕 Entering and Exiting with Layout',
},
DefaultAnimations: {
screen: DefaultAnimations,
title: '🆕 Default layout animations',
Expand Down
Loading