@@ -779,6 +779,20 @@ void ScrollViewComponentView::updateProps(
779
779
updateDecelerationRate (newViewProps.decelerationRate );
780
780
}
781
781
782
+ if (!oldProps || oldViewProps.scrollEventThrottle != newViewProps.scrollEventThrottle ) {
783
+ // Zero means "send value only once per significant logical event".
784
+ // Prop value is in milliseconds.
785
+ auto throttleInSeconds = newViewProps.scrollEventThrottle / 1000.0 ;
786
+ auto msPerFrame = 1.0 / 60.0 ;
787
+ if (throttleInSeconds < 0 ) {
788
+ m_scrollEventThrottle = INFINITY;
789
+ } else if (throttleInSeconds <= msPerFrame) {
790
+ m_scrollEventThrottle = 0 ;
791
+ } else {
792
+ m_scrollEventThrottle = throttleInSeconds;
793
+ }
794
+ }
795
+
782
796
if (oldViewProps.maximumZoomScale != newViewProps.maximumZoomScale ) {
783
797
m_scrollVisual.SetMaximumZoomScale (newViewProps.maximumZoomScale );
784
798
}
@@ -1000,6 +1014,8 @@ bool ScrollViewComponentView::scrollToEnd(bool animate) noexcept {
1000
1014
1001
1015
auto x = (m_contentSize.width - m_layoutMetrics.frame .size .width ) * m_layoutMetrics.pointScaleFactor ;
1002
1016
auto y = (m_contentSize.height - m_layoutMetrics.frame .size .height ) * m_layoutMetrics.pointScaleFactor ;
1017
+ // Ensure at least one scroll event will fire
1018
+ m_allowNextScrollNoMatterWhat = true ;
1003
1019
m_scrollVisual.TryUpdatePosition ({static_cast <float >(x), static_cast <float >(y), 0 .0f }, animate);
1004
1020
return true ;
1005
1021
}
@@ -1240,19 +1256,27 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
1240
1256
[this ](
1241
1257
winrt::IInspectable const & /* sender*/ ,
1242
1258
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
1243
- updateStateWithContentOffset ();
1244
- auto eventEmitter = GetEventEmitter ();
1245
- if (eventEmitter) {
1246
- facebook::react::ScrollViewEventEmitter::Metrics scrollMetrics;
1247
- scrollMetrics.containerSize .height = m_layoutMetrics.frame .size .height ;
1248
- scrollMetrics.containerSize .width = m_layoutMetrics.frame .size .width ;
1249
- scrollMetrics.contentOffset .x = args.Position ().x / m_layoutMetrics.pointScaleFactor ;
1250
- scrollMetrics.contentOffset .y = args.Position ().y / m_layoutMetrics.pointScaleFactor ;
1251
- scrollMetrics.zoomScale = 1.0 ;
1252
- scrollMetrics.contentSize .height = std::max (m_contentSize.height , m_layoutMetrics.frame .size .height );
1253
- scrollMetrics.contentSize .width = std::max (m_contentSize.width , m_layoutMetrics.frame .size .width );
1254
- std::static_pointer_cast<facebook::react::ScrollViewEventEmitter const >(eventEmitter)
1255
- ->onScroll (scrollMetrics);
1259
+ auto now = std::chrono::steady_clock::now ();
1260
+ auto elapsed = std::chrono::duration_cast<std::chrono::duration<double >>(now - m_lastScrollEventTime).count ();
1261
+
1262
+ if (m_allowNextScrollNoMatterWhat ||
1263
+ (m_scrollEventThrottle < std::max (std::chrono::duration<double >(0.017 ).count (), elapsed))) {
1264
+ updateStateWithContentOffset ();
1265
+ auto eventEmitter = GetEventEmitter ();
1266
+ if (eventEmitter) {
1267
+ facebook::react::ScrollViewEventEmitter::Metrics scrollMetrics;
1268
+ scrollMetrics.containerSize .height = m_layoutMetrics.frame .size .height ;
1269
+ scrollMetrics.containerSize .width = m_layoutMetrics.frame .size .width ;
1270
+ scrollMetrics.contentOffset .x = args.Position ().x / m_layoutMetrics.pointScaleFactor ;
1271
+ scrollMetrics.contentOffset .y = args.Position ().y / m_layoutMetrics.pointScaleFactor ;
1272
+ scrollMetrics.zoomScale = 1.0 ;
1273
+ scrollMetrics.contentSize .height = std::max (m_contentSize.height , m_layoutMetrics.frame .size .height );
1274
+ scrollMetrics.contentSize .width = std::max (m_contentSize.width , m_layoutMetrics.frame .size .width );
1275
+ std::static_pointer_cast<facebook::react::ScrollViewEventEmitter const >(eventEmitter)
1276
+ ->onScroll (scrollMetrics);
1277
+ m_lastScrollEventTime = now;
1278
+ m_allowNextScrollNoMatterWhat = false ;
1279
+ }
1256
1280
}
1257
1281
});
1258
1282
@@ -1261,6 +1285,7 @@ winrt::Microsoft::ReactNative::Composition::Experimental::IVisual ScrollViewComp
1261
1285
[this ](
1262
1286
winrt::IInspectable const & /* sender*/ ,
1263
1287
winrt::Microsoft::ReactNative::Composition::Experimental::IScrollPositionChangedArgs const &args) {
1288
+ m_allowNextScrollNoMatterWhat = true ; // Ensure next scroll event is recorded, regardless of throttle
1264
1289
updateStateWithContentOffset ();
1265
1290
auto eventEmitter = GetEventEmitter ();
1266
1291
if (eventEmitter) {
0 commit comments