diff --git a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm index 97b47aa8600172..d029337fb0d40f 100644 --- a/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm +++ b/packages/react-native/React/Fabric/Mounting/ComponentViews/ScrollView/RCTScrollViewComponentView.mm @@ -547,6 +547,17 @@ - (BOOL)_shouldDisableScrollInteraction return metrics; } +- (ScrollViewEventEmitter::EndDragMetrics)_scrollViewMetricsWithVelocity:(CGPoint)velocity + andTargetContentOffset:(CGPoint)targetContentOffset +{ + ScrollViewEventEmitter::EndDragMetrics metrics = [self _scrollViewMetrics]; + metrics.targetContentOffset.x = targetContentOffset.x; + metrics.targetContentOffset.y = targetContentOffset.y; + metrics.velocity.x = velocity.x; + metrics.velocity.y = velocity.y; + return metrics; +} + - (void)_updateStateWithContentOffset { if (!_state) { @@ -602,6 +613,14 @@ - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView targetContentOffset->y = scrollView.contentOffset.y + travel * _endDraggingSensitivityMultiplier; } } + + if (!_eventEmitter) { + return; + } + + auto metrics = [self _scrollViewMetricsWithVelocity:velocity andTargetContentOffset:*targetContentOffset]; + + static_cast(*_eventEmitter).onScrollEndDrag(metrics); } - (BOOL)touchesShouldCancelInContentView:(__unused UIView *)view @@ -672,8 +691,6 @@ - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL return; } - static_cast(*_eventEmitter).onScrollEndDrag([self _scrollViewMetrics]); - [self _updateStateWithContentOffset]; if (!decelerate) { @@ -770,7 +787,9 @@ - (void)scrollViewDidEndZooming:(UIScrollView *)scrollView withView:(nullable UI return; } - static_cast(*_eventEmitter).onScrollEndDrag([self _scrollViewMetrics]); + auto metrics = [self _scrollViewMetricsWithVelocity:{} andTargetContentOffset:{}]; + static_cast(*_eventEmitter).onScrollEndDrag(metrics); + [self _updateStateWithContentOffset]; } diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.cpp index 08dadd11f95b74..3c0f8b94adde46 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.cpp @@ -76,6 +76,39 @@ EventPayloadType ScrollEvent::getType() const { return EventPayloadType::ScrollEvent; } +jsi::Value ScrollEndDragEvent::asJSIValue(jsi::Runtime& runtime) const { + auto payload = ScrollEvent::asJSIValue(runtime).asObject(runtime); + + { + auto targetContentOffsetObj = jsi::Object(runtime); + targetContentOffsetObj.setProperty(runtime, "x", targetContentOffset.x); + targetContentOffsetObj.setProperty(runtime, "y", targetContentOffset.y); + payload.setProperty(runtime, "targetContentOffset", targetContentOffsetObj); + } + + { + auto velocityObj = jsi::Object(runtime); + velocityObj.setProperty(runtime, "x", velocity.x); + velocityObj.setProperty(runtime, "y", velocity.y); + payload.setProperty(runtime, "velocity", velocityObj); + } + + return payload; +} + +folly::dynamic ScrollEndDragEvent::asDynamic() const { + auto metrics = ScrollEvent::asDynamic(); + + auto targetContentOffsetObj = folly::dynamic::object( + "x", targetContentOffset.x)("y", targetContentOffset.y); + metrics["targetContentOffset"] = std::move(targetContentOffsetObj); + + auto velocityObj = folly::dynamic::object("x", velocity.x)("y", velocity.y); + metrics["velocity"] = std::move(velocityObj); + + return metrics; +}; + #if RN_DEBUG_STRING_CONVERTIBLE std::string getDebugName(const ScrollEvent& /*scrollEvent*/) { diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.h b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.h index 8eac9e879b5448..cb55148cf5d888 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.h +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollEvent.h @@ -37,6 +37,23 @@ struct ScrollEvent : public EventPayload { EventPayloadType getType() const override; }; +struct ScrollEndDragEvent : public ScrollEvent { + Point targetContentOffset; + Point velocity; + + ScrollEndDragEvent() = default; + + ScrollEndDragEvent(const ScrollEvent& scrollEvent) + : ScrollEvent(scrollEvent), targetContentOffset({}), velocity({}) {} + + folly::dynamic asDynamic() const; + + /* + * EventPayload implementations + */ + jsi::Value asJSIValue(jsi::Runtime& runtime) const override; +}; + #if RN_DEBUG_STRING_CONVERTIBLE std::string getDebugName(const ScrollEvent& scrollEvent); diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewEventEmitter.cpp b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewEventEmitter.cpp index 80a5e9d223de01..6ec9917110db96 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewEventEmitter.cpp +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewEventEmitter.cpp @@ -26,7 +26,15 @@ void ScrollViewEventEmitter::onScrollBeginDrag( void ScrollViewEventEmitter::onScrollEndDrag( const ScrollEvent& scrollEvent) const { - dispatchScrollViewEvent("scrollEndDrag", scrollEvent); + const auto* endDragScrollEvent = + dynamic_cast(&scrollEvent); + if (endDragScrollEvent) { + dispatchEvent( + "scrollEndDrag", + std::make_shared(*endDragScrollEvent)); + } else { + dispatchScrollViewEvent("scrollEndDrag", scrollEvent); + } } void ScrollViewEventEmitter::onMomentumScrollBegin( diff --git a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewEventEmitter.h b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewEventEmitter.h index 7bd8a555ecc671..b84f5540d8f7af 100644 --- a/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewEventEmitter.h +++ b/packages/react-native/ReactCommon/react/renderer/components/scrollview/ScrollViewEventEmitter.h @@ -21,6 +21,7 @@ class ScrollViewEventEmitter : public ViewEventEmitter { using ViewEventEmitter::ViewEventEmitter; using Metrics = ScrollEvent; + using EndDragMetrics = ScrollEndDragEvent; void onScroll(const ScrollEvent& scrollEvent) const; void onScrollBeginDrag(const ScrollEvent& scrollEvent) const;