Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "Implement scrollEventThrottle for ScrollView in Fabric",
"packageName": "react-native-windows",
"email": "54227869+anupriya13@users.noreply.github.com",
"dependentChangeType": "patch"
}
3 changes: 2 additions & 1 deletion packages/playground/Samples/scrollViewSnapSample.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,8 @@ export default class Bootstrap extends React.Component<{}, any> {
onScroll={() => {
console.log('onScroll');
}}
decelerationRate={0.95}>
decelerationRate={0.95}
scrollEventThrottle={50}>
{this.makeItems(20, [styles.itemWrapper])}
</ScrollView>
</View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,20 @@ void ScrollViewComponentView::updateProps(
updateDecelerationRate(newViewProps.decelerationRate);
}

if (!oldProps || oldViewProps.scrollEventThrottle != newViewProps.scrollEventThrottle) {
// Zero means "send value only once per significant logical event".
// Prop value is in milliseconds.
auto throttleInSeconds = newViewProps.scrollEventThrottle / 1000.0;
auto msPerFrame = 1.0 / 60.0;
if (throttleInSeconds < 0) {
m_scrollEventThrottle = INFINITY;
} else if (throttleInSeconds <= msPerFrame) {
m_scrollEventThrottle = 0;
} else {
m_scrollEventThrottle = throttleInSeconds;
}
}

if (oldViewProps.maximumZoomScale != newViewProps.maximumZoomScale) {
m_scrollVisual.SetMaximumZoomScale(newViewProps.maximumZoomScale);
}
Expand Down Expand Up @@ -1000,6 +1014,8 @@ bool ScrollViewComponentView::scrollToEnd(bool animate) noexcept {

auto x = (m_contentSize.width - m_layoutMetrics.frame.size.width) * m_layoutMetrics.pointScaleFactor;
auto y = (m_contentSize.height - m_layoutMetrics.frame.size.height) * m_layoutMetrics.pointScaleFactor;
// Ensure at least one scroll event will fire
m_allowNextScrollNoMatterWhat = true;
m_scrollVisual.TryUpdatePosition({static_cast<float>(x), static_cast<float>(y), 0.0f}, animate);
return true;
}
Expand Down Expand Up @@ -1240,19 +1256,27 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
[this](
winrt::IInspectable const & /*sender*/,
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
updateStateWithContentOffset();
auto eventEmitter = GetEventEmitter();
if (eventEmitter) {
facebook::react::ScrollViewEventEmitter::Metrics scrollMetrics;
scrollMetrics.containerSize.height = m_layoutMetrics.frame.size.height;
scrollMetrics.containerSize.width = m_layoutMetrics.frame.size.width;
scrollMetrics.contentOffset.x = args.Position().x / m_layoutMetrics.pointScaleFactor;
scrollMetrics.contentOffset.y = args.Position().y / m_layoutMetrics.pointScaleFactor;
scrollMetrics.zoomScale = 1.0;
scrollMetrics.contentSize.height = std::max(m_contentSize.height, m_layoutMetrics.frame.size.height);
scrollMetrics.contentSize.width = std::max(m_contentSize.width, m_layoutMetrics.frame.size.width);
std::static_pointer_cast<facebook::react::ScrollViewEventEmitter const>(eventEmitter)
->onScroll(scrollMetrics);
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::duration<double>>(now - m_lastScrollEventTime).count();

if (m_allowNextScrollNoMatterWhat ||
(m_scrollEventThrottle < std::max(std::chrono::duration<double>(0.017).count(), elapsed))) {
updateStateWithContentOffset();
auto eventEmitter = GetEventEmitter();
if (eventEmitter) {
facebook::react::ScrollViewEventEmitter::Metrics scrollMetrics;
scrollMetrics.containerSize.height = m_layoutMetrics.frame.size.height;
scrollMetrics.containerSize.width = m_layoutMetrics.frame.size.width;
scrollMetrics.contentOffset.x = args.Position().x / m_layoutMetrics.pointScaleFactor;
scrollMetrics.contentOffset.y = args.Position().y / m_layoutMetrics.pointScaleFactor;
scrollMetrics.zoomScale = 1.0;
scrollMetrics.contentSize.height = std::max(m_contentSize.height, m_layoutMetrics.frame.size.height);
scrollMetrics.contentSize.width = std::max(m_contentSize.width, m_layoutMetrics.frame.size.width);
std::static_pointer_cast<facebook::react::ScrollViewEventEmitter const>(eventEmitter)
->onScroll(scrollMetrics);
m_lastScrollEventTime = now;
m_allowNextScrollNoMatterWhat = false;
}
}
});

Expand All @@ -1261,6 +1285,7 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
[this](
winrt::IInspectable const & /*sender*/,
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
m_allowNextScrollNoMatterWhat = true; // Ensure next scroll event is recorded, regardless of throttle
updateStateWithContentOffset();
auto eventEmitter = GetEventEmitter();
if (eventEmitter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ struct ScrollInteractionTrackerOwner : public winrt::implements<
bool m_isHorizontal = false;
bool m_changeViewAfterLoaded = false;
bool m_dismissKeyboardOnDrag = false;
double m_scrollEventThrottle{0.0};
bool m_allowNextScrollNoMatterWhat{false};
std::chrono::steady_clock::time_point m_lastScrollEventTime{};
std::shared_ptr<facebook::react::ScrollViewShadowNode::ConcreteState const> m_state;
};

Expand Down