| // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // @dart = 2.9 |
| |
| part of touch; |
| |
| /** |
| * Adds a listener to the scroller with triggers events |
| * when a trigger point at the top, or bottom, of the screen is reached. |
| * |
| * To use this you will need to have an element with a scroller attached |
| * to it. You need to have defined (in pixels) how far from the top or |
| * bottom the scroll position must be in order to trigger (the "trigger |
| * point") The element using this must have functions for hitting the top |
| * trigger, and the bottom trigger. In general, these methods will |
| * ascertain whether we have more data to scroll to (i.e. when we hit |
| * the bottom trigger point but have reached the end of the data |
| * displayed in the element we should ignore it), make the call for |
| * more data and reposition the scroller - repositioning is key to |
| * good user experience. |
| * |
| * Triggers are generated by listening for the SCROLL_END event from the |
| * scroller, so data calls are not initiated whilst scrolling is happening, |
| * but after. |
| * |
| * Controls changing divs between the usual (non-loading) div and the |
| * loading div. To take advantage of this, callback function should return |
| * a boolean indicating whether the usual div should be replaced by the |
| * loading div. |
| */ |
| class InfiniteScroller { |
| Scroller _scroller; |
| |
| /** |
| * Function to invoke when trigger point is reached at the top of the view. |
| */ |
| Function _onTopScroll; |
| |
| /** |
| * Function to invoke when trigger point is reached at the bottom of the view. |
| */ |
| Function _onBottomScroll; |
| |
| /** Offset for trigger point at the top of the view. */ |
| double _offsetTop; |
| |
| /** Offset for trigger point at the bottom of the view. */ |
| double _offsetBottom; |
| |
| /** Saves the last Y position. */ |
| double _lastScrollY; |
| Element _topDiv; |
| Element _topLoadingDiv; |
| Element _bottomDiv; |
| Element _bottomLoadingDiv; |
| |
| InfiniteScroller(Scroller scroller, Function onTopScroll, |
| Function onBottomScroll, double offsetTop, |
| [double offsetBottom = null]) |
| : _scroller = scroller, |
| _onTopScroll = onTopScroll, |
| _onBottomScroll = onBottomScroll, |
| _offsetTop = offsetTop, |
| _offsetBottom = offsetBottom == null ? offsetTop : offsetBottom, |
| _lastScrollY = 0.0 {} |
| |
| /** |
| * Adds the loading divs. |
| * [topDiv] The div usually shown at the top. |
| * [topLoadingDiv] is the div to show at the top when waiting for more |
| * content to load at the top of the page. |
| * [bottomDiv] is the div usually shown at the bottom. |
| * [bottomLoadingDiv] is the div to show at the bottom when waiting for more |
| * content to load at the end of the page. |
| */ |
| void addLoadingDivs( |
| [Element topDiv = null, |
| Element topLoadingDiv = null, |
| Element bottomDiv = null, |
| Element bottomLoadingDiv = null]) { |
| _topDiv = topDiv; |
| _topLoadingDiv = topLoadingDiv; |
| _bottomDiv = bottomDiv; |
| _bottomLoadingDiv = bottomLoadingDiv; |
| _updateVisibility(false, _topDiv, _topLoadingDiv); |
| _updateVisibility(false, _bottomDiv, _bottomLoadingDiv); |
| } |
| |
| void initialize() { |
| _registerEventListeners(); |
| } |
| |
| /** |
| * Switch back the divs after loading complete. Delegate should call |
| * this function after loading is complete. |
| */ |
| void loadEnd() { |
| _updateVisibility(false, _topDiv, _topLoadingDiv); |
| _updateVisibility(false, _bottomDiv, _bottomLoadingDiv); |
| } |
| |
| /** |
| * Called at the end of a scroll event. |
| */ |
| void _onScrollEnd() { |
| double ypos = _scroller.getVerticalOffset(); |
| |
| // Scroll is below last point. |
| if (ypos < _lastScrollY) { |
| double bottomTrigger = _scroller.getMinPointY() + _offsetBottom; |
| // And below trigger point. |
| if (ypos <= bottomTrigger) { |
| _updateVisibility(_onBottomScroll(), _bottomDiv, _bottomLoadingDiv); |
| } |
| } else { |
| if (ypos > _lastScrollY) { |
| // Scroll is above last point. |
| double topTrigger = _scroller.getMaxPointY() - _offsetTop; |
| // And above trigger point. |
| if (ypos >= topTrigger) { |
| _updateVisibility(_onTopScroll(), _topDiv, _topLoadingDiv); |
| } |
| } |
| } |
| _lastScrollY = ypos; |
| } |
| |
| /** |
| * Register the event listeners. |
| */ |
| void _registerEventListeners() { |
| _scroller.onScrollerEnd.listen((Event event) { |
| _onScrollEnd(); |
| }); |
| } |
| |
| /** |
| * Hides one div and shows another. |
| */ |
| void _updateVisibility( |
| bool isLoading, Element element, Element loadingElement) { |
| if (element != null) { |
| element.style.display = isLoading ? "none" : ""; |
| } |
| if (loadingElement != null) { |
| loadingElement.style.display = isLoading ? "" : "none"; |
| } |
| } |
| } |