OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 /** | |
6 * Adds a listener to the scroller with triggers events | |
7 * when a trigger point at the top, or bottom, of the screen is reached. | |
8 * | |
9 * To use this you will need to have an element with a scroller attached | |
10 * to it. You need to have defined (in pixels) how far from the top or | |
11 * bottom the scroll position must be in order to trigger (the "trigger | |
12 * point") The element using this must have functions for hitting the top | |
13 * trigger, and the bottom trigger. In general, these methods will | |
14 * ascertain whether we have more data to scroll to (i.e. when we hit | |
15 * the bottom trigger point but have reached the end of the data | |
16 * displayed in the element we should ignore it), make the call for | |
17 * more data and reposition the scroller - repositioning is key to | |
18 * good user experience. | |
19 * | |
20 * Triggers are generated by listening for the SCROLL_END event from the | |
21 * scroller, so data calls are not initiated whilst scrolling is happening, | |
22 * but after. | |
23 * | |
24 * Controls changing divs between the usual (non-loading) div and the | |
25 * loading div. To take advantage of this, callback function should return | |
26 * a boolean indicating whether the usual div should be replaced by the | |
27 * loading div. | |
28 */ | |
29 class InfiniteScroller { | |
30 Scroller _scroller; | |
31 | |
32 /** | |
33 * Function to invoke when trigger point is reached at the top of the view. | |
34 */ | |
35 Function _onTopScroll; | |
36 | |
37 /** | |
38 * Function to invoke when trigger point is reached at the bottom of the view. | |
39 */ | |
40 Function _onBottomScroll; | |
41 | |
42 /** Offset for trigger point at the top of the view. */ | |
43 double _offsetTop; | |
44 | |
45 /** Offset for trigger point at the bottom of the view. */ | |
46 double _offsetBottom; | |
47 | |
48 /** Saves the last Y position. */ | |
49 double _lastScrollY; | |
50 Element _topDiv; | |
51 Element _topLoadingDiv; | |
52 Element _bottomDiv; | |
53 Element _bottomLoadingDiv; | |
54 | |
55 InfiniteScroller(Scroller scroller, | |
56 Function onTopScroll, Function onBottomScroll, | |
57 double offsetTop, [double offsetBottom = null]) | |
58 : _scroller = scroller, | |
59 _onTopScroll = onTopScroll, | |
60 _onBottomScroll = onBottomScroll, | |
61 _offsetTop = offsetTop, | |
62 _offsetBottom = offsetBottom == null ? offsetTop : offsetBottom, | |
63 _lastScrollY = 0.0 { | |
64 } | |
65 | |
66 /** | |
67 * Adds the loading divs. | |
68 * [topDiv] The div usually shown at the top. | |
69 * [topLoadingDiv] is the div to show at the top when waiting for more | |
70 * content to load at the top of the page. | |
71 * [bottomDiv] is the div usually shown at the bottom. | |
72 * [bottomLoadingDiv] is the div to show at the bottom when waiting for more | |
73 * content to load at the end of the page. | |
74 */ | |
75 void addLoadingDivs([Element topDiv = null, | |
76 Element topLoadingDiv = null, | |
77 Element bottomDiv = null, | |
78 Element bottomLoadingDiv = null]) { | |
79 _topDiv = topDiv; | |
80 _topLoadingDiv = topLoadingDiv; | |
81 _bottomDiv = bottomDiv; | |
82 _bottomLoadingDiv = bottomLoadingDiv; | |
83 _updateVisibility(false, _topDiv, _topLoadingDiv); | |
84 _updateVisibility(false, _bottomDiv, _bottomLoadingDiv); | |
85 } | |
86 | |
87 void initialize() { | |
88 _registerEventListeners(); | |
89 } | |
90 | |
91 /** | |
92 * Switch back the divs after loading complete. Delegate should call | |
93 * this function after loading is complete. | |
94 */ | |
95 void loadEnd() { | |
96 _updateVisibility(false, _topDiv, _topLoadingDiv); | |
97 _updateVisibility(false, _bottomDiv, _bottomLoadingDiv); | |
98 } | |
99 | |
100 /** | |
101 * Called at the end of a scroll event. | |
102 */ | |
103 void _onScrollEnd() { | |
104 double ypos = _scroller.getVerticalOffset(); | |
105 | |
106 // Scroll is below last point. | |
107 if (ypos < _lastScrollY) { | |
108 double bottomTrigger = _scroller.getMinPointY() + _offsetBottom; | |
109 // And below trigger point. | |
110 if (ypos <= bottomTrigger) { | |
111 _updateVisibility(_onBottomScroll(), _bottomDiv, _bottomLoadingDiv); | |
112 } | |
113 } else { | |
114 if (ypos > _lastScrollY) { | |
115 // Scroll is above last point. | |
116 double topTrigger = _scroller.getMaxPointY() - _offsetTop; | |
117 // And above trigger point. | |
118 if (ypos >= topTrigger) { | |
119 _updateVisibility(_onTopScroll(), _topDiv, _topLoadingDiv); | |
120 } | |
121 } | |
122 } | |
123 _lastScrollY = ypos; | |
124 } | |
125 | |
126 /** | |
127 * Register the event listeners. | |
128 */ | |
129 void _registerEventListeners() { | |
130 _scroller.onScrollerEnd.add((Event event) { _onScrollEnd(); }); | |
131 } | |
132 | |
133 /** | |
134 * Hides one div and shows another. | |
135 */ | |
136 void _updateVisibility(bool isLoading, Element element, | |
137 Element loadingElement) { | |
138 if (element != null) { | |
139 element.style.display = isLoading ? "none" : ""; | |
140 } | |
141 if (loadingElement != null) { | |
142 loadingElement.style.display = isLoading ? "" : "none"; | |
143 } | |
144 } | |
145 } | |
OLD | NEW |